From b709ff84c333c5dd487a6443540d629469b8b4ab Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 8 Oct 2025 21:14:45 +1300 Subject: [PATCH 001/526] Bump version to 2025.11.0-dev --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index cad97e645a..034fa3fa37 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.10.0-dev +PROJECT_NUMBER = 2025.11.0-dev # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index 44dc5a6052..086b5b4ce3 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.10.0-dev" +__version__ = "2025.11.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 82bdb08884a65b58b823848452edb17bd2faa751 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 8 Oct 2025 08:24:26 -1000 Subject: [PATCH 002/526] [ci] Reduce component test group size to prevent runner disk exhaustion (#11121) --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb038cb8aa..3dcfd7c044 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -391,7 +391,7 @@ jobs: ./script/test_build_components -e compile -c ${{ matrix.file }} test-build-components-splitter: - name: Split components for testing into 20 groups maximum + name: Split components for testing into 14 components per group runs-on: ubuntu-24.04 needs: - common @@ -402,10 +402,10 @@ jobs: steps: - name: Check out code from GitHub uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Split components into 20 groups + - name: Split components into groups of 14 id: split run: | - components=$(echo '${{ needs.determine-jobs.outputs.changed-components }}' | jq -c '.[]' | shuf | jq -s -c '[_nwise(20) | join(" ")]') + components=$(echo '${{ needs.determine-jobs.outputs.changed-components }}' | jq -c '.[]' | shuf | jq -s -c '[_nwise(14) | join(" ")]') echo "components=$components" >> $GITHUB_OUTPUT test-build-components-split: From 6bb1e4c9c0bccdbc20c3f91878cfbfd71d6ef346 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 8 Oct 2025 11:35:52 -1000 Subject: [PATCH 003/526] [ci] Reduce component test group size to 10 to prevent runner disk exhaustion (#11122) --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3dcfd7c044..1d7043c888 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -391,7 +391,7 @@ jobs: ./script/test_build_components -e compile -c ${{ matrix.file }} test-build-components-splitter: - name: Split components for testing into 14 components per group + name: Split components for testing into 10 components per group runs-on: ubuntu-24.04 needs: - common @@ -402,10 +402,10 @@ jobs: steps: - name: Check out code from GitHub uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Split components into groups of 14 + - name: Split components into groups of 10 id: split run: | - components=$(echo '${{ needs.determine-jobs.outputs.changed-components }}' | jq -c '.[]' | shuf | jq -s -c '[_nwise(14) | join(" ")]') + components=$(echo '${{ needs.determine-jobs.outputs.changed-components }}' | jq -c '.[]' | shuf | jq -s -c '[_nwise(10) | join(" ")]') echo "components=$components" >> $GITHUB_OUTPUT test-build-components-split: From d006008539b9ccd2624647d2161d9728bfdfec8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Oct 2025 09:26:38 -0400 Subject: [PATCH 004/526] Bump esphome-dashboard from 20250904.0 to 20251009.0 (#11123) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7ff4a6eeb2..8cc2b4ed45 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ pyserial==3.5 platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 -esphome-dashboard==20250904.0 +esphome-dashboard==20251009.0 aioesphomeapi==41.13.0 zeroconf==0.148.0 puremagic==1.30 From 84ad7ee0e423f34c2e7c548fa77c390c72823fbb Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 9 Oct 2025 10:10:48 -0700 Subject: [PATCH 005/526] [esp32] Accept more framework URL schemes as sources (#11125) --- esphome/components/esp32/__init__.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 860f2450e6..3cbcdb99b4 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -304,6 +304,17 @@ def _format_framework_espidf_version(ver: cv.Version, release: str) -> str: return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.zip" +def _is_framework_url(source: str) -> str: + # platformio accepts many URL schemes for framework repositories and archives including http, https, git, file, and symlink + import urllib.parse + + try: + parsed = urllib.parse.urlparse(source) + except ValueError: + return False + return bool(parsed.scheme) + + # NOTE: Keep this in mind when updating the recommended version: # * New framework historically have had some regressions, especially for WiFi. # The new version needs to be thoroughly validated before changing the @@ -386,7 +397,7 @@ def _check_versions(value): value[CONF_SOURCE] = value.get( CONF_SOURCE, _format_framework_arduino_version(version) ) - if value[CONF_SOURCE].startswith("http"): + if _is_framework_url(value[CONF_SOURCE]): value[CONF_SOURCE] = ( f"pioarduino/framework-arduinoespressif32@{value[CONF_SOURCE]}" ) @@ -399,7 +410,7 @@ def _check_versions(value): CONF_SOURCE, _format_framework_espidf_version(version, value.get(CONF_RELEASE, None)), ) - if value[CONF_SOURCE].startswith("http"): + if _is_framework_url(value[CONF_SOURCE]): value[CONF_SOURCE] = f"pioarduino/framework-espidf@{value[CONF_SOURCE]}" if CONF_PLATFORM_VERSION not in value: From a4b7e0c7009bdae610cb0868146ae5994c5166ab Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 07:41:49 -1000 Subject: [PATCH 006/526] [canbus][mcp23xxx_base] Mark virtual methods as pure virtual to fix linker errors (#11133) --- esphome/components/canbus/canbus.h | 6 +++--- esphome/components/mcp23xxx_base/mcp23xxx_base.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/canbus/canbus.h b/esphome/components/canbus/canbus.h index 56e2f2719b..51d7c0830a 100644 --- a/esphome/components/canbus/canbus.h +++ b/esphome/components/canbus/canbus.h @@ -105,9 +105,9 @@ class Canbus : public Component { CallbackManager &data)> callback_manager_{}; - virtual bool setup_internal(); - virtual Error send_message(struct CanFrame *frame); - virtual Error read_message(struct CanFrame *frame); + virtual bool setup_internal() = 0; + virtual Error send_message(struct CanFrame *frame) = 0; + virtual Error read_message(struct CanFrame *frame) = 0; }; template class CanbusSendAction : public Action, public Parented { diff --git a/esphome/components/mcp23xxx_base/mcp23xxx_base.h b/esphome/components/mcp23xxx_base/mcp23xxx_base.h index ab7f8ec398..cf0ef5d41c 100644 --- a/esphome/components/mcp23xxx_base/mcp23xxx_base.h +++ b/esphome/components/mcp23xxx_base/mcp23xxx_base.h @@ -21,11 +21,11 @@ template class MCP23XXXBase : public Component, public gpio_expander: protected: // read a given register - virtual bool read_reg(uint8_t reg, uint8_t *value); + virtual bool read_reg(uint8_t reg, uint8_t *value) = 0; // write a value to a given register - virtual bool write_reg(uint8_t reg, uint8_t value); + virtual bool write_reg(uint8_t reg, uint8_t value) = 0; // update registers with given pin value. - virtual void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a); + virtual void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a) = 0; bool open_drain_ints_; }; From 56334b7832ae28375d834001d259bace9afbae62 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 08:26:41 -1000 Subject: [PATCH 007/526] [ci][tests] Remove redundant ESP32 Arduino test files (#11136) --- tests/components/a01nyub/test.esp32-ard.yaml | 5 -- .../components/a01nyub/test.esp32-c3-ard.yaml | 5 -- tests/components/a02yyuw/test.esp32-ard.yaml | 5 -- .../components/a02yyuw/test.esp32-c3-ard.yaml | 5 -- tests/components/a4988/test.esp32-ard.yaml | 6 -- tests/components/a4988/test.esp32-c3-ard.yaml | 6 -- .../absolute_humidity/test.esp32-ard.yaml | 1 - .../absolute_humidity/test.esp32-c3-ard.yaml | 1 - tests/components/adc/test.esp32-ard.yaml | 6 -- tests/components/adc/test.esp32-c3-ard.yaml | 6 -- tests/components/adc/test.esp32-s2-ard.yaml | 6 -- tests/components/adc/test.esp32-s3-ard.yaml | 6 -- .../components/adc128s102/test.esp32-ard.yaml | 7 --- .../adc128s102/test.esp32-c3-ard.yaml | 7 --- .../esp32_rmt_led_strip.esp32-ard.yaml | 4 -- .../esp32_rmt_led_strip.esp32-c3-ard.yaml | 4 -- .../fastled_clockless.esp32-ard.yaml | 4 -- tests/components/ade7880/test.esp32-ard.yaml | 8 --- .../components/ade7880/test.esp32-c3-ard.yaml | 8 --- .../ade7953_i2c/test.esp32-ard.yaml | 6 -- .../ade7953_i2c/test.esp32-c3-ard.yaml | 6 -- .../ade7953_spi/test.esp32-ard.yaml | 8 --- .../ade7953_spi/test.esp32-c3-ard.yaml | 8 --- tests/components/ads1115/test.esp32-ard.yaml | 5 -- .../components/ads1115/test.esp32-c3-ard.yaml | 5 -- tests/components/ags10/test.esp32-ard.yaml | 5 -- tests/components/ags10/test.esp32-c3-ard.yaml | 5 -- tests/components/aht10/test.esp32-ard.yaml | 5 -- tests/components/aht10/test.esp32-c3-ard.yaml | 5 -- tests/components/aic3204/test.esp32-ard.yaml | 5 -- .../components/aic3204/test.esp32-c3-ard.yaml | 5 -- .../airthings_wave_mini/test.esp32-ard.yaml | 1 - .../test.esp32-c3-ard.yaml | 1 - .../airthings_wave_plus/test.esp32-ard.yaml | 1 - .../test.esp32-c3-ard.yaml | 1 - .../alarm_control_panel/test.esp32-ard.yaml | 1 - .../test.esp32-c3-ard.yaml | 1 - tests/components/alpha3/test.esp32-ard.yaml | 1 - .../components/alpha3/test.esp32-c3-ard.yaml | 1 - tests/components/am2315c/test.esp32-ard.yaml | 5 -- .../components/am2315c/test.esp32-c3-ard.yaml | 5 -- tests/components/am2320/test.esp32-ard.yaml | 5 -- .../components/am2320/test.esp32-c3-ard.yaml | 5 -- tests/components/am43/test.esp32-ard.yaml | 1 - tests/components/am43/test.esp32-c3-ard.yaml | 1 - .../analog_threshold/test.esp32-ard.yaml | 1 - .../analog_threshold/test.esp32-c3-ard.yaml | 1 - .../components/animation/test.esp32-ard.yaml | 17 ------ .../animation/test.esp32-c3-ard.yaml | 17 ------ tests/components/anova/test.esp32-ard.yaml | 1 - tests/components/anova/test.esp32-c3-ard.yaml | 1 - tests/components/apds9306/test.esp32-ard.yaml | 5 -- .../apds9306/test.esp32-c3-ard.yaml | 5 -- tests/components/apds9960/test.esp32-ard.yaml | 5 -- .../apds9960/test.esp32-c3-ard.yaml | 5 -- tests/components/api/test.esp32-ard.yaml | 5 -- tests/components/api/test.esp32-c3-ard.yaml | 5 -- .../components/as3935_i2c/test.esp32-ard.yaml | 11 ---- .../as3935_i2c/test.esp32-c3-ard.yaml | 6 -- .../components/as3935_spi/test.esp32-ard.yaml | 8 --- .../as3935_spi/test.esp32-c3-ard.yaml | 8 --- tests/components/as5600/test.esp32-ard.yaml | 6 -- .../components/as5600/test.esp32-c3-ard.yaml | 6 -- tests/components/as7341/test.esp32-ard.yaml | 5 -- .../components/as7341/test.esp32-c3-ard.yaml | 5 -- tests/components/at581x/test.esp32-ard.yaml | 5 -- .../components/at581x/test.esp32-c3-ard.yaml | 5 -- .../atc_mithermometer/test.esp32-ard.yaml | 1 - .../atc_mithermometer/test.esp32-c3-ard.yaml | 1 - tests/components/atm90e26/test.esp32-ard.yaml | 7 --- .../atm90e26/test.esp32-c3-ard.yaml | 7 --- tests/components/atm90e32/test.esp32-ard.yaml | 7 --- .../atm90e32/test.esp32-c3-ard.yaml | 7 --- tests/components/axs15231/test.esp32-ard.yaml | 1 - .../axs15231/test.esp32-c3-ard.yaml | 1 - .../components/b_parasite/test.esp32-ard.yaml | 1 - .../b_parasite/test.esp32-c3-ard.yaml | 1 - tests/components/ballu/test.esp32-ard.yaml | 4 -- .../components/bang_bang/test.esp32-ard.yaml | 1 - .../bang_bang/test.esp32-c3-ard.yaml | 1 - tests/components/bedjet/test.esp32-ard.yaml | 1 - .../components/bedjet/test.esp32-c3-ard.yaml | 1 - tests/components/bh1750/test.esp32-ard.yaml | 5 -- .../components/bh1750/test.esp32-c3-ard.yaml | 5 -- .../binary_sensor/test.esp32-ard.yaml | 2 - .../binary_sensor/test.esp32-c3-ard.yaml | 2 - .../binary_sensor_map/test.esp32-ard.yaml | 1 - .../binary_sensor_map/test.esp32-c3-ard.yaml | 1 - tests/components/bl0906/test.esp32-ard.yaml | 5 -- .../components/bl0906/test.esp32-c3-ard.yaml | 5 -- tests/components/bl0939/test.esp32-ard.yaml | 5 -- .../components/bl0939/test.esp32-c3-ard.yaml | 5 -- tests/components/bl0940/test.esp32-ard.yaml | 5 -- .../components/bl0940/test.esp32-c3-ard.yaml | 5 -- tests/components/bl0942/test.esp32-ard.yaml | 5 -- .../components/bl0942/test.esp32-c3-ard.yaml | 5 -- .../components/ble_client/test.esp32-ard.yaml | 1 - .../ble_client/test.esp32-c3-ard.yaml | 1 - .../ble_presence/test.esp32-ard.yaml | 1 - .../ble_presence/test.esp32-c3-ard.yaml | 1 - tests/components/ble_rssi/test.esp32-ard.yaml | 1 - .../ble_rssi/test.esp32-c3-ard.yaml | 1 - .../ble_scanner/test.esp32-ard.yaml | 1 - .../ble_scanner/test.esp32-c3-ard.yaml | 1 - .../bluetooth_proxy/test.esp32-s3-ard.yaml | 8 --- .../components/bme280_i2c/test.esp32-ard.yaml | 5 -- .../bme280_i2c/test.esp32-c3-ard.yaml | 5 -- .../components/bme280_spi/test.esp32-ard.yaml | 7 --- .../bme280_spi/test.esp32-c3-ard.yaml | 7 --- tests/components/bme680/test.esp32-ard.yaml | 5 -- .../components/bme680/test.esp32-c3-ard.yaml | 5 -- .../bme68x_bsec2_i2c/test.esp32-ard.yaml | 5 -- .../bme68x_bsec2_i2c/test.esp32-c3-ard.yaml | 5 -- .../bme68x_bsec2_i2c/test.esp32-s2-ard.yaml | 5 -- .../bme68x_bsec2_i2c/test.esp32-s3-ard.yaml | 5 -- tests/components/bmi160/test.esp32-ard.yaml | 5 -- .../components/bmi160/test.esp32-c3-ard.yaml | 5 -- tests/components/bmp085/test.esp32-ard.yaml | 5 -- .../components/bmp085/test.esp32-c3-ard.yaml | 5 -- .../components/bmp280_i2c/test.esp32-ard.yaml | 5 -- .../bmp280_i2c/test.esp32-c3-ard.yaml | 5 -- .../components/bmp280_spi/test.esp32-ard.yaml | 7 --- .../bmp280_spi/test.esp32-c3-ard.yaml | 7 --- .../components/bmp3xx_i2c/test.esp32-ard.yaml | 5 -- .../bmp3xx_i2c/test.esp32-c3-ard.yaml | 5 -- .../components/bmp3xx_spi/test.esp32-ard.yaml | 7 --- .../bmp3xx_spi/test.esp32-c3-ard.yaml | 7 --- tests/components/bmp581/test.esp32-ard.yaml | 5 -- .../components/bmp581/test.esp32-c3-ard.yaml | 5 -- tests/components/bp1658cj/test.esp32-ard.yaml | 5 -- .../bp1658cj/test.esp32-c3-ard.yaml | 5 -- tests/components/bp5758d/test.esp32-ard.yaml | 5 -- .../components/bp5758d/test.esp32-c3-ard.yaml | 5 -- tests/components/button/test.esp32-ard.yaml | 1 - .../components/button/test.esp32-c3-ard.yaml | 1 - .../components/bytebuffer/test.esp32-ard.yaml | 1 - .../bytebuffer/test.esp32-c3-ard.yaml | 1 - tests/components/camera/test.esp32-ard.yaml | 1 - .../camera_encoder/test.esp32-ard.yaml | 1 - tests/components/canbus/test.esp32-ard.yaml | 1 - .../components/canbus/test.esp32-c3-ard.yaml | 1 - tests/components/cap1188/test.esp32-ard.yaml | 6 -- .../components/cap1188/test.esp32-c3-ard.yaml | 6 -- tests/components/ccs811/test.esp32-ard.yaml | 5 -- .../components/ccs811/test.esp32-c3-ard.yaml | 5 -- .../components/cd74hc4067/test.esp32-ard.yaml | 8 --- .../cd74hc4067/test.esp32-c3-ard.yaml | 8 --- tests/components/ch422g/test.esp32-ard.yaml | 6 -- .../components/ch422g/test.esp32-c3-ard.yaml | 6 -- tests/components/chsc6x/test.esp32-ard.yaml | 25 -------- .../components/chsc6x/test.esp32-c3-ard.yaml | 25 -------- .../climate_ir_lg/test.esp32-ard.yaml | 4 -- .../climate_ir_lg/test.esp32-c3-ard.yaml | 4 -- tests/components/cm1106/test.esp32-ard.yaml | 5 -- .../components/cm1106/test.esp32-c3-ard.yaml | 5 -- tests/components/color/test.esp32-ard.yaml | 1 - tests/components/color/test.esp32-c3-ard.yaml | 1 - .../color_temperature/test.esp32-ard.yaml | 6 -- .../color_temperature/test.esp32-c3-ard.yaml | 6 -- .../combination/test.esp32-ard.yaml | 1 - .../combination/test.esp32-c3-ard.yaml | 1 - tests/components/coolix/test.esp32-ard.yaml | 4 -- .../components/coolix/test.esp32-c3-ard.yaml | 4 -- tests/components/copy/test.esp32-ard.yaml | 5 -- tests/components/copy/test.esp32-c3-ard.yaml | 5 -- tests/components/cs5460a/test.esp32-ard.yaml | 7 --- .../components/cs5460a/test.esp32-c3-ard.yaml | 7 --- tests/components/cse7761/test.esp32-ard.yaml | 5 -- .../components/cse7761/test.esp32-c3-ard.yaml | 5 -- tests/components/cse7766/test.esp32-ard.yaml | 5 -- .../components/cse7766/test.esp32-c3-ard.yaml | 5 -- tests/components/cst226/test.esp32-ard.yaml | 12 ---- .../components/cst226/test.esp32-c3-ard.yaml | 12 ---- tests/components/cst816/test.esp32-ard.yaml | 12 ---- .../components/cst816/test.esp32-c3-ard.yaml | 12 ---- tests/components/ct_clamp/test.esp32-ard.yaml | 4 -- .../ct_clamp/test.esp32-c3-ard.yaml | 4 -- .../current_based/test.esp32-ard.yaml | 6 -- .../current_based/test.esp32-c3-ard.yaml | 6 -- tests/components/cwww/test.esp32-ard.yaml | 6 -- tests/components/cwww/test.esp32-c3-ard.yaml | 6 -- tests/components/dac7678/test.esp32-ard.yaml | 5 -- .../components/dac7678/test.esp32-c3-ard.yaml | 5 -- tests/components/daikin/test.esp32-ard.yaml | 4 -- .../components/daikin_arc/test.esp32-ard.yaml | 5 -- .../components/daikin_brc/test.esp32-ard.yaml | 4 -- .../daikin_brc/test.esp32-c3-ard.yaml | 4 -- .../dallas_temp/test.esp32-ard.yaml | 1 - .../dallas_temp/test.esp32-c3-ard.yaml | 1 - tests/components/daly_bms/test.esp32-ard.yaml | 5 -- .../daly_bms/test.esp32-c3-ard.yaml | 5 -- .../components/deep_sleep/test.esp32-ard.yaml | 5 -- .../deep_sleep/test.esp32-c3-ard.yaml | 5 -- tests/components/delonghi/test.esp32-ard.yaml | 4 -- .../delonghi/test.esp32-c3-ard.yaml | 4 -- tests/components/dfplayer/test.esp32-ard.yaml | 5 -- .../dfplayer/test.esp32-c3-ard.yaml | 5 -- .../dfrobot_sen0395/test.esp32-ard.yaml | 5 -- .../dfrobot_sen0395/test.esp32-c3-ard.yaml | 5 -- tests/components/dht/test.esp32-ard.yaml | 1 - tests/components/dht/test.esp32-c3-ard.yaml | 1 - tests/components/dht12/test.esp32-ard.yaml | 5 -- tests/components/dht12/test.esp32-c3-ard.yaml | 5 -- tests/components/display/test.esp32-ard.yaml | 1 - tests/components/dps310/test.esp32-ard.yaml | 5 -- .../components/dps310/test.esp32-c3-ard.yaml | 5 -- tests/components/ds1307/test.esp32-ard.yaml | 5 -- .../components/ds1307/test.esp32-c3-ard.yaml | 5 -- tests/components/ds2484/test.esp32-ard.yaml | 5 -- .../components/ds2484/test.esp32-c3-ard.yaml | 5 -- .../components/duty_cycle/test.esp32-ard.yaml | 1 - .../duty_cycle/test.esp32-c3-ard.yaml | 1 - .../components/duty_time/test.esp32-ard.yaml | 1 - .../duty_time/test.esp32-c3-ard.yaml | 1 - tests/components/e131/test.esp32-ard.yaml | 5 -- tests/components/e131/test.esp32-c3-ard.yaml | 5 -- tests/components/ee895/test.esp32-ard.yaml | 5 -- tests/components/ee895/test.esp32-c3-ard.yaml | 5 -- tests/components/ektf2232/test.esp32-ard.yaml | 8 --- .../ektf2232/test.esp32-c3-ard.yaml | 8 --- tests/components/emc2101/test.esp32-ard.yaml | 5 -- .../components/emc2101/test.esp32-c3-ard.yaml | 5 -- tests/components/emmeti/test.esp32-ard.yaml | 5 -- tests/components/endstop/test.esp32-ard.yaml | 1 - .../components/endstop/test.esp32-c3-ard.yaml | 1 - .../components/ens160_i2c/test.esp32-ard.yaml | 5 -- .../ens160_i2c/test.esp32-c3-ard.yaml | 5 -- .../components/ens160_spi/test.esp32-ard.yaml | 7 --- .../ens160_spi/test.esp32-c3-ard.yaml | 7 --- tests/components/ens210/test.esp32-ard.yaml | 5 -- .../components/ens210/test.esp32-c3-ard.yaml | 5 -- tests/components/es7210/test.esp32-ard.yaml | 5 -- .../components/es7210/test.esp32-c3-ard.yaml | 5 -- tests/components/es7243e/test.esp32-ard.yaml | 5 -- .../components/es7243e/test.esp32-c3-ard.yaml | 5 -- tests/components/es8156/test.esp32-ard.yaml | 5 -- .../components/es8156/test.esp32-c3-ard.yaml | 5 -- tests/components/es8311/test.esp32-ard.yaml | 5 -- .../components/es8311/test.esp32-c3-ard.yaml | 5 -- tests/components/es8388/test.esp32-ard.yaml | 5 -- .../components/es8388/test.esp32-c3-ard.yaml | 5 -- .../esp32_ble_client/test.esp32-ard.yaml | 1 - .../esp32_ble_client/test.esp32-c3-ard.yaml | 1 - .../esp32_ble_server/test.esp32-ard.yaml | 1 - .../esp32_ble_server/test.esp32-c3-ard.yaml | 1 - .../esp32_camera/test.esp32-ard.yaml | 1 - .../test.esp32-ard.yaml | 1 - .../components/esp32_can/test.esp32-ard.yaml | 5 -- .../esp32_can/test.esp32-c3-ard.yaml | 5 -- .../components/esp32_dac/test.esp32-ard.yaml | 1 - .../esp32_improv/test.esp32-ard.yaml | 1 - .../esp32_improv/test.esp32-c3-ard.yaml | 1 - .../esp32_rmt_led_strip/test.esp32-ard.yaml | 6 -- .../test.esp32-c3-ard.yaml | 6 -- .../esp32_touch/test.esp32-ard.yaml | 4 -- .../esp32_touch/test.esp32-s2-ard.yaml | 4 -- .../esp32_touch/test.esp32-s3-ard.yaml | 4 -- tests/components/esphome/test.esp32-ard.yaml | 1 - .../components/esphome/test.esp32-c3-ard.yaml | 1 - .../ethernet_info/test.esp32-ard.yaml | 1 - tests/components/event/test.esp32-ard.yaml | 1 - tests/components/event/test.esp32-c3-ard.yaml | 1 - .../test.esp32-ard.yaml | 1 - .../test.esp32-c3-ard.yaml | 1 - .../external_components/test.esp32-ard.yaml | 1 - .../test.esp32-c3-ard.yaml | 1 - tests/components/ezo/test.esp32-ard.yaml | 5 -- tests/components/ezo/test.esp32-c3-ard.yaml | 5 -- tests/components/ezo_pmp/test.esp32-ard.yaml | 5 -- .../components/ezo_pmp/test.esp32-c3-ard.yaml | 5 -- .../factory_reset/test.esp32-ard.yaml | 1 - .../factory_reset/test.esp32-c3-ard.yaml | 1 - tests/components/feedback/test.esp32-ard.yaml | 1 - .../feedback/test.esp32-c3-ard.yaml | 1 - .../fingerprint_grow/test.esp32-ard.yaml | 6 -- .../fingerprint_grow/test.esp32-c3-ard.yaml | 6 -- tests/components/font/test.esp32-ard.yaml | 7 --- tests/components/font/test.esp32-c3-ard.yaml | 7 --- tests/components/fs3000/test.esp32-ard.yaml | 5 -- .../components/fs3000/test.esp32-c3-ard.yaml | 5 -- tests/components/ft5x06/test.esp32-ard.yaml | 6 -- .../components/ft5x06/test.esp32-c3-ard.yaml | 6 -- tests/components/ft63x6/test.esp32-ard.yaml | 9 --- .../components/ft63x6/test.esp32-c3-ard.yaml | 9 --- .../fujitsu_general/test.esp32-ard.yaml | 4 -- .../fujitsu_general/test.esp32-c3-ard.yaml | 4 -- tests/components/gcja5/test.esp32-ard.yaml | 5 -- tests/components/gcja5/test.esp32-c3-ard.yaml | 5 -- tests/components/gdk101/test.esp32-ard.yaml | 5 -- .../components/gl_r01_i2c/test.esp32-ard.yaml | 5 -- .../gl_r01_i2c/test.esp32-c3-ard.yaml | 5 -- tests/components/globals/test.esp32-ard.yaml | 1 - .../components/globals/test.esp32-c3-ard.yaml | 1 - .../gp2y1010au0f/test.esp32-ard.yaml | 5 -- .../gp2y1010au0f/test.esp32-c3-ard.yaml | 5 -- tests/components/gp8403/test.esp32-ard.yaml | 5 -- .../components/gp8403/test.esp32-c3-ard.yaml | 5 -- tests/components/gpio/test.esp32-ard.yaml | 6 -- tests/components/gpio/test.esp32-c3-ard.yaml | 6 -- tests/components/gps/test.esp32-ard.yaml | 5 -- tests/components/gps/test.esp32-c3-ard.yaml | 5 -- tests/components/graph/test.esp32-ard.yaml | 6 -- tests/components/graph/test.esp32-c3-ard.yaml | 6 -- .../test.esp32-ard.yaml | 6 -- .../test.esp32-c3-ard.yaml | 6 -- tests/components/gree/test.esp32-ard.yaml | 4 -- tests/components/gree/test.esp32-c3-ard.yaml | 4 -- .../grove_gas_mc_v2/test.esp32-ard.yaml | 5 -- .../grove_gas_mc_v2/test.esp32-c3-ard.yaml | 5 -- .../grove_tb6612fng/test.esp32-ard.yaml | 5 -- .../grove_tb6612fng/test.esp32-c3-ard.yaml | 5 -- .../growatt_solar/test.esp32-ard.yaml | 6 -- .../growatt_solar/test.esp32-c3-ard.yaml | 6 -- tests/components/gt911/test.esp32-ard.yaml | 1 - tests/components/gt911/test.esp32-c3-ard.yaml | 1 - tests/components/haier/test.esp32-ard.yaml | 5 -- tests/components/haier/test.esp32-c3-ard.yaml | 5 -- .../havells_solar/test.esp32-ard.yaml | 6 -- .../havells_solar/test.esp32-c3-ard.yaml | 6 -- tests/components/hbridge/test.esp32-ard.yaml | 17 ------ .../components/hbridge/test.esp32-c3-ard.yaml | 16 ----- tests/components/hdc1080/test.esp32-ard.yaml | 5 -- .../components/hdc1080/test.esp32-c3-ard.yaml | 5 -- tests/components/he60r/test.esp32-ard.yaml | 5 -- tests/components/he60r/test.esp32-c3-ard.yaml | 5 -- .../hitachi_ac344/test.esp32-ard.yaml | 4 -- .../hitachi_ac344/test.esp32-c3-ard.yaml | 4 -- .../hitachi_ac424/test.esp32-ard.yaml | 4 -- .../hitachi_ac424/test.esp32-c3-ard.yaml | 4 -- tests/components/hlw8012/test.esp32-ard.yaml | 6 -- .../components/hlw8012/test.esp32-c3-ard.yaml | 6 -- tests/components/hm3301/test.esp32-ard.yaml | 5 -- .../components/hm3301/test.esp32-c3-ard.yaml | 5 -- tests/components/hmc5883l/test.esp32-ard.yaml | 5 -- .../hmc5883l/test.esp32-c3-ard.yaml | 5 -- .../homeassistant/test.esp32-ard.yaml | 2 - .../homeassistant/test.esp32-c3-ard.yaml | 2 - .../honeywell_hih_i2c/test.esp32-ard.yaml | 5 -- .../honeywell_hih_i2c/test.esp32-c3-ard.yaml | 5 -- .../honeywellabp/test.esp32-ard.yaml | 7 --- .../honeywellabp/test.esp32-c3-ard.yaml | 7 --- .../honeywellabp2_i2c/test.esp32-ard.yaml | 5 -- .../honeywellabp2_i2c/test.esp32-c3-ard.yaml | 5 -- .../hrxl_maxsonar_wr/test.esp32-ard.yaml | 5 -- .../hrxl_maxsonar_wr/test.esp32-c3-ard.yaml | 5 -- tests/components/hte501/test.esp32-ard.yaml | 5 -- .../components/hte501/test.esp32-c3-ard.yaml | 5 -- tests/components/htu21d/test.esp32-ard.yaml | 5 -- .../components/htu21d/test.esp32-c3-ard.yaml | 5 -- tests/components/htu31d/test.esp32-ard.yaml | 5 -- .../components/htu31d/test.esp32-c3-ard.yaml | 5 -- tests/components/hx711/test.esp32-ard.yaml | 5 -- tests/components/hx711/test.esp32-c3-ard.yaml | 5 -- .../hydreon_rgxx/test.esp32-ard.yaml | 5 -- .../hydreon_rgxx/test.esp32-c3-ard.yaml | 5 -- tests/components/hyt271/test.esp32-ard.yaml | 5 -- .../components/hyt271/test.esp32-c3-ard.yaml | 5 -- .../components/i2c_device/test.esp32-ard.yaml | 5 -- .../i2c_device/test.esp32-c3-ard.yaml | 5 -- .../components/i2s_audio/test.esp32-ard.yaml | 6 -- .../i2s_audio/test.esp32-c3-ard.yaml | 6 -- tests/components/iaqcore/test.esp32-ard.yaml | 5 -- .../components/iaqcore/test.esp32-c3-ard.yaml | 5 -- tests/components/ili9xxx/test.esp32-ard.yaml | 11 ---- .../components/ili9xxx/test.esp32-c3-ard.yaml | 12 ---- tests/components/ina219/test.esp32-ard.yaml | 5 -- .../components/ina219/test.esp32-c3-ard.yaml | 5 -- tests/components/ina226/test.esp32-ard.yaml | 5 -- .../components/ina226/test.esp32-c3-ard.yaml | 5 -- tests/components/ina260/test.esp32-ard.yaml | 5 -- .../components/ina260/test.esp32-c3-ard.yaml | 5 -- .../components/ina2xx_i2c/test.esp32-ard.yaml | 5 -- .../ina2xx_i2c/test.esp32-c3-ard.yaml | 5 -- .../components/ina2xx_spi/test.esp32-ard.yaml | 7 --- .../ina2xx_spi/test.esp32-c3-ard.yaml | 7 --- tests/components/ina3221/test.esp32-ard.yaml | 5 -- .../components/ina3221/test.esp32-c3-ard.yaml | 5 -- .../inkbird_ibsth1_mini/test.esp32-ard.yaml | 1 - .../test.esp32-c3-ard.yaml | 1 - tests/components/inkplate/test.esp32-ard.yaml | 1 - .../integration/test.esp32-ard.yaml | 4 -- .../integration/test.esp32-c3-ard.yaml | 4 -- .../internal_temperature/test.esp32-ard.yaml | 1 - .../test.esp32-c3-ard.yaml | 1 - .../test.esp32-s2-ard.yaml | 1 - .../test.esp32-s3-ard.yaml | 1 - tests/components/interval/test.esp32-ard.yaml | 1 - .../interval/test.esp32-c3-ard.yaml | 1 - .../components/jsn_sr04t/test.esp32-ard.yaml | 5 -- .../jsn_sr04t/test.esp32-c3-ard.yaml | 5 -- .../kamstrup_kmp/test.esp32-ard.yaml | 5 -- .../key_collector/test.esp32-ard.yaml | 7 --- .../key_collector/test.esp32-c3-ard.yaml | 7 --- .../components/kmeteriso/test.esp32-ard.yaml | 5 -- .../kmeteriso/test.esp32-c3-ard.yaml | 5 -- tests/components/kuntze/test.esp32-ard.yaml | 6 -- .../components/kuntze/test.esp32-c3-ard.yaml | 6 -- .../components/lc709203f/test.esp32-ard.yaml | 5 -- .../lc709203f/test.esp32-c3-ard.yaml | 5 -- tests/components/lcd_gpio/test.esp32-ard.yaml | 9 --- .../lcd_gpio/test.esp32-c3-ard.yaml | 9 --- tests/components/lcd_menu/test.esp32-ard.yaml | 9 --- .../lcd_menu/test.esp32-c3-ard.yaml | 9 --- .../lcd_pcf8574/test.esp32-ard.yaml | 5 -- .../lcd_pcf8574/test.esp32-c3-ard.yaml | 5 -- tests/components/ld2410/test.esp32-ard.yaml | 5 -- .../components/ld2410/test.esp32-c3-ard.yaml | 5 -- tests/components/ld2412/test.esp32-ard.yaml | 5 -- .../components/ld2412/test.esp32-c3-ard.yaml | 5 -- tests/components/ld2420/test.esp32-ard.yaml | 5 -- .../components/ld2420/test.esp32-c3-ard.yaml | 5 -- tests/components/ld2450/test.esp32-ard.yaml | 5 -- .../components/ld2450/test.esp32-c3-ard.yaml | 5 -- tests/components/ledc/test.esp32-ard.yaml | 1 - tests/components/ledc/test.esp32-c3-ard.yaml | 1 - tests/components/light/test.esp32-ard.yaml | 21 ------- tests/components/light/test.esp32-c3-ard.yaml | 21 ------- .../lilygo_t5_47/test.esp32-ard.yaml | 7 --- .../lilygo_t5_47/test.esp32-c3-ard.yaml | 7 --- tests/components/lm75b/test.esp32-ard.yaml | 5 -- tests/components/lm75b/test.esp32-c3-ard.yaml | 5 -- tests/components/lock/test.esp32-ard.yaml | 1 - tests/components/lock/test.esp32-c3-ard.yaml | 1 - tests/components/lps22/test.esp32-ard.yaml | 6 -- tests/components/lps22/test.esp32-c3-ard.yaml | 6 -- tests/components/ltr390/test.esp32-ard.yaml | 5 -- .../components/ltr390/test.esp32-c3-ard.yaml | 5 -- tests/components/ltr501/test.esp32-ard.yaml | 6 -- .../components/ltr501/test.esp32-c3-ard.yaml | 6 -- .../components/ltr_als_ps/test.esp32-ard.yaml | 6 -- .../ltr_als_ps/test.esp32-c3-ard.yaml | 6 -- tests/components/lvgl/test.esp32-ard.yaml | 58 ------------------- .../m5stack_8angle/test.esp32-ard.yaml | 1 - .../m5stack_8angle/test.esp32-c3-ard.yaml | 1 - tests/components/mapping/test.esp32-ard.yaml | 17 ------ .../components/mapping/test.esp32-c3-ard.yaml | 17 ------ .../matrix_keypad/test.esp32-ard.yaml | 15 ----- .../matrix_keypad/test.esp32-c3-ard.yaml | 15 ----- tests/components/max17043/test.esp32-ard.yaml | 5 -- .../max17043/test.esp32-c3-ard.yaml | 5 -- tests/components/max31855/test.esp32-ard.yaml | 7 --- .../max31855/test.esp32-c3-ard.yaml | 7 --- tests/components/max31856/test.esp32-ard.yaml | 7 --- .../max31856/test.esp32-c3-ard.yaml | 7 --- tests/components/max31865/test.esp32-ard.yaml | 7 --- .../max31865/test.esp32-c3-ard.yaml | 7 --- tests/components/max44009/test.esp32-ard.yaml | 5 -- .../max44009/test.esp32-c3-ard.yaml | 5 -- tests/components/max6675/test.esp32-ard.yaml | 7 --- .../components/max6675/test.esp32-c3-ard.yaml | 7 --- tests/components/max6956/test.esp32-ard.yaml | 5 -- .../components/max6956/test.esp32-c3-ard.yaml | 5 -- tests/components/max7219/test.esp32-ard.yaml | 7 --- .../components/max7219/test.esp32-c3-ard.yaml | 7 --- .../max7219digit/test.esp32-ard.yaml | 7 --- .../max7219digit/test.esp32-c3-ard.yaml | 7 --- tests/components/max9611/test.esp32-ard.yaml | 5 -- .../components/max9611/test.esp32-c3-ard.yaml | 5 -- tests/components/mcp23008/test.esp32-ard.yaml | 5 -- .../mcp23008/test.esp32-c3-ard.yaml | 5 -- tests/components/mcp23016/test.esp32-ard.yaml | 5 -- .../mcp23016/test.esp32-c3-ard.yaml | 5 -- tests/components/mcp23017/test.esp32-ard.yaml | 5 -- .../mcp23017/test.esp32-c3-ard.yaml | 5 -- tests/components/mcp23s08/test.esp32-ard.yaml | 7 --- .../mcp23s08/test.esp32-c3-ard.yaml | 7 --- tests/components/mcp23s17/test.esp32-ard.yaml | 7 --- .../mcp23s17/test.esp32-c3-ard.yaml | 7 --- tests/components/mcp2515/test.esp32-ard.yaml | 7 --- .../components/mcp2515/test.esp32-c3-ard.yaml | 7 --- tests/components/mcp3008/test.esp32-ard.yaml | 7 --- .../components/mcp3008/test.esp32-c3-ard.yaml | 7 --- tests/components/mcp3204/test.esp32-ard.yaml | 7 --- .../components/mcp3204/test.esp32-c3-ard.yaml | 7 --- tests/components/mcp4461/test.esp32-ard.yaml | 5 -- .../components/mcp4461/test.esp32-c3-ard.yaml | 5 -- tests/components/mcp4725/test.esp32-ard.yaml | 5 -- .../components/mcp4725/test.esp32-c3-ard.yaml | 5 -- tests/components/mcp4728/test.esp32-ard.yaml | 5 -- .../components/mcp4728/test.esp32-c3-ard.yaml | 5 -- tests/components/mcp47a1/test.esp32-ard.yaml | 5 -- .../components/mcp47a1/test.esp32-c3-ard.yaml | 5 -- tests/components/mcp9600/test.esp32-ard.yaml | 5 -- .../components/mcp9600/test.esp32-c3-ard.yaml | 5 -- tests/components/mcp9808/test.esp32-ard.yaml | 5 -- .../components/mcp9808/test.esp32-c3-ard.yaml | 5 -- .../media_player/test.esp32-ard.yaml | 1 - tests/components/mhz19/test.esp32-ard.yaml | 5 -- tests/components/mhz19/test.esp32-c3-ard.yaml | 5 -- .../components/micronova/test.esp32-ard.yaml | 6 -- .../micronova/test.esp32-c3-ard.yaml | 6 -- .../components/microphone/test.esp32-ard.yaml | 13 ----- .../microphone/test.esp32-c3-ard.yaml | 7 --- .../components/mics_4514/test.esp32-ard.yaml | 5 -- .../mics_4514/test.esp32-c3-ard.yaml | 5 -- tests/components/midea_ir/test.esp32-ard.yaml | 1 - .../midea_ir/test.esp32-c3-ard.yaml | 1 - tests/components/mipi_spi/test.esp32-ard.yaml | 15 ----- .../mipi_spi/test.esp32-c3-ard.yaml | 10 ---- .../components/mitsubishi/test.esp32-ard.yaml | 1 - .../mitsubishi/test.esp32-c3-ard.yaml | 1 - tests/components/mixer/test.esp32-ard.yaml | 7 --- tests/components/mixer/test.esp32-c3-ard.yaml | 7 --- tests/components/mixer/test.esp32-s3-ard.yaml | 7 --- tests/components/mlx90393/test.esp32-ard.yaml | 5 -- .../mlx90393/test.esp32-c3-ard.yaml | 5 -- .../mlx90393/test.esp32-s3-ard.yaml | 5 -- tests/components/mlx90614/test.esp32-ard.yaml | 5 -- .../mlx90614/test.esp32-c3-ard.yaml | 5 -- tests/components/mmc5603/test.esp32-ard.yaml | 5 -- .../components/mmc5603/test.esp32-c3-ard.yaml | 5 -- tests/components/mmc5983/test.esp32-ard.yaml | 5 -- .../components/mmc5983/test.esp32-c3-ard.yaml | 5 -- tests/components/modbus/test.esp32-ard.yaml | 6 -- .../components/modbus/test.esp32-c3-ard.yaml | 6 -- .../modbus_controller/test.esp32-ard.yaml | 8 --- .../modbus_controller/test.esp32-c3-ard.yaml | 8 --- .../monochromatic/test.esp32-ard.yaml | 5 -- .../monochromatic/test.esp32-c3-ard.yaml | 5 -- .../components/mopeka_ble/test.esp32-ard.yaml | 1 - .../mopeka_ble/test.esp32-c3-ard.yaml | 1 - .../mopeka_pro_check/test.esp32-ard.yaml | 1 - .../mopeka_pro_check/test.esp32-c3-ard.yaml | 1 - .../mopeka_std_check/test.esp32-ard.yaml | 1 - .../mopeka_std_check/test.esp32-c3-ard.yaml | 1 - .../components/mpl3115a2/test.esp32-ard.yaml | 5 -- .../mpl3115a2/test.esp32-c3-ard.yaml | 5 -- tests/components/mpr121/test.esp32-ard.yaml | 5 -- .../components/mpr121/test.esp32-c3-ard.yaml | 5 -- tests/components/mpu6050/test.esp32-ard.yaml | 5 -- .../components/mpu6050/test.esp32-c3-ard.yaml | 5 -- tests/components/mpu6886/test.esp32-ard.yaml | 5 -- .../components/mpu6886/test.esp32-c3-ard.yaml | 5 -- tests/components/mqtt/test.esp32-ard.yaml | 6 -- tests/components/mqtt/test.esp32-c3-ard.yaml | 6 -- .../mqtt_subscribe/test.esp32-ard.yaml | 1 - .../mqtt_subscribe/test.esp32-c3-ard.yaml | 1 - tests/components/ms5611/test.esp32-ard.yaml | 5 -- .../components/ms5611/test.esp32-c3-ard.yaml | 5 -- tests/components/msa3xx/test.esp32-ard.yaml | 6 -- .../components/msa3xx/test.esp32-c3-ard.yaml | 6 -- tests/components/my9231/test.esp32-ard.yaml | 1 - .../components/my9231/test.esp32-c3-ard.yaml | 1 - tests/components/nau7802/test.esp32-ard.yaml | 6 -- .../components/nau7802/test.esp32-c3-ard.yaml | 6 -- tests/components/noblex/test.esp32-ard.yaml | 1 - .../components/noblex/test.esp32-c3-ard.yaml | 1 - tests/components/npi19/test.esp32-ard.yaml | 5 -- tests/components/npi19/test.esp32-s3-ard.yaml | 5 -- tests/components/ntc/test.esp32-ard.yaml | 4 -- tests/components/ntc/test.esp32-c3-ard.yaml | 4 -- tests/components/ntc/test.esp32-s2-ard.yaml | 4 -- tests/components/ntc/test.esp32-s3-ard.yaml | 4 -- .../online_image/test.esp32-ard.yaml | 4 -- tests/components/opt3001/test.esp32-ard.yaml | 5 -- .../components/opt3001/test.esp32-c3-ard.yaml | 5 -- tests/components/output/test.esp32-ard.yaml | 5 -- .../components/output/test.esp32-c3-ard.yaml | 5 -- tests/components/packages/test.esp32-ard.yaml | 11 ---- .../packet_transport/test.esp32-ard.yaml | 1 - .../packet_transport/test.esp32-c3-ard.yaml | 1 - .../components/partition/test.esp32-ard.yaml | 5 -- .../partition/test.esp32-c3-ard.yaml | 5 -- tests/components/pca6416a/test.esp32-ard.yaml | 5 -- .../pca6416a/test.esp32-c3-ard.yaml | 5 -- tests/components/pca9554/test.esp32-ard.yaml | 5 -- .../components/pca9554/test.esp32-c3-ard.yaml | 5 -- tests/components/pca9685/test.esp32-ard.yaml | 5 -- .../components/pca9685/test.esp32-c3-ard.yaml | 5 -- tests/components/pcd8544/test.esp32-ard.yaml | 9 --- .../components/pcd8544/test.esp32-c3-ard.yaml | 9 --- tests/components/pcf85063/test.esp32-ard.yaml | 5 -- .../pcf85063/test.esp32-c3-ard.yaml | 5 -- tests/components/pcf8563/test.esp32-ard.yaml | 5 -- .../components/pcf8563/test.esp32-c3-ard.yaml | 5 -- tests/components/pcf8574/test.esp32-ard.yaml | 5 -- .../components/pcf8574/test.esp32-c3-ard.yaml | 5 -- .../pi4ioe5v6408/test.esp32-ard.yaml | 5 -- tests/components/pid/test.esp32-ard.yaml | 1 - tests/components/pid/test.esp32-c3-ard.yaml | 1 - tests/components/pipsolar/test.esp32-ard.yaml | 5 -- .../pipsolar/test.esp32-c3-ard.yaml | 5 -- tests/components/pm1006/test.esp32-ard.yaml | 5 -- .../components/pm1006/test.esp32-c3-ard.yaml | 5 -- tests/components/pm2005/test.esp32-ard.yaml | 5 -- .../components/pm2005/test.esp32-c3-ard.yaml | 5 -- tests/components/pmsa003i/test.esp32-ard.yaml | 5 -- .../pmsa003i/test.esp32-c3-ard.yaml | 5 -- tests/components/pmsx003/test.esp32-ard.yaml | 5 -- .../components/pmsx003/test.esp32-c3-ard.yaml | 5 -- tests/components/pmwcs3/test.esp32-ard.yaml | 5 -- .../components/pmwcs3/test.esp32-c3-ard.yaml | 5 -- .../components/pn532_i2c/test.esp32-ard.yaml | 5 -- .../pn532_i2c/test.esp32-c3-ard.yaml | 5 -- .../components/pn532_spi/test.esp32-ard.yaml | 7 --- .../pn532_spi/test.esp32-c3-ard.yaml | 7 --- .../components/pn7150_i2c/test.esp32-ard.yaml | 7 --- .../pn7150_i2c/test.esp32-c3-ard.yaml | 7 --- .../components/pn7160_i2c/test.esp32-ard.yaml | 7 --- .../pn7160_i2c/test.esp32-c3-ard.yaml | 7 --- .../components/pn7160_spi/test.esp32-ard.yaml | 9 --- .../pn7160_spi/test.esp32-c3-ard.yaml | 9 --- .../power_supply/test.esp32-ard.yaml | 1 - .../power_supply/test.esp32-c3-ard.yaml | 1 - .../components/prometheus/test.esp32-ard.yaml | 38 ------------ .../prometheus/test.esp32-c3-ard.yaml | 5 -- tests/components/psram/test.esp32-ard.yaml | 1 - tests/components/psram/test.esp32-s2-ard.yaml | 1 - tests/components/psram/test.esp32-s3-ard.yaml | 3 - .../pulse_counter/test.esp32-ard.yaml | 1 - .../pulse_counter/test.esp32-c3-ard.yaml | 1 - .../pulse_meter/test.esp32-ard.yaml | 1 - .../pulse_meter/test.esp32-c3-ard.yaml | 1 - .../pulse_width/test.esp32-ard.yaml | 1 - .../pulse_width/test.esp32-c3-ard.yaml | 1 - .../pvvx_mithermometer/test.esp32-ard.yaml | 1 - .../pvvx_mithermometer/test.esp32-c3-ard.yaml | 1 - .../components/pylontech/test.esp32-ard.yaml | 5 -- .../pylontech/test.esp32-c3-ard.yaml | 5 -- tests/components/pzem004t/test.esp32-ard.yaml | 5 -- .../pzem004t/test.esp32-c3-ard.yaml | 5 -- tests/components/pzemac/test.esp32-ard.yaml | 5 -- .../components/pzemac/test.esp32-c3-ard.yaml | 5 -- tests/components/pzemdc/test.esp32-ard.yaml | 5 -- .../components/pzemdc/test.esp32-c3-ard.yaml | 5 -- tests/components/qmc5883l/test.esp32-ard.yaml | 6 -- .../qmc5883l/test.esp32-c3-ard.yaml | 6 -- tests/components/qmp6988/test.esp32-ard.yaml | 5 -- .../components/qmp6988/test.esp32-c3-ard.yaml | 5 -- tests/components/qr_code/test.esp32-ard.yaml | 8 --- .../components/qr_code/test.esp32-c3-ard.yaml | 9 --- .../components/qwiic_pir/test.esp32-ard.yaml | 5 -- .../qwiic_pir/test.esp32-c3-ard.yaml | 5 -- .../radon_eye_ble/test.esp32-ard.yaml | 1 - .../radon_eye_ble/test.esp32-c3-ard.yaml | 1 - .../radon_eye_rd200/test.esp32-ard.yaml | 1 - .../radon_eye_rd200/test.esp32-c3-ard.yaml | 1 - .../components/rc522_i2c/test.esp32-ard.yaml | 5 -- .../rc522_i2c/test.esp32-c3-ard.yaml | 5 -- .../components/rc522_spi/test.esp32-ard.yaml | 7 --- .../rc522_spi/test.esp32-c3-ard.yaml | 7 --- tests/components/rdm6300/test.esp32-ard.yaml | 5 -- .../components/rdm6300/test.esp32-c3-ard.yaml | 5 -- .../remote_receiver/test.esp32-ard.yaml | 9 --- .../remote_receiver/test.esp32-c3-ard.yaml | 9 --- .../remote_transmitter/test.esp32-ard.yaml | 7 --- .../remote_transmitter/test.esp32-c3-ard.yaml | 7 --- .../components/resampler/test.esp32-ard.yaml | 7 --- .../resampler/test.esp32-c3-ard.yaml | 7 --- .../resampler/test.esp32-s3-ard.yaml | 7 --- .../components/resistance/test.esp32-ard.yaml | 4 -- .../resistance/test.esp32-c3-ard.yaml | 4 -- .../resistance/test.esp32-s2-ard.yaml | 4 -- .../resistance/test.esp32-s3-ard.yaml | 4 -- tests/components/restart/test.esp32-ard.yaml | 1 - .../components/restart/test.esp32-c3-ard.yaml | 1 - .../components/rf_bridge/test.esp32-ard.yaml | 5 -- .../rf_bridge/test.esp32-c3-ard.yaml | 5 -- tests/components/rgb/test.esp32-ard.yaml | 7 --- tests/components/rgb/test.esp32-c3-ard.yaml | 7 --- tests/components/rgbct/test.esp32-ard.yaml | 9 --- tests/components/rgbct/test.esp32-c3-ard.yaml | 9 --- tests/components/rgbw/test.esp32-ard.yaml | 8 --- tests/components/rgbw/test.esp32-c3-ard.yaml | 8 --- tests/components/rgbww/test.esp32-ard.yaml | 9 --- tests/components/rgbww/test.esp32-c3-ard.yaml | 9 --- .../rotary_encoder/test.esp32-ard.yaml | 6 -- .../rotary_encoder/test.esp32-c3-ard.yaml | 6 -- tests/components/rtttl/test.esp32-ard.yaml | 5 -- tests/components/rtttl/test.esp32-c3-ard.yaml | 5 -- .../runtime_stats/test.esp32-ard.yaml | 1 - .../components/ruuvi_ble/test.esp32-ard.yaml | 1 - .../ruuvi_ble/test.esp32-c3-ard.yaml | 1 - tests/components/ruuvitag/test.esp32-ard.yaml | 1 - .../ruuvitag/test.esp32-c3-ard.yaml | 1 - .../safe_mode/test-enabled.esp32-ard.yaml | 1 - .../safe_mode/test-enabled.esp32-c3-ard.yaml | 1 - tests/components/scd30/test.esp32-ard.yaml | 5 -- tests/components/scd30/test.esp32-c3-ard.yaml | 5 -- tests/components/scd4x/test.esp32-ard.yaml | 5 -- tests/components/scd4x/test.esp32-c3-ard.yaml | 5 -- tests/components/script/test.esp32-ard.yaml | 1 - .../components/script/test.esp32-c3-ard.yaml | 1 - .../components/sdm_meter/test.esp32-ard.yaml | 5 -- .../sdm_meter/test.esp32-c3-ard.yaml | 5 -- tests/components/sdp3x/test.esp32-ard.yaml | 5 -- tests/components/sdp3x/test.esp32-c3-ard.yaml | 5 -- tests/components/sds011/test.esp32-ard.yaml | 5 -- .../components/sds011/test.esp32-c3-ard.yaml | 5 -- .../seeed_mr24hpc1/test.esp32-c3-ard.yaml | 5 -- .../seeed_mr60bha2/test.esp32-c3-ard.yaml | 5 -- .../seeed_mr60fda2/test.esp32-c3-ard.yaml | 5 -- .../selec_meter/test.esp32-ard.yaml | 5 -- .../selec_meter/test.esp32-c3-ard.yaml | 5 -- tests/components/sen0321/test.esp32-ard.yaml | 5 -- .../components/sen0321/test.esp32-c3-ard.yaml | 5 -- tests/components/sen21231/test.esp32-ard.yaml | 5 -- .../sen21231/test.esp32-c3-ard.yaml | 5 -- tests/components/sen5x/test.esp32-ard.yaml | 5 -- tests/components/sen5x/test.esp32-c3-ard.yaml | 5 -- tests/components/senseair/test.esp32-ard.yaml | 5 -- .../senseair/test.esp32-c3-ard.yaml | 5 -- tests/components/servo/test.esp32-ard.yaml | 5 -- tests/components/servo/test.esp32-c3-ard.yaml | 5 -- tests/components/sfa30/test.esp32-ard.yaml | 5 -- tests/components/sfa30/test.esp32-c3-ard.yaml | 5 -- tests/components/sgp30/test.esp32-ard.yaml | 5 -- tests/components/sgp30/test.esp32-c3-ard.yaml | 5 -- tests/components/sgp4x/test.esp32-ard.yaml | 5 -- tests/components/sgp4x/test.esp32-c3-ard.yaml | 5 -- tests/components/sht3xd/test.esp32-ard.yaml | 5 -- .../components/sht3xd/test.esp32-c3-ard.yaml | 5 -- tests/components/sht4x/test.esp32-ard.yaml | 5 -- tests/components/sht4x/test.esp32-c3-ard.yaml | 5 -- tests/components/shtcx/test.esp32-ard.yaml | 5 -- tests/components/shtcx/test.esp32-c3-ard.yaml | 5 -- tests/components/shutdown/test.esp32-ard.yaml | 1 - .../shutdown/test.esp32-c3-ard.yaml | 1 - .../sigma_delta_output/test.esp32-ard.yaml | 1 - .../sigma_delta_output/test.esp32-c3-ard.yaml | 1 - tests/components/sim800l/test.esp32-ard.yaml | 5 -- .../components/sim800l/test.esp32-c3-ard.yaml | 5 -- tests/components/slow_pwm/test.esp32-ard.yaml | 1 - .../slow_pwm/test.esp32-c3-ard.yaml | 1 - tests/components/sm16716/test.esp32-ard.yaml | 5 -- .../components/sm16716/test.esp32-c3-ard.yaml | 5 -- tests/components/sm2135/test.esp32-ard.yaml | 5 -- .../components/sm2135/test.esp32-c3-ard.yaml | 5 -- tests/components/sm2235/test.esp32-ard.yaml | 5 -- .../components/sm2235/test.esp32-c3-ard.yaml | 5 -- tests/components/sm2335/test.esp32-ard.yaml | 5 -- .../components/sm2335/test.esp32-c3-ard.yaml | 5 -- tests/components/sm300d2/test.esp32-ard.yaml | 5 -- .../components/sm300d2/test.esp32-c3-ard.yaml | 5 -- tests/components/sml/test.esp32-ard.yaml | 5 -- tests/components/sml/test.esp32-c3-ard.yaml | 5 -- tests/components/smt100/test.esp32-ard.yaml | 5 -- .../components/smt100/test.esp32-c3-ard.yaml | 5 -- .../components/sn74hc165/test.esp32-ard.yaml | 7 --- .../sn74hc165/test.esp32-c3-ard.yaml | 7 --- .../components/sn74hc595/test.esp32-ard.yaml | 12 ---- .../sn74hc595/test.esp32-c3-ard.yaml | 12 ---- tests/components/sntp/test.esp32-ard.yaml | 1 - tests/components/sntp/test.esp32-c3-ard.yaml | 1 - .../components/sonoff_d1/test.esp32-ard.yaml | 5 -- .../sonoff_d1/test.esp32-c3-ard.yaml | 5 -- .../sound_level/test.esp32-ard.yaml | 6 -- .../sound_level/test.esp32-c3-ard.yaml | 6 -- .../sound_level/test.esp32-s3-ard.yaml | 6 -- tests/components/speed/test.esp32-ard.yaml | 5 -- tests/components/speed/test.esp32-c3-ard.yaml | 5 -- .../components/spi_device/test.esp32-ard.yaml | 6 -- .../spi_device/test.esp32-c3-ard.yaml | 6 -- .../spi_led_strip/test.esp32-ard.yaml | 5 -- .../spi_led_strip/test.esp32-c3-ard.yaml | 5 -- .../components/sprinkler/test.esp32-ard.yaml | 1 - .../sprinkler/test.esp32-c3-ard.yaml | 1 - tests/components/sps30/test.esp32-ard.yaml | 5 -- tests/components/sps30/test.esp32-c3-ard.yaml | 5 -- .../ssd1306_i2c/test.esp32-ard.yaml | 6 -- .../ssd1306_i2c/test.esp32-c3-ard.yaml | 6 -- .../ssd1306_spi/test.esp32-ard.yaml | 8 --- .../ssd1306_spi/test.esp32-c3-ard.yaml | 9 --- .../ssd1322_spi/test.esp32-ard.yaml | 8 --- .../ssd1322_spi/test.esp32-c3-ard.yaml | 9 --- .../ssd1325_spi/test.esp32-ard.yaml | 8 --- .../ssd1325_spi/test.esp32-c3-ard.yaml | 9 --- .../ssd1327_i2c/test.esp32-ard.yaml | 6 -- .../ssd1327_i2c/test.esp32-c3-ard.yaml | 6 -- .../ssd1327_spi/test.esp32-ard.yaml | 8 --- .../ssd1327_spi/test.esp32-c3-ard.yaml | 9 --- .../ssd1331_spi/test.esp32-ard.yaml | 8 --- .../ssd1331_spi/test.esp32-c3-ard.yaml | 9 --- .../ssd1351_spi/test.esp32-ard.yaml | 8 --- .../ssd1351_spi/test.esp32-c3-ard.yaml | 9 --- .../components/st7567_i2c/test.esp32-ard.yaml | 6 -- .../st7567_i2c/test.esp32-c3-ard.yaml | 6 -- .../components/st7567_spi/test.esp32-ard.yaml | 8 --- .../st7567_spi/test.esp32-c3-ard.yaml | 9 --- tests/components/st7735/test.esp32-ard.yaml | 8 --- .../components/st7735/test.esp32-c3-ard.yaml | 9 --- tests/components/st7789v/test.esp32-ard.yaml | 8 --- .../components/st7789v/test.esp32-c3-ard.yaml | 9 --- tests/components/st7920/test.esp32-ard.yaml | 6 -- .../components/st7920/test.esp32-c3-ard.yaml | 7 --- tests/components/statsD/test.esp32-ard.yaml | 2 - .../components/statsD/test.esp32-c3-ard.yaml | 2 - tests/components/status/test.esp32-ard.yaml | 1 - .../components/status/test.esp32-c3-ard.yaml | 1 - .../components/status_led/test.esp32-ard.yaml | 1 - .../status_led/test.esp32-c3-ard.yaml | 1 - tests/components/stepper/test.esp32-ard.yaml | 1 - .../components/stepper/test.esp32-c3-ard.yaml | 1 - tests/components/sts3x/test.esp32-ard.yaml | 5 -- tests/components/sts3x/test.esp32-c3-ard.yaml | 5 -- tests/components/sun/test.esp32-ard.yaml | 1 - tests/components/sun/test.esp32-c3-ard.yaml | 1 - .../components/sun_gtil2/test.esp32-ard.yaml | 4 -- .../sun_gtil2/test.esp32-c3-ard.yaml | 4 -- tests/components/switch/test.esp32-ard.yaml | 2 - .../components/switch/test.esp32-c3-ard.yaml | 2 - tests/components/sx126x/test.esp32-ard.yaml | 10 ---- .../components/sx126x/test.esp32-c3-ard.yaml | 10 ---- tests/components/sx127x/test.esp32-ard.yaml | 9 --- .../components/sx127x/test.esp32-c3-ard.yaml | 9 --- tests/components/sx1509/test.esp32-ard.yaml | 5 -- .../components/sx1509/test.esp32-c3-ard.yaml | 5 -- tests/components/syslog/test.esp32-ard.yaml | 1 - .../components/syslog/test.esp32-c3-ard.yaml | 1 - tests/components/t6615/test.esp32-ard.yaml | 5 -- tests/components/t6615/test.esp32-c3-ard.yaml | 5 -- tests/components/tc74/test.esp32-ard.yaml | 5 -- tests/components/tc74/test.esp32-c3-ard.yaml | 5 -- tests/components/tca9548a/test.esp32-ard.yaml | 5 -- .../tca9548a/test.esp32-c3-ard.yaml | 5 -- tests/components/tca9555/test.esp32-ard.yaml | 5 -- .../components/tca9555/test.esp32-c3-ard.yaml | 5 -- tests/components/tcl112/test.esp32-ard.yaml | 4 -- .../components/tcl112/test.esp32-c3-ard.yaml | 4 -- tests/components/tcs34725/test.esp32-ard.yaml | 5 -- .../tcs34725/test.esp32-c3-ard.yaml | 5 -- tests/components/tee501/test.esp32-ard.yaml | 5 -- .../components/tee501/test.esp32-c3-ard.yaml | 5 -- tests/components/teleinfo/test.esp32-ard.yaml | 5 -- .../teleinfo/test.esp32-c3-ard.yaml | 5 -- tests/components/tem3200/test.esp32-ard.yaml | 5 -- .../components/tem3200/test.esp32-s3-ard.yaml | 5 -- tests/components/template/test.esp32-ard.yaml | 2 - .../template/test.esp32-c3-ard.yaml | 2 - .../components/thermostat/test.esp32-ard.yaml | 1 - .../thermostat/test.esp32-c3-ard.yaml | 1 - .../components/time_based/test.esp32-ard.yaml | 1 - .../time_based/test.esp32-c3-ard.yaml | 1 - .../components/tlc59208f/test.esp32-ard.yaml | 5 -- .../tlc59208f/test.esp32-c3-ard.yaml | 5 -- tests/components/tlc5947/test.esp32-ard.yaml | 7 --- .../components/tlc5947/test.esp32-c3-ard.yaml | 7 --- tests/components/tlc5971/test.esp32-ard.yaml | 7 --- .../components/tlc5971/test.esp32-c3-ard.yaml | 6 -- .../components/tlc5971/test.esp32-s2-ard.yaml | 6 -- tests/components/tm1621/test.esp32-ard.yaml | 7 --- .../components/tm1621/test.esp32-c3-ard.yaml | 7 --- tests/components/tm1637/test.esp32-ard.yaml | 5 -- .../components/tm1637/test.esp32-c3-ard.yaml | 5 -- tests/components/tm1638/test.esp32-ard.yaml | 1 - .../components/tm1638/test.esp32-c3-ard.yaml | 1 - tests/components/tm1651/test.esp32-ard.yaml | 1 - .../components/tm1651/test.esp32-c3-ard.yaml | 1 - tests/components/tmp102/test.esp32-ard.yaml | 5 -- .../components/tmp102/test.esp32-c3-ard.yaml | 5 -- tests/components/tmp1075/test.esp32-ard.yaml | 5 -- .../components/tmp1075/test.esp32-c3-ard.yaml | 5 -- tests/components/tmp117/test.esp32-ard.yaml | 5 -- .../components/tmp117/test.esp32-c3-ard.yaml | 5 -- tests/components/tof10120/test.esp32-ard.yaml | 5 -- .../tof10120/test.esp32-c3-ard.yaml | 5 -- tests/components/tormatic/test.esp32-ard.yaml | 5 -- .../tormatic/test.esp32-c3-ard.yaml | 5 -- tests/components/toshiba/test.esp32-ard.yaml | 4 -- .../components/toshiba/test.esp32-c3-ard.yaml | 4 -- .../total_daily_energy/test.esp32-ard.yaml | 6 -- .../total_daily_energy/test.esp32-c3-ard.yaml | 6 -- tests/components/tsl2561/test.esp32-ard.yaml | 5 -- .../components/tsl2561/test.esp32-c3-ard.yaml | 5 -- tests/components/tsl2591/test.esp32-ard.yaml | 5 -- .../components/tsl2591/test.esp32-c3-ard.yaml | 5 -- tests/components/tt21100/test.esp32-ard.yaml | 8 --- .../components/tt21100/test.esp32-c3-ard.yaml | 8 --- .../components/ttp229_bsf/test.esp32-ard.yaml | 5 -- .../ttp229_bsf/test.esp32-c3-ard.yaml | 5 -- .../components/ttp229_lsf/test.esp32-ard.yaml | 5 -- .../ttp229_lsf/test.esp32-c3-ard.yaml | 5 -- tests/components/tuya/test.esp32-ard.yaml | 6 -- tests/components/tuya/test.esp32-c3-ard.yaml | 6 -- tests/components/tx20/test.esp32-ard.yaml | 1 - tests/components/tx20/test.esp32-c3-ard.yaml | 1 - ...st-uart_max_with_usb_cdc.esp32-c3-ard.yaml | 36 ------------ ...st-uart_max_with_usb_cdc.esp32-s2-ard.yaml | 36 ------------ ...st-uart_max_with_usb_cdc.esp32-s3-ard.yaml | 48 --------------- tests/components/uart/test.esp32-ard.yaml | 18 ------ tests/components/uart/test.esp32-c3-ard.yaml | 18 ------ tests/components/udp/test.esp32-ard.yaml | 1 - tests/components/udp/test.esp32-c3-ard.yaml | 1 - tests/components/ufire_ec/test.esp32-ard.yaml | 5 -- .../ufire_ec/test.esp32-c3-ard.yaml | 5 -- .../components/ufire_ise/test.esp32-ard.yaml | 5 -- .../ufire_ise/test.esp32-c3-ard.yaml | 5 -- tests/components/uln2003/test.esp32-ard.yaml | 7 --- .../components/uln2003/test.esp32-c3-ard.yaml | 7 --- .../components/ultrasonic/test.esp32-ard.yaml | 1 - .../ultrasonic/test.esp32-c3-ard.yaml | 1 - tests/components/update/test.esp32-ard.yaml | 4 -- .../uponor_smatrix/test.esp32-ard.yaml | 5 -- .../uponor_smatrix/test.esp32-c3-ard.yaml | 5 -- tests/components/uptime/test.esp32-ard.yaml | 1 - .../components/uptime/test.esp32-c3-ard.yaml | 1 - tests/components/vbus/test.esp32-ard.yaml | 5 -- tests/components/vbus/test.esp32-c3-ard.yaml | 5 -- tests/components/veml3235/test.esp32-ard.yaml | 5 -- .../veml3235/test.esp32-c3-ard.yaml | 5 -- tests/components/veml7700/test.esp32-ard.yaml | 5 -- .../veml7700/test.esp32-c3-ard.yaml | 5 -- tests/components/version/test.esp32-ard.yaml | 1 - .../components/version/test.esp32-c3-ard.yaml | 1 - tests/components/vl53l0x/test.esp32-ard.yaml | 5 -- .../components/vl53l0x/test.esp32-c3-ard.yaml | 5 -- .../voice_assistant/test.esp32-ard.yaml | 8 --- .../voice_assistant/test.esp32-c3-ard.yaml | 8 --- .../wake_on_lan/test.esp32-ard.yaml | 1 - .../wake_on_lan/test.esp32-c3-ard.yaml | 1 - .../waveshare_epaper/test.esp32-ard.yaml | 9 --- .../waveshare_epaper/test.esp32-c3-ard.yaml | 9 --- .../components/whirlpool/test.esp32-ard.yaml | 4 -- .../whirlpool/test.esp32-c3-ard.yaml | 4 -- tests/components/whynter/test.esp32-ard.yaml | 4 -- .../components/whynter/test.esp32-c3-ard.yaml | 4 -- tests/components/wiegand/test.esp32-ard.yaml | 1 - .../components/wiegand/test.esp32-c3-ard.yaml | 1 - tests/components/wifi/test-eap.esp32-ard.yaml | 1 - tests/components/wifi/test.esp32-ard.yaml | 1 - tests/components/wifi/test.esp32-c3-ard.yaml | 1 - .../components/wifi_info/test.esp32-ard.yaml | 1 - .../wifi_info/test.esp32-c3-ard.yaml | 1 - .../wifi_signal/test.esp32-ard.yaml | 1 - .../wifi_signal/test.esp32-c3-ard.yaml | 1 - .../components/wireguard/test.esp32-ard.yaml | 4 -- .../wireguard/test.esp32-c3-ard.yaml | 1 - .../components/wk2132_i2c/test.esp32-ard.yaml | 5 -- .../wk2132_i2c/test.esp32-s3-ard.yaml | 5 -- .../components/wk2132_spi/test.esp32-ard.yaml | 7 --- .../wk2132_spi/test.esp32-s3-ard.yaml | 7 --- .../components/wk2168_i2c/test.esp32-ard.yaml | 5 -- .../wk2168_i2c/test.esp32-s3-ard.yaml | 5 -- .../components/wk2168_spi/test.esp32-ard.yaml | 7 --- .../wk2168_spi/test.esp32-s3-ard.yaml | 7 --- .../components/wk2204_i2c/test.esp32-ard.yaml | 5 -- .../wk2204_i2c/test.esp32-s3-ard.yaml | 5 -- .../components/wk2204_spi/test.esp32-ard.yaml | 7 --- .../wk2204_spi/test.esp32-s3-ard.yaml | 7 --- .../components/wk2212_i2c/test.esp32-ard.yaml | 5 -- .../wk2212_i2c/test.esp32-s3-ard.yaml | 5 -- .../components/wk2212_spi/test.esp32-ard.yaml | 7 --- .../wk2212_spi/test.esp32-s3-ard.yaml | 7 --- tests/components/wl_134/test.esp32-ard.yaml | 5 -- .../components/wl_134/test.esp32-c3-ard.yaml | 5 -- tests/components/wts01/test.esp32-ard.yaml | 5 -- tests/components/wts01/test.esp32-c3-ard.yaml | 5 -- tests/components/x9c/test.esp32-ard.yaml | 6 -- tests/components/x9c/test.esp32-c3-ard.yaml | 6 -- tests/components/xgzp68xx/test.esp32-ard.yaml | 5 -- .../xgzp68xx/test.esp32-c3-ard.yaml | 5 -- .../components/xiaomi_ble/test.esp32-ard.yaml | 1 - .../xiaomi_ble/test.esp32-c3-ard.yaml | 1 - .../xiaomi_cgd1/test.esp32-ard.yaml | 1 - .../xiaomi_cgd1/test.esp32-c3-ard.yaml | 1 - .../xiaomi_cgdk2/test.esp32-ard.yaml | 1 - .../xiaomi_cgdk2/test.esp32-c3-ard.yaml | 1 - .../xiaomi_cgg1/test.esp32-ard.yaml | 1 - .../xiaomi_cgg1/test.esp32-c3-ard.yaml | 1 - .../xiaomi_cgpr1/test.esp32-ard.yaml | 1 - .../xiaomi_cgpr1/test.esp32-c3-ard.yaml | 1 - .../xiaomi_gcls002/test.esp32-ard.yaml | 1 - .../xiaomi_gcls002/test.esp32-c3-ard.yaml | 1 - .../xiaomi_hhccjcy01/test.esp32-ard.yaml | 1 - .../xiaomi_hhccjcy01/test.esp32-c3-ard.yaml | 1 - .../xiaomi_hhccpot002/test.esp32-ard.yaml | 1 - .../xiaomi_hhccpot002/test.esp32-c3-ard.yaml | 1 - .../xiaomi_jqjcy01ym/test.esp32-ard.yaml | 1 - .../xiaomi_jqjcy01ym/test.esp32-c3-ard.yaml | 1 - .../xiaomi_lywsd02/test.esp32-ard.yaml | 1 - .../xiaomi_lywsd02/test.esp32-c3-ard.yaml | 1 - .../xiaomi_lywsd02mmc/test.esp32-ard.yaml | 1 - .../xiaomi_lywsd02mmc/test.esp32-c3-ard.yaml | 1 - .../xiaomi_lywsd03mmc/test.esp32-ard.yaml | 1 - .../xiaomi_lywsd03mmc/test.esp32-c3-ard.yaml | 1 - .../xiaomi_lywsdcgq/test.esp32-ard.yaml | 1 - .../xiaomi_lywsdcgq/test.esp32-c3-ard.yaml | 1 - .../xiaomi_mhoc303/test.esp32-ard.yaml | 1 - .../xiaomi_mhoc303/test.esp32-c3-ard.yaml | 1 - .../xiaomi_mhoc401/test.esp32-ard.yaml | 1 - .../xiaomi_mhoc401/test.esp32-c3-ard.yaml | 1 - .../xiaomi_miscale/test.esp32-ard.yaml | 1 - .../xiaomi_miscale/test.esp32-c3-ard.yaml | 1 - .../xiaomi_mjyd02yla/test.esp32-ard.yaml | 1 - .../xiaomi_mjyd02yla/test.esp32-c3-ard.yaml | 1 - .../xiaomi_mue4094rt/test.esp32-ard.yaml | 1 - .../xiaomi_mue4094rt/test.esp32-c3-ard.yaml | 1 - .../xiaomi_rtcgq02lm/test.esp32-ard.yaml | 1 - .../xiaomi_rtcgq02lm/test.esp32-c3-ard.yaml | 1 - .../xiaomi_wx08zm/test.esp32-ard.yaml | 1 - .../xiaomi_wx08zm/test.esp32-c3-ard.yaml | 1 - .../xiaomi_xmwsdj04mmc/test.esp32-ard.yaml | 1 - .../xiaomi_xmwsdj04mmc/test.esp32-c3-ard.yaml | 1 - tests/components/xl9535/test.esp32-ard.yaml | 5 -- .../components/xl9535/test.esp32-c3-ard.yaml | 5 -- tests/components/xpt2046/test.esp32-ard.yaml | 11 ---- .../components/xpt2046/test.esp32-c3-ard.yaml | 11 ---- tests/components/yashima/test.esp32-ard.yaml | 4 -- .../components/yashima/test.esp32-c3-ard.yaml | 4 -- tests/components/zhlt01/test.esp32-ard.yaml | 4 -- .../components/zhlt01/test.esp32-c3-ard.yaml | 4 -- .../zio_ultrasonic/test.esp32-ard.yaml | 5 -- .../zio_ultrasonic/test.esp32-c3-ard.yaml | 5 -- .../zwave_proxy/test.esp32-ard.yaml | 5 -- .../zwave_proxy/test.esp32-c3-ard.yaml | 5 -- tests/components/zyaura/test.esp32-ard.yaml | 5 -- .../components/zyaura/test.esp32-c3-ard.yaml | 5 -- 1007 files changed, 4942 deletions(-) delete mode 100644 tests/components/a01nyub/test.esp32-ard.yaml delete mode 100644 tests/components/a01nyub/test.esp32-c3-ard.yaml delete mode 100644 tests/components/a02yyuw/test.esp32-ard.yaml delete mode 100644 tests/components/a02yyuw/test.esp32-c3-ard.yaml delete mode 100644 tests/components/a4988/test.esp32-ard.yaml delete mode 100644 tests/components/a4988/test.esp32-c3-ard.yaml delete mode 100644 tests/components/absolute_humidity/test.esp32-ard.yaml delete mode 100644 tests/components/absolute_humidity/test.esp32-c3-ard.yaml delete mode 100644 tests/components/adc/test.esp32-ard.yaml delete mode 100644 tests/components/adc/test.esp32-c3-ard.yaml delete mode 100644 tests/components/adc/test.esp32-s2-ard.yaml delete mode 100644 tests/components/adc/test.esp32-s3-ard.yaml delete mode 100644 tests/components/adc128s102/test.esp32-ard.yaml delete mode 100644 tests/components/adc128s102/test.esp32-c3-ard.yaml delete mode 100644 tests/components/addressable_light/esp32_rmt_led_strip.esp32-ard.yaml delete mode 100644 tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-ard.yaml delete mode 100644 tests/components/addressable_light/fastled_clockless.esp32-ard.yaml delete mode 100644 tests/components/ade7880/test.esp32-ard.yaml delete mode 100644 tests/components/ade7880/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ade7953_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/ade7953_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ade7953_spi/test.esp32-ard.yaml delete mode 100644 tests/components/ade7953_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ads1115/test.esp32-ard.yaml delete mode 100644 tests/components/ads1115/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ags10/test.esp32-ard.yaml delete mode 100644 tests/components/ags10/test.esp32-c3-ard.yaml delete mode 100644 tests/components/aht10/test.esp32-ard.yaml delete mode 100644 tests/components/aht10/test.esp32-c3-ard.yaml delete mode 100644 tests/components/aic3204/test.esp32-ard.yaml delete mode 100644 tests/components/aic3204/test.esp32-c3-ard.yaml delete mode 100644 tests/components/airthings_wave_mini/test.esp32-ard.yaml delete mode 100644 tests/components/airthings_wave_mini/test.esp32-c3-ard.yaml delete mode 100644 tests/components/airthings_wave_plus/test.esp32-ard.yaml delete mode 100644 tests/components/airthings_wave_plus/test.esp32-c3-ard.yaml delete mode 100644 tests/components/alarm_control_panel/test.esp32-ard.yaml delete mode 100644 tests/components/alarm_control_panel/test.esp32-c3-ard.yaml delete mode 100644 tests/components/alpha3/test.esp32-ard.yaml delete mode 100644 tests/components/alpha3/test.esp32-c3-ard.yaml delete mode 100644 tests/components/am2315c/test.esp32-ard.yaml delete mode 100644 tests/components/am2315c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/am2320/test.esp32-ard.yaml delete mode 100644 tests/components/am2320/test.esp32-c3-ard.yaml delete mode 100644 tests/components/am43/test.esp32-ard.yaml delete mode 100644 tests/components/am43/test.esp32-c3-ard.yaml delete mode 100644 tests/components/analog_threshold/test.esp32-ard.yaml delete mode 100644 tests/components/analog_threshold/test.esp32-c3-ard.yaml delete mode 100644 tests/components/animation/test.esp32-ard.yaml delete mode 100644 tests/components/animation/test.esp32-c3-ard.yaml delete mode 100644 tests/components/anova/test.esp32-ard.yaml delete mode 100644 tests/components/anova/test.esp32-c3-ard.yaml delete mode 100644 tests/components/apds9306/test.esp32-ard.yaml delete mode 100644 tests/components/apds9306/test.esp32-c3-ard.yaml delete mode 100644 tests/components/apds9960/test.esp32-ard.yaml delete mode 100644 tests/components/apds9960/test.esp32-c3-ard.yaml delete mode 100644 tests/components/api/test.esp32-ard.yaml delete mode 100644 tests/components/api/test.esp32-c3-ard.yaml delete mode 100644 tests/components/as3935_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/as3935_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/as3935_spi/test.esp32-ard.yaml delete mode 100644 tests/components/as3935_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/as5600/test.esp32-ard.yaml delete mode 100644 tests/components/as5600/test.esp32-c3-ard.yaml delete mode 100644 tests/components/as7341/test.esp32-ard.yaml delete mode 100644 tests/components/as7341/test.esp32-c3-ard.yaml delete mode 100644 tests/components/at581x/test.esp32-ard.yaml delete mode 100644 tests/components/at581x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/atc_mithermometer/test.esp32-ard.yaml delete mode 100644 tests/components/atc_mithermometer/test.esp32-c3-ard.yaml delete mode 100644 tests/components/atm90e26/test.esp32-ard.yaml delete mode 100644 tests/components/atm90e26/test.esp32-c3-ard.yaml delete mode 100644 tests/components/atm90e32/test.esp32-ard.yaml delete mode 100644 tests/components/atm90e32/test.esp32-c3-ard.yaml delete mode 100644 tests/components/axs15231/test.esp32-ard.yaml delete mode 100644 tests/components/axs15231/test.esp32-c3-ard.yaml delete mode 100644 tests/components/b_parasite/test.esp32-ard.yaml delete mode 100644 tests/components/b_parasite/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ballu/test.esp32-ard.yaml delete mode 100644 tests/components/bang_bang/test.esp32-ard.yaml delete mode 100644 tests/components/bang_bang/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bedjet/test.esp32-ard.yaml delete mode 100644 tests/components/bedjet/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bh1750/test.esp32-ard.yaml delete mode 100644 tests/components/bh1750/test.esp32-c3-ard.yaml delete mode 100644 tests/components/binary_sensor/test.esp32-ard.yaml delete mode 100644 tests/components/binary_sensor/test.esp32-c3-ard.yaml delete mode 100644 tests/components/binary_sensor_map/test.esp32-ard.yaml delete mode 100644 tests/components/binary_sensor_map/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bl0906/test.esp32-ard.yaml delete mode 100644 tests/components/bl0906/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bl0939/test.esp32-ard.yaml delete mode 100644 tests/components/bl0939/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bl0940/test.esp32-ard.yaml delete mode 100644 tests/components/bl0940/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bl0942/test.esp32-ard.yaml delete mode 100644 tests/components/bl0942/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ble_client/test.esp32-ard.yaml delete mode 100644 tests/components/ble_client/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ble_presence/test.esp32-ard.yaml delete mode 100644 tests/components/ble_presence/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ble_rssi/test.esp32-ard.yaml delete mode 100644 tests/components/ble_rssi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ble_scanner/test.esp32-ard.yaml delete mode 100644 tests/components/ble_scanner/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bluetooth_proxy/test.esp32-s3-ard.yaml delete mode 100644 tests/components/bme280_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/bme280_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bme280_spi/test.esp32-ard.yaml delete mode 100644 tests/components/bme280_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bme680/test.esp32-ard.yaml delete mode 100644 tests/components/bme680/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bme68x_bsec2_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/bme68x_bsec2_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bme68x_bsec2_i2c/test.esp32-s2-ard.yaml delete mode 100644 tests/components/bme68x_bsec2_i2c/test.esp32-s3-ard.yaml delete mode 100644 tests/components/bmi160/test.esp32-ard.yaml delete mode 100644 tests/components/bmi160/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bmp085/test.esp32-ard.yaml delete mode 100644 tests/components/bmp085/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bmp280_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/bmp280_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bmp280_spi/test.esp32-ard.yaml delete mode 100644 tests/components/bmp280_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bmp3xx_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/bmp3xx_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bmp3xx_spi/test.esp32-ard.yaml delete mode 100644 tests/components/bmp3xx_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bmp581/test.esp32-ard.yaml delete mode 100644 tests/components/bmp581/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bp1658cj/test.esp32-ard.yaml delete mode 100644 tests/components/bp1658cj/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bp5758d/test.esp32-ard.yaml delete mode 100644 tests/components/bp5758d/test.esp32-c3-ard.yaml delete mode 100644 tests/components/button/test.esp32-ard.yaml delete mode 100644 tests/components/button/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bytebuffer/test.esp32-ard.yaml delete mode 100644 tests/components/bytebuffer/test.esp32-c3-ard.yaml delete mode 100644 tests/components/camera/test.esp32-ard.yaml delete mode 100644 tests/components/camera_encoder/test.esp32-ard.yaml delete mode 100644 tests/components/canbus/test.esp32-ard.yaml delete mode 100644 tests/components/canbus/test.esp32-c3-ard.yaml delete mode 100644 tests/components/cap1188/test.esp32-ard.yaml delete mode 100644 tests/components/cap1188/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ccs811/test.esp32-ard.yaml delete mode 100644 tests/components/ccs811/test.esp32-c3-ard.yaml delete mode 100644 tests/components/cd74hc4067/test.esp32-ard.yaml delete mode 100644 tests/components/cd74hc4067/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ch422g/test.esp32-ard.yaml delete mode 100644 tests/components/ch422g/test.esp32-c3-ard.yaml delete mode 100644 tests/components/chsc6x/test.esp32-ard.yaml delete mode 100644 tests/components/chsc6x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/climate_ir_lg/test.esp32-ard.yaml delete mode 100644 tests/components/climate_ir_lg/test.esp32-c3-ard.yaml delete mode 100644 tests/components/cm1106/test.esp32-ard.yaml delete mode 100644 tests/components/cm1106/test.esp32-c3-ard.yaml delete mode 100644 tests/components/color/test.esp32-ard.yaml delete mode 100644 tests/components/color/test.esp32-c3-ard.yaml delete mode 100644 tests/components/color_temperature/test.esp32-ard.yaml delete mode 100644 tests/components/color_temperature/test.esp32-c3-ard.yaml delete mode 100644 tests/components/combination/test.esp32-ard.yaml delete mode 100644 tests/components/combination/test.esp32-c3-ard.yaml delete mode 100644 tests/components/coolix/test.esp32-ard.yaml delete mode 100644 tests/components/coolix/test.esp32-c3-ard.yaml delete mode 100644 tests/components/copy/test.esp32-ard.yaml delete mode 100644 tests/components/copy/test.esp32-c3-ard.yaml delete mode 100644 tests/components/cs5460a/test.esp32-ard.yaml delete mode 100644 tests/components/cs5460a/test.esp32-c3-ard.yaml delete mode 100644 tests/components/cse7761/test.esp32-ard.yaml delete mode 100644 tests/components/cse7761/test.esp32-c3-ard.yaml delete mode 100644 tests/components/cse7766/test.esp32-ard.yaml delete mode 100644 tests/components/cse7766/test.esp32-c3-ard.yaml delete mode 100644 tests/components/cst226/test.esp32-ard.yaml delete mode 100644 tests/components/cst226/test.esp32-c3-ard.yaml delete mode 100644 tests/components/cst816/test.esp32-ard.yaml delete mode 100644 tests/components/cst816/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ct_clamp/test.esp32-ard.yaml delete mode 100644 tests/components/ct_clamp/test.esp32-c3-ard.yaml delete mode 100644 tests/components/current_based/test.esp32-ard.yaml delete mode 100644 tests/components/current_based/test.esp32-c3-ard.yaml delete mode 100644 tests/components/cwww/test.esp32-ard.yaml delete mode 100644 tests/components/cwww/test.esp32-c3-ard.yaml delete mode 100644 tests/components/dac7678/test.esp32-ard.yaml delete mode 100644 tests/components/dac7678/test.esp32-c3-ard.yaml delete mode 100644 tests/components/daikin/test.esp32-ard.yaml delete mode 100644 tests/components/daikin_arc/test.esp32-ard.yaml delete mode 100644 tests/components/daikin_brc/test.esp32-ard.yaml delete mode 100644 tests/components/daikin_brc/test.esp32-c3-ard.yaml delete mode 100644 tests/components/dallas_temp/test.esp32-ard.yaml delete mode 100644 tests/components/dallas_temp/test.esp32-c3-ard.yaml delete mode 100644 tests/components/daly_bms/test.esp32-ard.yaml delete mode 100644 tests/components/daly_bms/test.esp32-c3-ard.yaml delete mode 100644 tests/components/deep_sleep/test.esp32-ard.yaml delete mode 100644 tests/components/deep_sleep/test.esp32-c3-ard.yaml delete mode 100644 tests/components/delonghi/test.esp32-ard.yaml delete mode 100644 tests/components/delonghi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/dfplayer/test.esp32-ard.yaml delete mode 100644 tests/components/dfplayer/test.esp32-c3-ard.yaml delete mode 100644 tests/components/dfrobot_sen0395/test.esp32-ard.yaml delete mode 100644 tests/components/dfrobot_sen0395/test.esp32-c3-ard.yaml delete mode 100644 tests/components/dht/test.esp32-ard.yaml delete mode 100644 tests/components/dht/test.esp32-c3-ard.yaml delete mode 100644 tests/components/dht12/test.esp32-ard.yaml delete mode 100644 tests/components/dht12/test.esp32-c3-ard.yaml delete mode 100644 tests/components/display/test.esp32-ard.yaml delete mode 100644 tests/components/dps310/test.esp32-ard.yaml delete mode 100644 tests/components/dps310/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ds1307/test.esp32-ard.yaml delete mode 100644 tests/components/ds1307/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ds2484/test.esp32-ard.yaml delete mode 100644 tests/components/ds2484/test.esp32-c3-ard.yaml delete mode 100644 tests/components/duty_cycle/test.esp32-ard.yaml delete mode 100644 tests/components/duty_cycle/test.esp32-c3-ard.yaml delete mode 100644 tests/components/duty_time/test.esp32-ard.yaml delete mode 100644 tests/components/duty_time/test.esp32-c3-ard.yaml delete mode 100644 tests/components/e131/test.esp32-ard.yaml delete mode 100644 tests/components/e131/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ee895/test.esp32-ard.yaml delete mode 100644 tests/components/ee895/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ektf2232/test.esp32-ard.yaml delete mode 100644 tests/components/ektf2232/test.esp32-c3-ard.yaml delete mode 100644 tests/components/emc2101/test.esp32-ard.yaml delete mode 100644 tests/components/emc2101/test.esp32-c3-ard.yaml delete mode 100644 tests/components/emmeti/test.esp32-ard.yaml delete mode 100644 tests/components/endstop/test.esp32-ard.yaml delete mode 100644 tests/components/endstop/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ens160_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/ens160_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ens160_spi/test.esp32-ard.yaml delete mode 100644 tests/components/ens160_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ens210/test.esp32-ard.yaml delete mode 100644 tests/components/ens210/test.esp32-c3-ard.yaml delete mode 100644 tests/components/es7210/test.esp32-ard.yaml delete mode 100644 tests/components/es7210/test.esp32-c3-ard.yaml delete mode 100644 tests/components/es7243e/test.esp32-ard.yaml delete mode 100644 tests/components/es7243e/test.esp32-c3-ard.yaml delete mode 100644 tests/components/es8156/test.esp32-ard.yaml delete mode 100644 tests/components/es8156/test.esp32-c3-ard.yaml delete mode 100644 tests/components/es8311/test.esp32-ard.yaml delete mode 100644 tests/components/es8311/test.esp32-c3-ard.yaml delete mode 100644 tests/components/es8388/test.esp32-ard.yaml delete mode 100644 tests/components/es8388/test.esp32-c3-ard.yaml delete mode 100644 tests/components/esp32_ble_client/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_ble_client/test.esp32-c3-ard.yaml delete mode 100644 tests/components/esp32_ble_server/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_ble_server/test.esp32-c3-ard.yaml delete mode 100644 tests/components/esp32_camera/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_camera_web_server/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_can/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_can/test.esp32-c3-ard.yaml delete mode 100644 tests/components/esp32_dac/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_improv/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_improv/test.esp32-c3-ard.yaml delete mode 100644 tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml delete mode 100644 tests/components/esp32_touch/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_touch/test.esp32-s2-ard.yaml delete mode 100644 tests/components/esp32_touch/test.esp32-s3-ard.yaml delete mode 100644 tests/components/esphome/test.esp32-ard.yaml delete mode 100644 tests/components/esphome/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ethernet_info/test.esp32-ard.yaml delete mode 100644 tests/components/event/test.esp32-ard.yaml delete mode 100644 tests/components/event/test.esp32-c3-ard.yaml delete mode 100644 tests/components/exposure_notifications/test.esp32-ard.yaml delete mode 100644 tests/components/exposure_notifications/test.esp32-c3-ard.yaml delete mode 100644 tests/components/external_components/test.esp32-ard.yaml delete mode 100644 tests/components/external_components/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ezo/test.esp32-ard.yaml delete mode 100644 tests/components/ezo/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ezo_pmp/test.esp32-ard.yaml delete mode 100644 tests/components/ezo_pmp/test.esp32-c3-ard.yaml delete mode 100644 tests/components/factory_reset/test.esp32-ard.yaml delete mode 100644 tests/components/factory_reset/test.esp32-c3-ard.yaml delete mode 100644 tests/components/feedback/test.esp32-ard.yaml delete mode 100644 tests/components/feedback/test.esp32-c3-ard.yaml delete mode 100644 tests/components/fingerprint_grow/test.esp32-ard.yaml delete mode 100644 tests/components/fingerprint_grow/test.esp32-c3-ard.yaml delete mode 100644 tests/components/font/test.esp32-ard.yaml delete mode 100644 tests/components/font/test.esp32-c3-ard.yaml delete mode 100644 tests/components/fs3000/test.esp32-ard.yaml delete mode 100644 tests/components/fs3000/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ft5x06/test.esp32-ard.yaml delete mode 100644 tests/components/ft5x06/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ft63x6/test.esp32-ard.yaml delete mode 100644 tests/components/ft63x6/test.esp32-c3-ard.yaml delete mode 100644 tests/components/fujitsu_general/test.esp32-ard.yaml delete mode 100644 tests/components/fujitsu_general/test.esp32-c3-ard.yaml delete mode 100644 tests/components/gcja5/test.esp32-ard.yaml delete mode 100644 tests/components/gcja5/test.esp32-c3-ard.yaml delete mode 100644 tests/components/gdk101/test.esp32-ard.yaml delete mode 100644 tests/components/gl_r01_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/gl_r01_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/globals/test.esp32-ard.yaml delete mode 100644 tests/components/globals/test.esp32-c3-ard.yaml delete mode 100644 tests/components/gp2y1010au0f/test.esp32-ard.yaml delete mode 100644 tests/components/gp2y1010au0f/test.esp32-c3-ard.yaml delete mode 100644 tests/components/gp8403/test.esp32-ard.yaml delete mode 100644 tests/components/gp8403/test.esp32-c3-ard.yaml delete mode 100644 tests/components/gpio/test.esp32-ard.yaml delete mode 100644 tests/components/gpio/test.esp32-c3-ard.yaml delete mode 100644 tests/components/gps/test.esp32-ard.yaml delete mode 100644 tests/components/gps/test.esp32-c3-ard.yaml delete mode 100644 tests/components/graph/test.esp32-ard.yaml delete mode 100644 tests/components/graph/test.esp32-c3-ard.yaml delete mode 100644 tests/components/graphical_display_menu/test.esp32-ard.yaml delete mode 100644 tests/components/graphical_display_menu/test.esp32-c3-ard.yaml delete mode 100644 tests/components/gree/test.esp32-ard.yaml delete mode 100644 tests/components/gree/test.esp32-c3-ard.yaml delete mode 100644 tests/components/grove_gas_mc_v2/test.esp32-ard.yaml delete mode 100644 tests/components/grove_gas_mc_v2/test.esp32-c3-ard.yaml delete mode 100644 tests/components/grove_tb6612fng/test.esp32-ard.yaml delete mode 100644 tests/components/grove_tb6612fng/test.esp32-c3-ard.yaml delete mode 100644 tests/components/growatt_solar/test.esp32-ard.yaml delete mode 100644 tests/components/growatt_solar/test.esp32-c3-ard.yaml delete mode 100644 tests/components/gt911/test.esp32-ard.yaml delete mode 100644 tests/components/gt911/test.esp32-c3-ard.yaml delete mode 100644 tests/components/haier/test.esp32-ard.yaml delete mode 100644 tests/components/haier/test.esp32-c3-ard.yaml delete mode 100644 tests/components/havells_solar/test.esp32-ard.yaml delete mode 100644 tests/components/havells_solar/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hbridge/test.esp32-ard.yaml delete mode 100644 tests/components/hbridge/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hdc1080/test.esp32-ard.yaml delete mode 100644 tests/components/hdc1080/test.esp32-c3-ard.yaml delete mode 100644 tests/components/he60r/test.esp32-ard.yaml delete mode 100644 tests/components/he60r/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hitachi_ac344/test.esp32-ard.yaml delete mode 100644 tests/components/hitachi_ac344/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hitachi_ac424/test.esp32-ard.yaml delete mode 100644 tests/components/hitachi_ac424/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hlw8012/test.esp32-ard.yaml delete mode 100644 tests/components/hlw8012/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hm3301/test.esp32-ard.yaml delete mode 100644 tests/components/hm3301/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hmc5883l/test.esp32-ard.yaml delete mode 100644 tests/components/hmc5883l/test.esp32-c3-ard.yaml delete mode 100644 tests/components/homeassistant/test.esp32-ard.yaml delete mode 100644 tests/components/homeassistant/test.esp32-c3-ard.yaml delete mode 100644 tests/components/honeywell_hih_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/honeywell_hih_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/honeywellabp/test.esp32-ard.yaml delete mode 100644 tests/components/honeywellabp/test.esp32-c3-ard.yaml delete mode 100644 tests/components/honeywellabp2_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/honeywellabp2_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hrxl_maxsonar_wr/test.esp32-ard.yaml delete mode 100644 tests/components/hrxl_maxsonar_wr/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hte501/test.esp32-ard.yaml delete mode 100644 tests/components/hte501/test.esp32-c3-ard.yaml delete mode 100644 tests/components/htu21d/test.esp32-ard.yaml delete mode 100644 tests/components/htu21d/test.esp32-c3-ard.yaml delete mode 100644 tests/components/htu31d/test.esp32-ard.yaml delete mode 100644 tests/components/htu31d/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hx711/test.esp32-ard.yaml delete mode 100644 tests/components/hx711/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hydreon_rgxx/test.esp32-ard.yaml delete mode 100644 tests/components/hydreon_rgxx/test.esp32-c3-ard.yaml delete mode 100644 tests/components/hyt271/test.esp32-ard.yaml delete mode 100644 tests/components/hyt271/test.esp32-c3-ard.yaml delete mode 100644 tests/components/i2c_device/test.esp32-ard.yaml delete mode 100644 tests/components/i2c_device/test.esp32-c3-ard.yaml delete mode 100644 tests/components/i2s_audio/test.esp32-ard.yaml delete mode 100644 tests/components/i2s_audio/test.esp32-c3-ard.yaml delete mode 100644 tests/components/iaqcore/test.esp32-ard.yaml delete mode 100644 tests/components/iaqcore/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ili9xxx/test.esp32-ard.yaml delete mode 100644 tests/components/ili9xxx/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ina219/test.esp32-ard.yaml delete mode 100644 tests/components/ina219/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ina226/test.esp32-ard.yaml delete mode 100644 tests/components/ina226/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ina260/test.esp32-ard.yaml delete mode 100644 tests/components/ina260/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ina2xx_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/ina2xx_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ina2xx_spi/test.esp32-ard.yaml delete mode 100644 tests/components/ina2xx_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ina3221/test.esp32-ard.yaml delete mode 100644 tests/components/ina3221/test.esp32-c3-ard.yaml delete mode 100644 tests/components/inkbird_ibsth1_mini/test.esp32-ard.yaml delete mode 100644 tests/components/inkbird_ibsth1_mini/test.esp32-c3-ard.yaml delete mode 100644 tests/components/inkplate/test.esp32-ard.yaml delete mode 100644 tests/components/integration/test.esp32-ard.yaml delete mode 100644 tests/components/integration/test.esp32-c3-ard.yaml delete mode 100644 tests/components/internal_temperature/test.esp32-ard.yaml delete mode 100644 tests/components/internal_temperature/test.esp32-c3-ard.yaml delete mode 100644 tests/components/internal_temperature/test.esp32-s2-ard.yaml delete mode 100644 tests/components/internal_temperature/test.esp32-s3-ard.yaml delete mode 100644 tests/components/interval/test.esp32-ard.yaml delete mode 100644 tests/components/interval/test.esp32-c3-ard.yaml delete mode 100644 tests/components/jsn_sr04t/test.esp32-ard.yaml delete mode 100644 tests/components/jsn_sr04t/test.esp32-c3-ard.yaml delete mode 100644 tests/components/kamstrup_kmp/test.esp32-ard.yaml delete mode 100644 tests/components/key_collector/test.esp32-ard.yaml delete mode 100644 tests/components/key_collector/test.esp32-c3-ard.yaml delete mode 100644 tests/components/kmeteriso/test.esp32-ard.yaml delete mode 100644 tests/components/kmeteriso/test.esp32-c3-ard.yaml delete mode 100644 tests/components/kuntze/test.esp32-ard.yaml delete mode 100644 tests/components/kuntze/test.esp32-c3-ard.yaml delete mode 100644 tests/components/lc709203f/test.esp32-ard.yaml delete mode 100644 tests/components/lc709203f/test.esp32-c3-ard.yaml delete mode 100644 tests/components/lcd_gpio/test.esp32-ard.yaml delete mode 100644 tests/components/lcd_gpio/test.esp32-c3-ard.yaml delete mode 100644 tests/components/lcd_menu/test.esp32-ard.yaml delete mode 100644 tests/components/lcd_menu/test.esp32-c3-ard.yaml delete mode 100644 tests/components/lcd_pcf8574/test.esp32-ard.yaml delete mode 100644 tests/components/lcd_pcf8574/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ld2410/test.esp32-ard.yaml delete mode 100644 tests/components/ld2410/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ld2412/test.esp32-ard.yaml delete mode 100644 tests/components/ld2412/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ld2420/test.esp32-ard.yaml delete mode 100644 tests/components/ld2420/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ld2450/test.esp32-ard.yaml delete mode 100644 tests/components/ld2450/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ledc/test.esp32-ard.yaml delete mode 100644 tests/components/ledc/test.esp32-c3-ard.yaml delete mode 100644 tests/components/light/test.esp32-ard.yaml delete mode 100644 tests/components/light/test.esp32-c3-ard.yaml delete mode 100644 tests/components/lilygo_t5_47/test.esp32-ard.yaml delete mode 100644 tests/components/lilygo_t5_47/test.esp32-c3-ard.yaml delete mode 100644 tests/components/lm75b/test.esp32-ard.yaml delete mode 100644 tests/components/lm75b/test.esp32-c3-ard.yaml delete mode 100644 tests/components/lock/test.esp32-ard.yaml delete mode 100644 tests/components/lock/test.esp32-c3-ard.yaml delete mode 100644 tests/components/lps22/test.esp32-ard.yaml delete mode 100644 tests/components/lps22/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ltr390/test.esp32-ard.yaml delete mode 100644 tests/components/ltr390/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ltr501/test.esp32-ard.yaml delete mode 100644 tests/components/ltr501/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ltr_als_ps/test.esp32-ard.yaml delete mode 100644 tests/components/ltr_als_ps/test.esp32-c3-ard.yaml delete mode 100644 tests/components/lvgl/test.esp32-ard.yaml delete mode 100644 tests/components/m5stack_8angle/test.esp32-ard.yaml delete mode 100644 tests/components/m5stack_8angle/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mapping/test.esp32-ard.yaml delete mode 100644 tests/components/mapping/test.esp32-c3-ard.yaml delete mode 100644 tests/components/matrix_keypad/test.esp32-ard.yaml delete mode 100644 tests/components/matrix_keypad/test.esp32-c3-ard.yaml delete mode 100644 tests/components/max17043/test.esp32-ard.yaml delete mode 100644 tests/components/max17043/test.esp32-c3-ard.yaml delete mode 100644 tests/components/max31855/test.esp32-ard.yaml delete mode 100644 tests/components/max31855/test.esp32-c3-ard.yaml delete mode 100644 tests/components/max31856/test.esp32-ard.yaml delete mode 100644 tests/components/max31856/test.esp32-c3-ard.yaml delete mode 100644 tests/components/max31865/test.esp32-ard.yaml delete mode 100644 tests/components/max31865/test.esp32-c3-ard.yaml delete mode 100644 tests/components/max44009/test.esp32-ard.yaml delete mode 100644 tests/components/max44009/test.esp32-c3-ard.yaml delete mode 100644 tests/components/max6675/test.esp32-ard.yaml delete mode 100644 tests/components/max6675/test.esp32-c3-ard.yaml delete mode 100644 tests/components/max6956/test.esp32-ard.yaml delete mode 100644 tests/components/max6956/test.esp32-c3-ard.yaml delete mode 100644 tests/components/max7219/test.esp32-ard.yaml delete mode 100644 tests/components/max7219/test.esp32-c3-ard.yaml delete mode 100644 tests/components/max7219digit/test.esp32-ard.yaml delete mode 100644 tests/components/max7219digit/test.esp32-c3-ard.yaml delete mode 100644 tests/components/max9611/test.esp32-ard.yaml delete mode 100644 tests/components/max9611/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp23008/test.esp32-ard.yaml delete mode 100644 tests/components/mcp23008/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp23016/test.esp32-ard.yaml delete mode 100644 tests/components/mcp23016/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp23017/test.esp32-ard.yaml delete mode 100644 tests/components/mcp23017/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp23s08/test.esp32-ard.yaml delete mode 100644 tests/components/mcp23s08/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp23s17/test.esp32-ard.yaml delete mode 100644 tests/components/mcp23s17/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp2515/test.esp32-ard.yaml delete mode 100644 tests/components/mcp2515/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp3008/test.esp32-ard.yaml delete mode 100644 tests/components/mcp3008/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp3204/test.esp32-ard.yaml delete mode 100644 tests/components/mcp3204/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp4461/test.esp32-ard.yaml delete mode 100644 tests/components/mcp4461/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp4725/test.esp32-ard.yaml delete mode 100644 tests/components/mcp4725/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp4728/test.esp32-ard.yaml delete mode 100644 tests/components/mcp4728/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp47a1/test.esp32-ard.yaml delete mode 100644 tests/components/mcp47a1/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp9600/test.esp32-ard.yaml delete mode 100644 tests/components/mcp9600/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mcp9808/test.esp32-ard.yaml delete mode 100644 tests/components/mcp9808/test.esp32-c3-ard.yaml delete mode 100644 tests/components/media_player/test.esp32-ard.yaml delete mode 100644 tests/components/mhz19/test.esp32-ard.yaml delete mode 100644 tests/components/mhz19/test.esp32-c3-ard.yaml delete mode 100644 tests/components/micronova/test.esp32-ard.yaml delete mode 100644 tests/components/micronova/test.esp32-c3-ard.yaml delete mode 100644 tests/components/microphone/test.esp32-ard.yaml delete mode 100644 tests/components/microphone/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mics_4514/test.esp32-ard.yaml delete mode 100644 tests/components/mics_4514/test.esp32-c3-ard.yaml delete mode 100644 tests/components/midea_ir/test.esp32-ard.yaml delete mode 100644 tests/components/midea_ir/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mipi_spi/test.esp32-ard.yaml delete mode 100644 tests/components/mipi_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mitsubishi/test.esp32-ard.yaml delete mode 100644 tests/components/mitsubishi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mixer/test.esp32-ard.yaml delete mode 100644 tests/components/mixer/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mixer/test.esp32-s3-ard.yaml delete mode 100644 tests/components/mlx90393/test.esp32-ard.yaml delete mode 100644 tests/components/mlx90393/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mlx90393/test.esp32-s3-ard.yaml delete mode 100644 tests/components/mlx90614/test.esp32-ard.yaml delete mode 100644 tests/components/mlx90614/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mmc5603/test.esp32-ard.yaml delete mode 100644 tests/components/mmc5603/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mmc5983/test.esp32-ard.yaml delete mode 100644 tests/components/mmc5983/test.esp32-c3-ard.yaml delete mode 100644 tests/components/modbus/test.esp32-ard.yaml delete mode 100644 tests/components/modbus/test.esp32-c3-ard.yaml delete mode 100644 tests/components/modbus_controller/test.esp32-ard.yaml delete mode 100644 tests/components/modbus_controller/test.esp32-c3-ard.yaml delete mode 100644 tests/components/monochromatic/test.esp32-ard.yaml delete mode 100644 tests/components/monochromatic/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mopeka_ble/test.esp32-ard.yaml delete mode 100644 tests/components/mopeka_ble/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mopeka_pro_check/test.esp32-ard.yaml delete mode 100644 tests/components/mopeka_pro_check/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mopeka_std_check/test.esp32-ard.yaml delete mode 100644 tests/components/mopeka_std_check/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mpl3115a2/test.esp32-ard.yaml delete mode 100644 tests/components/mpl3115a2/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mpr121/test.esp32-ard.yaml delete mode 100644 tests/components/mpr121/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mpu6050/test.esp32-ard.yaml delete mode 100644 tests/components/mpu6050/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mpu6886/test.esp32-ard.yaml delete mode 100644 tests/components/mpu6886/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mqtt/test.esp32-ard.yaml delete mode 100644 tests/components/mqtt/test.esp32-c3-ard.yaml delete mode 100644 tests/components/mqtt_subscribe/test.esp32-ard.yaml delete mode 100644 tests/components/mqtt_subscribe/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ms5611/test.esp32-ard.yaml delete mode 100644 tests/components/ms5611/test.esp32-c3-ard.yaml delete mode 100644 tests/components/msa3xx/test.esp32-ard.yaml delete mode 100644 tests/components/msa3xx/test.esp32-c3-ard.yaml delete mode 100644 tests/components/my9231/test.esp32-ard.yaml delete mode 100644 tests/components/my9231/test.esp32-c3-ard.yaml delete mode 100644 tests/components/nau7802/test.esp32-ard.yaml delete mode 100644 tests/components/nau7802/test.esp32-c3-ard.yaml delete mode 100644 tests/components/noblex/test.esp32-ard.yaml delete mode 100644 tests/components/noblex/test.esp32-c3-ard.yaml delete mode 100644 tests/components/npi19/test.esp32-ard.yaml delete mode 100644 tests/components/npi19/test.esp32-s3-ard.yaml delete mode 100644 tests/components/ntc/test.esp32-ard.yaml delete mode 100644 tests/components/ntc/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ntc/test.esp32-s2-ard.yaml delete mode 100644 tests/components/ntc/test.esp32-s3-ard.yaml delete mode 100644 tests/components/online_image/test.esp32-ard.yaml delete mode 100644 tests/components/opt3001/test.esp32-ard.yaml delete mode 100644 tests/components/opt3001/test.esp32-c3-ard.yaml delete mode 100644 tests/components/output/test.esp32-ard.yaml delete mode 100644 tests/components/output/test.esp32-c3-ard.yaml delete mode 100644 tests/components/packages/test.esp32-ard.yaml delete mode 100644 tests/components/packet_transport/test.esp32-ard.yaml delete mode 100644 tests/components/packet_transport/test.esp32-c3-ard.yaml delete mode 100644 tests/components/partition/test.esp32-ard.yaml delete mode 100644 tests/components/partition/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pca6416a/test.esp32-ard.yaml delete mode 100644 tests/components/pca6416a/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pca9554/test.esp32-ard.yaml delete mode 100644 tests/components/pca9554/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pca9685/test.esp32-ard.yaml delete mode 100644 tests/components/pca9685/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pcd8544/test.esp32-ard.yaml delete mode 100644 tests/components/pcd8544/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pcf85063/test.esp32-ard.yaml delete mode 100644 tests/components/pcf85063/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pcf8563/test.esp32-ard.yaml delete mode 100644 tests/components/pcf8563/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pcf8574/test.esp32-ard.yaml delete mode 100644 tests/components/pcf8574/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pi4ioe5v6408/test.esp32-ard.yaml delete mode 100644 tests/components/pid/test.esp32-ard.yaml delete mode 100644 tests/components/pid/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pipsolar/test.esp32-ard.yaml delete mode 100644 tests/components/pipsolar/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pm1006/test.esp32-ard.yaml delete mode 100644 tests/components/pm1006/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pm2005/test.esp32-ard.yaml delete mode 100644 tests/components/pm2005/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pmsa003i/test.esp32-ard.yaml delete mode 100644 tests/components/pmsa003i/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pmsx003/test.esp32-ard.yaml delete mode 100644 tests/components/pmsx003/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pmwcs3/test.esp32-ard.yaml delete mode 100644 tests/components/pmwcs3/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pn532_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/pn532_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pn532_spi/test.esp32-ard.yaml delete mode 100644 tests/components/pn532_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pn7150_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/pn7150_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pn7160_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/pn7160_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pn7160_spi/test.esp32-ard.yaml delete mode 100644 tests/components/pn7160_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/power_supply/test.esp32-ard.yaml delete mode 100644 tests/components/power_supply/test.esp32-c3-ard.yaml delete mode 100644 tests/components/prometheus/test.esp32-ard.yaml delete mode 100644 tests/components/prometheus/test.esp32-c3-ard.yaml delete mode 100644 tests/components/psram/test.esp32-ard.yaml delete mode 100644 tests/components/psram/test.esp32-s2-ard.yaml delete mode 100644 tests/components/psram/test.esp32-s3-ard.yaml delete mode 100644 tests/components/pulse_counter/test.esp32-ard.yaml delete mode 100644 tests/components/pulse_counter/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pulse_meter/test.esp32-ard.yaml delete mode 100644 tests/components/pulse_meter/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pulse_width/test.esp32-ard.yaml delete mode 100644 tests/components/pulse_width/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pvvx_mithermometer/test.esp32-ard.yaml delete mode 100644 tests/components/pvvx_mithermometer/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pylontech/test.esp32-ard.yaml delete mode 100644 tests/components/pylontech/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pzem004t/test.esp32-ard.yaml delete mode 100644 tests/components/pzem004t/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pzemac/test.esp32-ard.yaml delete mode 100644 tests/components/pzemac/test.esp32-c3-ard.yaml delete mode 100644 tests/components/pzemdc/test.esp32-ard.yaml delete mode 100644 tests/components/pzemdc/test.esp32-c3-ard.yaml delete mode 100644 tests/components/qmc5883l/test.esp32-ard.yaml delete mode 100644 tests/components/qmc5883l/test.esp32-c3-ard.yaml delete mode 100644 tests/components/qmp6988/test.esp32-ard.yaml delete mode 100644 tests/components/qmp6988/test.esp32-c3-ard.yaml delete mode 100644 tests/components/qr_code/test.esp32-ard.yaml delete mode 100644 tests/components/qr_code/test.esp32-c3-ard.yaml delete mode 100644 tests/components/qwiic_pir/test.esp32-ard.yaml delete mode 100644 tests/components/qwiic_pir/test.esp32-c3-ard.yaml delete mode 100644 tests/components/radon_eye_ble/test.esp32-ard.yaml delete mode 100644 tests/components/radon_eye_ble/test.esp32-c3-ard.yaml delete mode 100644 tests/components/radon_eye_rd200/test.esp32-ard.yaml delete mode 100644 tests/components/radon_eye_rd200/test.esp32-c3-ard.yaml delete mode 100644 tests/components/rc522_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/rc522_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/rc522_spi/test.esp32-ard.yaml delete mode 100644 tests/components/rc522_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/rdm6300/test.esp32-ard.yaml delete mode 100644 tests/components/rdm6300/test.esp32-c3-ard.yaml delete mode 100644 tests/components/remote_receiver/test.esp32-ard.yaml delete mode 100644 tests/components/remote_receiver/test.esp32-c3-ard.yaml delete mode 100644 tests/components/remote_transmitter/test.esp32-ard.yaml delete mode 100644 tests/components/remote_transmitter/test.esp32-c3-ard.yaml delete mode 100644 tests/components/resampler/test.esp32-ard.yaml delete mode 100644 tests/components/resampler/test.esp32-c3-ard.yaml delete mode 100644 tests/components/resampler/test.esp32-s3-ard.yaml delete mode 100644 tests/components/resistance/test.esp32-ard.yaml delete mode 100644 tests/components/resistance/test.esp32-c3-ard.yaml delete mode 100644 tests/components/resistance/test.esp32-s2-ard.yaml delete mode 100644 tests/components/resistance/test.esp32-s3-ard.yaml delete mode 100644 tests/components/restart/test.esp32-ard.yaml delete mode 100644 tests/components/restart/test.esp32-c3-ard.yaml delete mode 100644 tests/components/rf_bridge/test.esp32-ard.yaml delete mode 100644 tests/components/rf_bridge/test.esp32-c3-ard.yaml delete mode 100644 tests/components/rgb/test.esp32-ard.yaml delete mode 100644 tests/components/rgb/test.esp32-c3-ard.yaml delete mode 100644 tests/components/rgbct/test.esp32-ard.yaml delete mode 100644 tests/components/rgbct/test.esp32-c3-ard.yaml delete mode 100644 tests/components/rgbw/test.esp32-ard.yaml delete mode 100644 tests/components/rgbw/test.esp32-c3-ard.yaml delete mode 100644 tests/components/rgbww/test.esp32-ard.yaml delete mode 100644 tests/components/rgbww/test.esp32-c3-ard.yaml delete mode 100644 tests/components/rotary_encoder/test.esp32-ard.yaml delete mode 100644 tests/components/rotary_encoder/test.esp32-c3-ard.yaml delete mode 100644 tests/components/rtttl/test.esp32-ard.yaml delete mode 100644 tests/components/rtttl/test.esp32-c3-ard.yaml delete mode 100644 tests/components/runtime_stats/test.esp32-ard.yaml delete mode 100644 tests/components/ruuvi_ble/test.esp32-ard.yaml delete mode 100644 tests/components/ruuvi_ble/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ruuvitag/test.esp32-ard.yaml delete mode 100644 tests/components/ruuvitag/test.esp32-c3-ard.yaml delete mode 100644 tests/components/safe_mode/test-enabled.esp32-ard.yaml delete mode 100644 tests/components/safe_mode/test-enabled.esp32-c3-ard.yaml delete mode 100644 tests/components/scd30/test.esp32-ard.yaml delete mode 100644 tests/components/scd30/test.esp32-c3-ard.yaml delete mode 100644 tests/components/scd4x/test.esp32-ard.yaml delete mode 100644 tests/components/scd4x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/script/test.esp32-ard.yaml delete mode 100644 tests/components/script/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sdm_meter/test.esp32-ard.yaml delete mode 100644 tests/components/sdm_meter/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sdp3x/test.esp32-ard.yaml delete mode 100644 tests/components/sdp3x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sds011/test.esp32-ard.yaml delete mode 100644 tests/components/sds011/test.esp32-c3-ard.yaml delete mode 100644 tests/components/seeed_mr24hpc1/test.esp32-c3-ard.yaml delete mode 100644 tests/components/seeed_mr60bha2/test.esp32-c3-ard.yaml delete mode 100644 tests/components/seeed_mr60fda2/test.esp32-c3-ard.yaml delete mode 100644 tests/components/selec_meter/test.esp32-ard.yaml delete mode 100644 tests/components/selec_meter/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sen0321/test.esp32-ard.yaml delete mode 100644 tests/components/sen0321/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sen21231/test.esp32-ard.yaml delete mode 100644 tests/components/sen21231/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sen5x/test.esp32-ard.yaml delete mode 100644 tests/components/sen5x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/senseair/test.esp32-ard.yaml delete mode 100644 tests/components/senseair/test.esp32-c3-ard.yaml delete mode 100644 tests/components/servo/test.esp32-ard.yaml delete mode 100644 tests/components/servo/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sfa30/test.esp32-ard.yaml delete mode 100644 tests/components/sfa30/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sgp30/test.esp32-ard.yaml delete mode 100644 tests/components/sgp30/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sgp4x/test.esp32-ard.yaml delete mode 100644 tests/components/sgp4x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sht3xd/test.esp32-ard.yaml delete mode 100644 tests/components/sht3xd/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sht4x/test.esp32-ard.yaml delete mode 100644 tests/components/sht4x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/shtcx/test.esp32-ard.yaml delete mode 100644 tests/components/shtcx/test.esp32-c3-ard.yaml delete mode 100644 tests/components/shutdown/test.esp32-ard.yaml delete mode 100644 tests/components/shutdown/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sigma_delta_output/test.esp32-ard.yaml delete mode 100644 tests/components/sigma_delta_output/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sim800l/test.esp32-ard.yaml delete mode 100644 tests/components/sim800l/test.esp32-c3-ard.yaml delete mode 100644 tests/components/slow_pwm/test.esp32-ard.yaml delete mode 100644 tests/components/slow_pwm/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sm16716/test.esp32-ard.yaml delete mode 100644 tests/components/sm16716/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sm2135/test.esp32-ard.yaml delete mode 100644 tests/components/sm2135/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sm2235/test.esp32-ard.yaml delete mode 100644 tests/components/sm2235/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sm2335/test.esp32-ard.yaml delete mode 100644 tests/components/sm2335/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sm300d2/test.esp32-ard.yaml delete mode 100644 tests/components/sm300d2/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sml/test.esp32-ard.yaml delete mode 100644 tests/components/sml/test.esp32-c3-ard.yaml delete mode 100644 tests/components/smt100/test.esp32-ard.yaml delete mode 100644 tests/components/smt100/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sn74hc165/test.esp32-ard.yaml delete mode 100644 tests/components/sn74hc165/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sn74hc595/test.esp32-ard.yaml delete mode 100644 tests/components/sn74hc595/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sntp/test.esp32-ard.yaml delete mode 100644 tests/components/sntp/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sonoff_d1/test.esp32-ard.yaml delete mode 100644 tests/components/sonoff_d1/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sound_level/test.esp32-ard.yaml delete mode 100644 tests/components/sound_level/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sound_level/test.esp32-s3-ard.yaml delete mode 100644 tests/components/speed/test.esp32-ard.yaml delete mode 100644 tests/components/speed/test.esp32-c3-ard.yaml delete mode 100644 tests/components/spi_device/test.esp32-ard.yaml delete mode 100644 tests/components/spi_device/test.esp32-c3-ard.yaml delete mode 100644 tests/components/spi_led_strip/test.esp32-ard.yaml delete mode 100644 tests/components/spi_led_strip/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sprinkler/test.esp32-ard.yaml delete mode 100644 tests/components/sprinkler/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sps30/test.esp32-ard.yaml delete mode 100644 tests/components/sps30/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ssd1306_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/ssd1306_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ssd1306_spi/test.esp32-ard.yaml delete mode 100644 tests/components/ssd1306_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ssd1322_spi/test.esp32-ard.yaml delete mode 100644 tests/components/ssd1322_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ssd1325_spi/test.esp32-ard.yaml delete mode 100644 tests/components/ssd1325_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ssd1327_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/ssd1327_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ssd1327_spi/test.esp32-ard.yaml delete mode 100644 tests/components/ssd1327_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ssd1331_spi/test.esp32-ard.yaml delete mode 100644 tests/components/ssd1331_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ssd1351_spi/test.esp32-ard.yaml delete mode 100644 tests/components/ssd1351_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/st7567_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/st7567_i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/st7567_spi/test.esp32-ard.yaml delete mode 100644 tests/components/st7567_spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/st7735/test.esp32-ard.yaml delete mode 100644 tests/components/st7735/test.esp32-c3-ard.yaml delete mode 100644 tests/components/st7789v/test.esp32-ard.yaml delete mode 100644 tests/components/st7789v/test.esp32-c3-ard.yaml delete mode 100644 tests/components/st7920/test.esp32-ard.yaml delete mode 100644 tests/components/st7920/test.esp32-c3-ard.yaml delete mode 100644 tests/components/statsD/test.esp32-ard.yaml delete mode 100644 tests/components/statsD/test.esp32-c3-ard.yaml delete mode 100644 tests/components/status/test.esp32-ard.yaml delete mode 100644 tests/components/status/test.esp32-c3-ard.yaml delete mode 100644 tests/components/status_led/test.esp32-ard.yaml delete mode 100644 tests/components/status_led/test.esp32-c3-ard.yaml delete mode 100644 tests/components/stepper/test.esp32-ard.yaml delete mode 100644 tests/components/stepper/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sts3x/test.esp32-ard.yaml delete mode 100644 tests/components/sts3x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sun/test.esp32-ard.yaml delete mode 100644 tests/components/sun/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sun_gtil2/test.esp32-ard.yaml delete mode 100644 tests/components/sun_gtil2/test.esp32-c3-ard.yaml delete mode 100644 tests/components/switch/test.esp32-ard.yaml delete mode 100644 tests/components/switch/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sx126x/test.esp32-ard.yaml delete mode 100644 tests/components/sx126x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sx127x/test.esp32-ard.yaml delete mode 100644 tests/components/sx127x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/sx1509/test.esp32-ard.yaml delete mode 100644 tests/components/sx1509/test.esp32-c3-ard.yaml delete mode 100644 tests/components/syslog/test.esp32-ard.yaml delete mode 100644 tests/components/syslog/test.esp32-c3-ard.yaml delete mode 100644 tests/components/t6615/test.esp32-ard.yaml delete mode 100644 tests/components/t6615/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tc74/test.esp32-ard.yaml delete mode 100644 tests/components/tc74/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tca9548a/test.esp32-ard.yaml delete mode 100644 tests/components/tca9548a/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tca9555/test.esp32-ard.yaml delete mode 100644 tests/components/tca9555/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tcl112/test.esp32-ard.yaml delete mode 100644 tests/components/tcl112/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tcs34725/test.esp32-ard.yaml delete mode 100644 tests/components/tcs34725/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tee501/test.esp32-ard.yaml delete mode 100644 tests/components/tee501/test.esp32-c3-ard.yaml delete mode 100644 tests/components/teleinfo/test.esp32-ard.yaml delete mode 100644 tests/components/teleinfo/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tem3200/test.esp32-ard.yaml delete mode 100644 tests/components/tem3200/test.esp32-s3-ard.yaml delete mode 100644 tests/components/template/test.esp32-ard.yaml delete mode 100644 tests/components/template/test.esp32-c3-ard.yaml delete mode 100644 tests/components/thermostat/test.esp32-ard.yaml delete mode 100644 tests/components/thermostat/test.esp32-c3-ard.yaml delete mode 100644 tests/components/time_based/test.esp32-ard.yaml delete mode 100644 tests/components/time_based/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tlc59208f/test.esp32-ard.yaml delete mode 100644 tests/components/tlc59208f/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tlc5947/test.esp32-ard.yaml delete mode 100644 tests/components/tlc5947/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tlc5971/test.esp32-ard.yaml delete mode 100644 tests/components/tlc5971/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tlc5971/test.esp32-s2-ard.yaml delete mode 100644 tests/components/tm1621/test.esp32-ard.yaml delete mode 100644 tests/components/tm1621/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tm1637/test.esp32-ard.yaml delete mode 100644 tests/components/tm1637/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tm1638/test.esp32-ard.yaml delete mode 100644 tests/components/tm1638/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tm1651/test.esp32-ard.yaml delete mode 100644 tests/components/tm1651/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tmp102/test.esp32-ard.yaml delete mode 100644 tests/components/tmp102/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tmp1075/test.esp32-ard.yaml delete mode 100644 tests/components/tmp1075/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tmp117/test.esp32-ard.yaml delete mode 100644 tests/components/tmp117/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tof10120/test.esp32-ard.yaml delete mode 100644 tests/components/tof10120/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tormatic/test.esp32-ard.yaml delete mode 100644 tests/components/tormatic/test.esp32-c3-ard.yaml delete mode 100644 tests/components/toshiba/test.esp32-ard.yaml delete mode 100644 tests/components/toshiba/test.esp32-c3-ard.yaml delete mode 100644 tests/components/total_daily_energy/test.esp32-ard.yaml delete mode 100644 tests/components/total_daily_energy/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tsl2561/test.esp32-ard.yaml delete mode 100644 tests/components/tsl2561/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tsl2591/test.esp32-ard.yaml delete mode 100644 tests/components/tsl2591/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tt21100/test.esp32-ard.yaml delete mode 100644 tests/components/tt21100/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ttp229_bsf/test.esp32-ard.yaml delete mode 100644 tests/components/ttp229_bsf/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ttp229_lsf/test.esp32-ard.yaml delete mode 100644 tests/components/ttp229_lsf/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tuya/test.esp32-ard.yaml delete mode 100644 tests/components/tuya/test.esp32-c3-ard.yaml delete mode 100644 tests/components/tx20/test.esp32-ard.yaml delete mode 100644 tests/components/tx20/test.esp32-c3-ard.yaml delete mode 100644 tests/components/uart/test-uart_max_with_usb_cdc.esp32-c3-ard.yaml delete mode 100644 tests/components/uart/test-uart_max_with_usb_cdc.esp32-s2-ard.yaml delete mode 100644 tests/components/uart/test-uart_max_with_usb_cdc.esp32-s3-ard.yaml delete mode 100644 tests/components/uart/test.esp32-ard.yaml delete mode 100644 tests/components/uart/test.esp32-c3-ard.yaml delete mode 100644 tests/components/udp/test.esp32-ard.yaml delete mode 100644 tests/components/udp/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ufire_ec/test.esp32-ard.yaml delete mode 100644 tests/components/ufire_ec/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ufire_ise/test.esp32-ard.yaml delete mode 100644 tests/components/ufire_ise/test.esp32-c3-ard.yaml delete mode 100644 tests/components/uln2003/test.esp32-ard.yaml delete mode 100644 tests/components/uln2003/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ultrasonic/test.esp32-ard.yaml delete mode 100644 tests/components/ultrasonic/test.esp32-c3-ard.yaml delete mode 100644 tests/components/update/test.esp32-ard.yaml delete mode 100644 tests/components/uponor_smatrix/test.esp32-ard.yaml delete mode 100644 tests/components/uponor_smatrix/test.esp32-c3-ard.yaml delete mode 100644 tests/components/uptime/test.esp32-ard.yaml delete mode 100644 tests/components/uptime/test.esp32-c3-ard.yaml delete mode 100644 tests/components/vbus/test.esp32-ard.yaml delete mode 100644 tests/components/vbus/test.esp32-c3-ard.yaml delete mode 100644 tests/components/veml3235/test.esp32-ard.yaml delete mode 100644 tests/components/veml3235/test.esp32-c3-ard.yaml delete mode 100644 tests/components/veml7700/test.esp32-ard.yaml delete mode 100644 tests/components/veml7700/test.esp32-c3-ard.yaml delete mode 100644 tests/components/version/test.esp32-ard.yaml delete mode 100644 tests/components/version/test.esp32-c3-ard.yaml delete mode 100644 tests/components/vl53l0x/test.esp32-ard.yaml delete mode 100644 tests/components/vl53l0x/test.esp32-c3-ard.yaml delete mode 100644 tests/components/voice_assistant/test.esp32-ard.yaml delete mode 100644 tests/components/voice_assistant/test.esp32-c3-ard.yaml delete mode 100644 tests/components/wake_on_lan/test.esp32-ard.yaml delete mode 100644 tests/components/wake_on_lan/test.esp32-c3-ard.yaml delete mode 100644 tests/components/waveshare_epaper/test.esp32-ard.yaml delete mode 100644 tests/components/waveshare_epaper/test.esp32-c3-ard.yaml delete mode 100644 tests/components/whirlpool/test.esp32-ard.yaml delete mode 100644 tests/components/whirlpool/test.esp32-c3-ard.yaml delete mode 100644 tests/components/whynter/test.esp32-ard.yaml delete mode 100644 tests/components/whynter/test.esp32-c3-ard.yaml delete mode 100644 tests/components/wiegand/test.esp32-ard.yaml delete mode 100644 tests/components/wiegand/test.esp32-c3-ard.yaml delete mode 100644 tests/components/wifi/test-eap.esp32-ard.yaml delete mode 100644 tests/components/wifi/test.esp32-ard.yaml delete mode 100644 tests/components/wifi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/wifi_info/test.esp32-ard.yaml delete mode 100644 tests/components/wifi_info/test.esp32-c3-ard.yaml delete mode 100644 tests/components/wifi_signal/test.esp32-ard.yaml delete mode 100644 tests/components/wifi_signal/test.esp32-c3-ard.yaml delete mode 100644 tests/components/wireguard/test.esp32-ard.yaml delete mode 100644 tests/components/wireguard/test.esp32-c3-ard.yaml delete mode 100644 tests/components/wk2132_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/wk2132_i2c/test.esp32-s3-ard.yaml delete mode 100644 tests/components/wk2132_spi/test.esp32-ard.yaml delete mode 100644 tests/components/wk2132_spi/test.esp32-s3-ard.yaml delete mode 100644 tests/components/wk2168_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/wk2168_i2c/test.esp32-s3-ard.yaml delete mode 100644 tests/components/wk2168_spi/test.esp32-ard.yaml delete mode 100644 tests/components/wk2168_spi/test.esp32-s3-ard.yaml delete mode 100644 tests/components/wk2204_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/wk2204_i2c/test.esp32-s3-ard.yaml delete mode 100644 tests/components/wk2204_spi/test.esp32-ard.yaml delete mode 100644 tests/components/wk2204_spi/test.esp32-s3-ard.yaml delete mode 100644 tests/components/wk2212_i2c/test.esp32-ard.yaml delete mode 100644 tests/components/wk2212_i2c/test.esp32-s3-ard.yaml delete mode 100644 tests/components/wk2212_spi/test.esp32-ard.yaml delete mode 100644 tests/components/wk2212_spi/test.esp32-s3-ard.yaml delete mode 100644 tests/components/wl_134/test.esp32-ard.yaml delete mode 100644 tests/components/wl_134/test.esp32-c3-ard.yaml delete mode 100644 tests/components/wts01/test.esp32-ard.yaml delete mode 100644 tests/components/wts01/test.esp32-c3-ard.yaml delete mode 100644 tests/components/x9c/test.esp32-ard.yaml delete mode 100644 tests/components/x9c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xgzp68xx/test.esp32-ard.yaml delete mode 100644 tests/components/xgzp68xx/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_ble/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_ble/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_cgd1/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_cgd1/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_cgdk2/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_cgdk2/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_cgg1/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_cgg1/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_cgpr1/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_cgpr1/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_gcls002/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_gcls002/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_hhccjcy01/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_hhccjcy01/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_hhccpot002/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_hhccpot002/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_jqjcy01ym/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_jqjcy01ym/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_lywsd02/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_lywsd02/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_lywsd02mmc/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_lywsd02mmc/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_lywsd03mmc/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_lywsd03mmc/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_lywsdcgq/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_lywsdcgq/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_mhoc303/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_mhoc303/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_mhoc401/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_mhoc401/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_miscale/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_miscale/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_mjyd02yla/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_mjyd02yla/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_mue4094rt/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_mue4094rt/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_rtcgq02lm/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_rtcgq02lm/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_wx08zm/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_wx08zm/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_xmwsdj04mmc/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xl9535/test.esp32-ard.yaml delete mode 100644 tests/components/xl9535/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xpt2046/test.esp32-ard.yaml delete mode 100644 tests/components/xpt2046/test.esp32-c3-ard.yaml delete mode 100644 tests/components/yashima/test.esp32-ard.yaml delete mode 100644 tests/components/yashima/test.esp32-c3-ard.yaml delete mode 100644 tests/components/zhlt01/test.esp32-ard.yaml delete mode 100644 tests/components/zhlt01/test.esp32-c3-ard.yaml delete mode 100644 tests/components/zio_ultrasonic/test.esp32-ard.yaml delete mode 100644 tests/components/zio_ultrasonic/test.esp32-c3-ard.yaml delete mode 100644 tests/components/zwave_proxy/test.esp32-ard.yaml delete mode 100644 tests/components/zwave_proxy/test.esp32-c3-ard.yaml delete mode 100644 tests/components/zyaura/test.esp32-ard.yaml delete mode 100644 tests/components/zyaura/test.esp32-c3-ard.yaml diff --git a/tests/components/a01nyub/test.esp32-ard.yaml b/tests/components/a01nyub/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/a01nyub/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/a01nyub/test.esp32-c3-ard.yaml b/tests/components/a01nyub/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/a01nyub/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/a02yyuw/test.esp32-ard.yaml b/tests/components/a02yyuw/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/a02yyuw/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/a02yyuw/test.esp32-c3-ard.yaml b/tests/components/a02yyuw/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/a02yyuw/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/a4988/test.esp32-ard.yaml b/tests/components/a4988/test.esp32-ard.yaml deleted file mode 100644 index 1ca8c0c084..0000000000 --- a/tests/components/a4988/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - step_pin: GPIO22 - dir_pin: GPIO23 - sleep_pin: GPIO25 - -<<: !include common.yaml diff --git a/tests/components/a4988/test.esp32-c3-ard.yaml b/tests/components/a4988/test.esp32-c3-ard.yaml deleted file mode 100644 index 25caba75b5..0000000000 --- a/tests/components/a4988/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - step_pin: GPIO2 - dir_pin: GPIO3 - sleep_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/absolute_humidity/test.esp32-ard.yaml b/tests/components/absolute_humidity/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/absolute_humidity/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/absolute_humidity/test.esp32-c3-ard.yaml b/tests/components/absolute_humidity/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/absolute_humidity/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/adc/test.esp32-ard.yaml b/tests/components/adc/test.esp32-ard.yaml deleted file mode 100644 index e6a1fd3bd9..0000000000 --- a/tests/components/adc/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -packages: - base: !include common.yaml - -sensor: - - id: !extend my_sensor - pin: A0 diff --git a/tests/components/adc/test.esp32-c3-ard.yaml b/tests/components/adc/test.esp32-c3-ard.yaml deleted file mode 100644 index ea3b00a85f..0000000000 --- a/tests/components/adc/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -packages: - base: !include common.yaml - -sensor: - - id: !extend my_sensor - pin: 4 diff --git a/tests/components/adc/test.esp32-s2-ard.yaml b/tests/components/adc/test.esp32-s2-ard.yaml deleted file mode 100644 index bbd91c5e5a..0000000000 --- a/tests/components/adc/test.esp32-s2-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -packages: - base: !include common.yaml - -sensor: - - id: !extend my_sensor - pin: 1 diff --git a/tests/components/adc/test.esp32-s3-ard.yaml b/tests/components/adc/test.esp32-s3-ard.yaml deleted file mode 100644 index bbd91c5e5a..0000000000 --- a/tests/components/adc/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -packages: - base: !include common.yaml - -sensor: - - id: !extend my_sensor - pin: 1 diff --git a/tests/components/adc128s102/test.esp32-ard.yaml b/tests/components/adc128s102/test.esp32-ard.yaml deleted file mode 100644 index aba72f0614..0000000000 --- a/tests/components/adc128s102/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO12 - -<<: !include common.yaml diff --git a/tests/components/adc128s102/test.esp32-c3-ard.yaml b/tests/components/adc128s102/test.esp32-c3-ard.yaml deleted file mode 100644 index 24da4b5452..0000000000 --- a/tests/components/adc128s102/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/addressable_light/esp32_rmt_led_strip.esp32-ard.yaml b/tests/components/addressable_light/esp32_rmt_led_strip.esp32-ard.yaml deleted file mode 100644 index d93c554dae..0000000000 --- a/tests/components/addressable_light/esp32_rmt_led_strip.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common-ard-esp32_rmt_led_strip.yaml diff --git a/tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-ard.yaml b/tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-ard.yaml deleted file mode 100644 index d93c554dae..0000000000 --- a/tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common-ard-esp32_rmt_led_strip.yaml diff --git a/tests/components/addressable_light/fastled_clockless.esp32-ard.yaml b/tests/components/addressable_light/fastled_clockless.esp32-ard.yaml deleted file mode 100644 index 78eb5d7fdb..0000000000 --- a/tests/components/addressable_light/fastled_clockless.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common-ard-fastled.yaml diff --git a/tests/components/ade7880/test.esp32-ard.yaml b/tests/components/ade7880/test.esp32-ard.yaml deleted file mode 100644 index 685b49ff32..0000000000 --- a/tests/components/ade7880/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - irq0_pin: GPIO13 - irq1_pin: GPIO15 - reset_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/ade7880/test.esp32-c3-ard.yaml b/tests/components/ade7880/test.esp32-c3-ard.yaml deleted file mode 100644 index 87db3e9427..0000000000 --- a/tests/components/ade7880/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - irq0_pin: GPIO6 - irq1_pin: GPIO7 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.esp32-ard.yaml b/tests/components/ade7953_i2c/test.esp32-ard.yaml deleted file mode 100644 index 2c57d412f6..0000000000 --- a/tests/components/ade7953_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - irq_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.esp32-c3-ard.yaml b/tests/components/ade7953_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index 799acabd5a..0000000000 --- a/tests/components/ade7953_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - irq_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.esp32-ard.yaml b/tests/components/ade7953_spi/test.esp32-ard.yaml deleted file mode 100644 index e00f522dd4..0000000000 --- a/tests/components/ade7953_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - irq_pin: GPIO13 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.esp32-c3-ard.yaml b/tests/components/ade7953_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index fcf35f528e..0000000000 --- a/tests/components/ade7953_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - irq_pin: GPIO9 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/ads1115/test.esp32-ard.yaml b/tests/components/ads1115/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ads1115/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ads1115/test.esp32-c3-ard.yaml b/tests/components/ads1115/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ads1115/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ags10/test.esp32-ard.yaml b/tests/components/ags10/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ags10/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ags10/test.esp32-c3-ard.yaml b/tests/components/ags10/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ags10/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/aht10/test.esp32-ard.yaml b/tests/components/aht10/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/aht10/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/aht10/test.esp32-c3-ard.yaml b/tests/components/aht10/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/aht10/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/aic3204/test.esp32-ard.yaml b/tests/components/aic3204/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/aic3204/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/aic3204/test.esp32-c3-ard.yaml b/tests/components/aic3204/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/aic3204/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/airthings_wave_mini/test.esp32-ard.yaml b/tests/components/airthings_wave_mini/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/airthings_wave_mini/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/airthings_wave_mini/test.esp32-c3-ard.yaml b/tests/components/airthings_wave_mini/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/airthings_wave_mini/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/airthings_wave_plus/test.esp32-ard.yaml b/tests/components/airthings_wave_plus/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/airthings_wave_plus/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/airthings_wave_plus/test.esp32-c3-ard.yaml b/tests/components/airthings_wave_plus/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/airthings_wave_plus/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/alarm_control_panel/test.esp32-ard.yaml b/tests/components/alarm_control_panel/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/alarm_control_panel/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/alarm_control_panel/test.esp32-c3-ard.yaml b/tests/components/alarm_control_panel/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/alarm_control_panel/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/alpha3/test.esp32-ard.yaml b/tests/components/alpha3/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/alpha3/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/alpha3/test.esp32-c3-ard.yaml b/tests/components/alpha3/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/alpha3/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/am2315c/test.esp32-ard.yaml b/tests/components/am2315c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/am2315c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/am2315c/test.esp32-c3-ard.yaml b/tests/components/am2315c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/am2315c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/am2320/test.esp32-ard.yaml b/tests/components/am2320/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/am2320/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/am2320/test.esp32-c3-ard.yaml b/tests/components/am2320/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/am2320/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/am43/test.esp32-ard.yaml b/tests/components/am43/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/am43/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/am43/test.esp32-c3-ard.yaml b/tests/components/am43/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/am43/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/analog_threshold/test.esp32-ard.yaml b/tests/components/analog_threshold/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/analog_threshold/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/analog_threshold/test.esp32-c3-ard.yaml b/tests/components/analog_threshold/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/analog_threshold/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/animation/test.esp32-ard.yaml b/tests/components/animation/test.esp32-ard.yaml deleted file mode 100644 index 7d9fe45bff..0000000000 --- a/tests/components/animation/test.esp32-ard.yaml +++ /dev/null @@ -1,17 +0,0 @@ -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 - -display: - - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 12 - dc_pin: 13 - reset_pin: 21 - invert_colors: false - -packages: - animation: !include common.yaml diff --git a/tests/components/animation/test.esp32-c3-ard.yaml b/tests/components/animation/test.esp32-c3-ard.yaml deleted file mode 100644 index 18aa2a5b06..0000000000 --- a/tests/components/animation/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,17 +0,0 @@ -spi: - - id: spi_main_lcd - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 - -display: - - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - invert_colors: false - -packages: - animation: !include common.yaml diff --git a/tests/components/anova/test.esp32-ard.yaml b/tests/components/anova/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/anova/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/anova/test.esp32-c3-ard.yaml b/tests/components/anova/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/anova/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/apds9306/test.esp32-ard.yaml b/tests/components/apds9306/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/apds9306/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/apds9306/test.esp32-c3-ard.yaml b/tests/components/apds9306/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/apds9306/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/apds9960/test.esp32-ard.yaml b/tests/components/apds9960/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/apds9960/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/apds9960/test.esp32-c3-ard.yaml b/tests/components/apds9960/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/apds9960/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/api/test.esp32-ard.yaml b/tests/components/api/test.esp32-ard.yaml deleted file mode 100644 index 46c01d926f..0000000000 --- a/tests/components/api/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -<<: !include common.yaml - -wifi: - ssid: MySSID - password: password1 diff --git a/tests/components/api/test.esp32-c3-ard.yaml b/tests/components/api/test.esp32-c3-ard.yaml deleted file mode 100644 index 46c01d926f..0000000000 --- a/tests/components/api/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -<<: !include common.yaml - -wifi: - ssid: MySSID - password: password1 diff --git a/tests/components/as3935_i2c/test.esp32-ard.yaml b/tests/components/as3935_i2c/test.esp32-ard.yaml deleted file mode 100644 index 52d5a045cb..0000000000 --- a/tests/components/as3935_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - irq_pin: GPIO15 - -packages: - as3935: !include common.yaml - -# Trigger issue: https://github.com/esphome/issues/issues/6990 -# Compile with no binary sensor results in error -binary_sensor: !remove diff --git a/tests/components/as3935_i2c/test.esp32-c3-ard.yaml b/tests/components/as3935_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index 799acabd5a..0000000000 --- a/tests/components/as3935_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - irq_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/as3935_spi/test.esp32-ard.yaml b/tests/components/as3935_spi/test.esp32-ard.yaml deleted file mode 100644 index e00f522dd4..0000000000 --- a/tests/components/as3935_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - irq_pin: GPIO13 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/as3935_spi/test.esp32-c3-ard.yaml b/tests/components/as3935_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index fcf35f528e..0000000000 --- a/tests/components/as3935_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - irq_pin: GPIO9 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/as5600/test.esp32-ard.yaml b/tests/components/as5600/test.esp32-ard.yaml deleted file mode 100644 index fa08763501..0000000000 --- a/tests/components/as5600/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - dir_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/as5600/test.esp32-c3-ard.yaml b/tests/components/as5600/test.esp32-c3-ard.yaml deleted file mode 100644 index a0623c91e5..0000000000 --- a/tests/components/as5600/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - dir_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/as7341/test.esp32-ard.yaml b/tests/components/as7341/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/as7341/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/as7341/test.esp32-c3-ard.yaml b/tests/components/as7341/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/as7341/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/at581x/test.esp32-ard.yaml b/tests/components/at581x/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/at581x/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/at581x/test.esp32-c3-ard.yaml b/tests/components/at581x/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/at581x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/atc_mithermometer/test.esp32-ard.yaml b/tests/components/atc_mithermometer/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/atc_mithermometer/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/atc_mithermometer/test.esp32-c3-ard.yaml b/tests/components/atc_mithermometer/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/atc_mithermometer/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/atm90e26/test.esp32-ard.yaml b/tests/components/atm90e26/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/atm90e26/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/atm90e26/test.esp32-c3-ard.yaml b/tests/components/atm90e26/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/atm90e26/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/atm90e32/test.esp32-ard.yaml b/tests/components/atm90e32/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/atm90e32/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/atm90e32/test.esp32-c3-ard.yaml b/tests/components/atm90e32/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/atm90e32/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/axs15231/test.esp32-ard.yaml b/tests/components/axs15231/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/axs15231/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/axs15231/test.esp32-c3-ard.yaml b/tests/components/axs15231/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/axs15231/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/b_parasite/test.esp32-ard.yaml b/tests/components/b_parasite/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/b_parasite/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/b_parasite/test.esp32-c3-ard.yaml b/tests/components/b_parasite/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/b_parasite/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ballu/test.esp32-ard.yaml b/tests/components/ballu/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/ballu/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/bang_bang/test.esp32-ard.yaml b/tests/components/bang_bang/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/bang_bang/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/bang_bang/test.esp32-c3-ard.yaml b/tests/components/bang_bang/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/bang_bang/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/bedjet/test.esp32-ard.yaml b/tests/components/bedjet/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/bedjet/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/bedjet/test.esp32-c3-ard.yaml b/tests/components/bedjet/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/bedjet/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/bh1750/test.esp32-ard.yaml b/tests/components/bh1750/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/bh1750/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/bh1750/test.esp32-c3-ard.yaml b/tests/components/bh1750/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/bh1750/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/binary_sensor/test.esp32-ard.yaml b/tests/components/binary_sensor/test.esp32-ard.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/binary_sensor/test.esp32-ard.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/binary_sensor/test.esp32-c3-ard.yaml b/tests/components/binary_sensor/test.esp32-c3-ard.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/binary_sensor/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/binary_sensor_map/test.esp32-ard.yaml b/tests/components/binary_sensor_map/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/binary_sensor_map/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/binary_sensor_map/test.esp32-c3-ard.yaml b/tests/components/binary_sensor_map/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/binary_sensor_map/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/bl0906/test.esp32-ard.yaml b/tests/components/bl0906/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/bl0906/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/bl0906/test.esp32-c3-ard.yaml b/tests/components/bl0906/test.esp32-c3-ard.yaml deleted file mode 100644 index c79d14c740..0000000000 --- a/tests/components/bl0906/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/bl0939/test.esp32-ard.yaml b/tests/components/bl0939/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/bl0939/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/bl0939/test.esp32-c3-ard.yaml b/tests/components/bl0939/test.esp32-c3-ard.yaml deleted file mode 100644 index c79d14c740..0000000000 --- a/tests/components/bl0939/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/bl0940/test.esp32-ard.yaml b/tests/components/bl0940/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/bl0940/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/bl0940/test.esp32-c3-ard.yaml b/tests/components/bl0940/test.esp32-c3-ard.yaml deleted file mode 100644 index c79d14c740..0000000000 --- a/tests/components/bl0940/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/bl0942/test.esp32-ard.yaml b/tests/components/bl0942/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/bl0942/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/bl0942/test.esp32-c3-ard.yaml b/tests/components/bl0942/test.esp32-c3-ard.yaml deleted file mode 100644 index c79d14c740..0000000000 --- a/tests/components/bl0942/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/ble_client/test.esp32-ard.yaml b/tests/components/ble_client/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ble_client/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ble_client/test.esp32-c3-ard.yaml b/tests/components/ble_client/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ble_client/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ble_presence/test.esp32-ard.yaml b/tests/components/ble_presence/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ble_presence/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ble_presence/test.esp32-c3-ard.yaml b/tests/components/ble_presence/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ble_presence/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ble_rssi/test.esp32-ard.yaml b/tests/components/ble_rssi/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ble_rssi/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ble_rssi/test.esp32-c3-ard.yaml b/tests/components/ble_rssi/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ble_rssi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ble_scanner/test.esp32-ard.yaml b/tests/components/ble_scanner/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ble_scanner/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ble_scanner/test.esp32-c3-ard.yaml b/tests/components/ble_scanner/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ble_scanner/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/bluetooth_proxy/test.esp32-s3-ard.yaml b/tests/components/bluetooth_proxy/test.esp32-s3-ard.yaml deleted file mode 100644 index bf01b65b6f..0000000000 --- a/tests/components/bluetooth_proxy/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -<<: !include common.yaml - -esp32_ble_tracker: - max_connections: 3 - -bluetooth_proxy: - active: true - connection_slots: 2 diff --git a/tests/components/bme280_i2c/test.esp32-ard.yaml b/tests/components/bme280_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/bme280_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/bme280_i2c/test.esp32-c3-ard.yaml b/tests/components/bme280_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/bme280_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/bme280_spi/test.esp32-ard.yaml b/tests/components/bme280_spi/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/bme280_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/bme280_spi/test.esp32-c3-ard.yaml b/tests/components/bme280_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/bme280_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/bme680/test.esp32-ard.yaml b/tests/components/bme680/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/bme680/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/bme680/test.esp32-c3-ard.yaml b/tests/components/bme680/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/bme680/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/bme68x_bsec2_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-c3-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index 84a9dd4bb4..0000000000 --- a/tests/components/bme68x_bsec2_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO6 - sda_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-s2-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-s2-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/bme68x_bsec2_i2c/test.esp32-s2-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-s3-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-s3-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/bme68x_bsec2_i2c/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/bmi160/test.esp32-ard.yaml b/tests/components/bmi160/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/bmi160/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/bmi160/test.esp32-c3-ard.yaml b/tests/components/bmi160/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/bmi160/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/bmp085/test.esp32-ard.yaml b/tests/components/bmp085/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/bmp085/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/bmp085/test.esp32-c3-ard.yaml b/tests/components/bmp085/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/bmp085/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.esp32-ard.yaml b/tests/components/bmp280_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/bmp280_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.esp32-c3-ard.yaml b/tests/components/bmp280_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/bmp280_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.esp32-ard.yaml b/tests/components/bmp280_spi/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/bmp280_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.esp32-c3-ard.yaml b/tests/components/bmp280_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/bmp280_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/bmp3xx_i2c/test.esp32-ard.yaml b/tests/components/bmp3xx_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/bmp3xx_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/bmp3xx_i2c/test.esp32-c3-ard.yaml b/tests/components/bmp3xx_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/bmp3xx_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/bmp3xx_spi/test.esp32-ard.yaml b/tests/components/bmp3xx_spi/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/bmp3xx_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/bmp3xx_spi/test.esp32-c3-ard.yaml b/tests/components/bmp3xx_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/bmp3xx_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/bmp581/test.esp32-ard.yaml b/tests/components/bmp581/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/bmp581/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/bmp581/test.esp32-c3-ard.yaml b/tests/components/bmp581/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/bmp581/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/bp1658cj/test.esp32-ard.yaml b/tests/components/bp1658cj/test.esp32-ard.yaml deleted file mode 100644 index d295973e3f..0000000000 --- a/tests/components/bp1658cj/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/bp1658cj/test.esp32-c3-ard.yaml b/tests/components/bp1658cj/test.esp32-c3-ard.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/bp1658cj/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/bp5758d/test.esp32-ard.yaml b/tests/components/bp5758d/test.esp32-ard.yaml deleted file mode 100644 index d295973e3f..0000000000 --- a/tests/components/bp5758d/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/bp5758d/test.esp32-c3-ard.yaml b/tests/components/bp5758d/test.esp32-c3-ard.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/bp5758d/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/button/test.esp32-ard.yaml b/tests/components/button/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/button/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/button/test.esp32-c3-ard.yaml b/tests/components/button/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/button/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/bytebuffer/test.esp32-ard.yaml b/tests/components/bytebuffer/test.esp32-ard.yaml deleted file mode 100644 index 380ca87628..0000000000 --- a/tests/components/bytebuffer/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -!include common.yaml diff --git a/tests/components/bytebuffer/test.esp32-c3-ard.yaml b/tests/components/bytebuffer/test.esp32-c3-ard.yaml deleted file mode 100644 index 380ca87628..0000000000 --- a/tests/components/bytebuffer/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -!include common.yaml diff --git a/tests/components/camera/test.esp32-ard.yaml b/tests/components/camera/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/camera/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/camera_encoder/test.esp32-ard.yaml b/tests/components/camera_encoder/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/camera_encoder/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/canbus/test.esp32-ard.yaml b/tests/components/canbus/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/canbus/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/canbus/test.esp32-c3-ard.yaml b/tests/components/canbus/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/canbus/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/cap1188/test.esp32-ard.yaml b/tests/components/cap1188/test.esp32-ard.yaml deleted file mode 100644 index 1ca773e06c..0000000000 --- a/tests/components/cap1188/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - reset_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/cap1188/test.esp32-c3-ard.yaml b/tests/components/cap1188/test.esp32-c3-ard.yaml deleted file mode 100644 index 1e6670c196..0000000000 --- a/tests/components/cap1188/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - reset_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/ccs811/test.esp32-ard.yaml b/tests/components/ccs811/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ccs811/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ccs811/test.esp32-c3-ard.yaml b/tests/components/ccs811/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ccs811/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/cd74hc4067/test.esp32-ard.yaml b/tests/components/cd74hc4067/test.esp32-ard.yaml deleted file mode 100644 index c4dd280943..0000000000 --- a/tests/components/cd74hc4067/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - pin_s0: GPIO12 - pin_s1: GPIO13 - pin_s2: GPIO14 - pin_s3: GPIO15 - pin: GPIO39 - -<<: !include common.yaml diff --git a/tests/components/cd74hc4067/test.esp32-c3-ard.yaml b/tests/components/cd74hc4067/test.esp32-c3-ard.yaml deleted file mode 100644 index 5e8784c1fc..0000000000 --- a/tests/components/cd74hc4067/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - pin_s0: GPIO2 - pin_s1: GPIO3 - pin_s2: GPIO4 - pin_s3: GPIO5 - pin: GPIO0 - -<<: !include common.yaml diff --git a/tests/components/ch422g/test.esp32-ard.yaml b/tests/components/ch422g/test.esp32-ard.yaml deleted file mode 100644 index cd3f1bbeef..0000000000 --- a/tests/components/ch422g/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_ch422g - scl: 16 - sda: 17 - -<<: !include common.yaml diff --git a/tests/components/ch422g/test.esp32-c3-ard.yaml b/tests/components/ch422g/test.esp32-c3-ard.yaml deleted file mode 100644 index cd822cb308..0000000000 --- a/tests/components/ch422g/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_ch422g - scl: 5 - sda: 4 - -<<: !include common.yaml diff --git a/tests/components/chsc6x/test.esp32-ard.yaml b/tests/components/chsc6x/test.esp32-ard.yaml deleted file mode 100644 index 9bc58b66f6..0000000000 --- a/tests/components/chsc6x/test.esp32-ard.yaml +++ /dev/null @@ -1,25 +0,0 @@ -i2c: - - id: i2c_chsc6x - scl: 3 - sda: 21 - -spi: - clk_pin: 16 - mosi_pin: 17 - -display: - - platform: ili9xxx - id: ili9xxx_display - model: GC9A01A - invert_colors: True - cs_pin: 18 - dc_pin: 19 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: chsc6x - display: ili9xxx_display - interrupt_pin: 20 diff --git a/tests/components/chsc6x/test.esp32-c3-ard.yaml b/tests/components/chsc6x/test.esp32-c3-ard.yaml deleted file mode 100644 index b0f55eb2e6..0000000000 --- a/tests/components/chsc6x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,25 +0,0 @@ -i2c: - - id: i2c_chsc6x - scl: 3 - sda: 9 - -spi: - clk_pin: 5 - mosi_pin: 4 - -display: - - platform: ili9xxx - id: ili9xxx_display - model: GC9A01A - invert_colors: True - cs_pin: 18 - dc_pin: 19 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: chsc6x - display: ili9xxx_display - interrupt_pin: 20 diff --git a/tests/components/climate_ir_lg/test.esp32-ard.yaml b/tests/components/climate_ir_lg/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/climate_ir_lg/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/climate_ir_lg/test.esp32-c3-ard.yaml b/tests/components/climate_ir_lg/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/climate_ir_lg/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/cm1106/test.esp32-ard.yaml b/tests/components/cm1106/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/cm1106/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/cm1106/test.esp32-c3-ard.yaml b/tests/components/cm1106/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/cm1106/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/color/test.esp32-ard.yaml b/tests/components/color/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/color/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/color/test.esp32-c3-ard.yaml b/tests/components/color/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/color/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/color_temperature/test.esp32-ard.yaml b/tests/components/color_temperature/test.esp32-ard.yaml deleted file mode 100644 index 1831adda6e..0000000000 --- a/tests/components/color_temperature/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - light_platform: ledc - pin_o1: GPIO16 - pin_o2: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/color_temperature/test.esp32-c3-ard.yaml b/tests/components/color_temperature/test.esp32-c3-ard.yaml deleted file mode 100644 index 016f315d9f..0000000000 --- a/tests/components/color_temperature/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - light_platform: ledc - pin_o1: GPIO6 - pin_o2: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/combination/test.esp32-ard.yaml b/tests/components/combination/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/combination/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/combination/test.esp32-c3-ard.yaml b/tests/components/combination/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/combination/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/coolix/test.esp32-ard.yaml b/tests/components/coolix/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/coolix/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/coolix/test.esp32-c3-ard.yaml b/tests/components/coolix/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/coolix/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/copy/test.esp32-ard.yaml b/tests/components/copy/test.esp32-ard.yaml deleted file mode 100644 index e5337726dc..0000000000 --- a/tests/components/copy/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - pwm_platform: ledc - pin: GPIO12 - -<<: !include common.yaml diff --git a/tests/components/copy/test.esp32-c3-ard.yaml b/tests/components/copy/test.esp32-c3-ard.yaml deleted file mode 100644 index 76272beb77..0000000000 --- a/tests/components/copy/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - pwm_platform: ledc - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/cs5460a/test.esp32-ard.yaml b/tests/components/cs5460a/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/cs5460a/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/cs5460a/test.esp32-c3-ard.yaml b/tests/components/cs5460a/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/cs5460a/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/cse7761/test.esp32-ard.yaml b/tests/components/cse7761/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/cse7761/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/cse7761/test.esp32-c3-ard.yaml b/tests/components/cse7761/test.esp32-c3-ard.yaml deleted file mode 100644 index c79d14c740..0000000000 --- a/tests/components/cse7761/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/cse7766/test.esp32-ard.yaml b/tests/components/cse7766/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/cse7766/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/cse7766/test.esp32-c3-ard.yaml b/tests/components/cse7766/test.esp32-c3-ard.yaml deleted file mode 100644 index c79d14c740..0000000000 --- a/tests/components/cse7766/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/cst226/test.esp32-ard.yaml b/tests/components/cst226/test.esp32-ard.yaml deleted file mode 100644 index 11e2c4fd43..0000000000 --- a/tests/components/cst226/test.esp32-ard.yaml +++ /dev/null @@ -1,12 +0,0 @@ -substitutions: - clk_pin: GPIO0 - mosi_pin: GPIO2 - cs_pin: GPIO4 - dc_pin: GPIO5 - disp_reset_pin: GPIO12 - scl_pin: GPIO13 - sda_pin: GPIO14 - interrupt_pin: GPIO15 - reset_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/cst226/test.esp32-c3-ard.yaml b/tests/components/cst226/test.esp32-c3-ard.yaml deleted file mode 100644 index 2f9bd72882..0000000000 --- a/tests/components/cst226/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,12 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - cs_pin: GPIO8 - dc_pin: GPIO9 - disp_reset_pin: GPIO10 - scl_pin: GPIO0 - sda_pin: GPIO1 - interrupt_pin: GPIO2 - reset_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/cst816/test.esp32-ard.yaml b/tests/components/cst816/test.esp32-ard.yaml deleted file mode 100644 index 11e2c4fd43..0000000000 --- a/tests/components/cst816/test.esp32-ard.yaml +++ /dev/null @@ -1,12 +0,0 @@ -substitutions: - clk_pin: GPIO0 - mosi_pin: GPIO2 - cs_pin: GPIO4 - dc_pin: GPIO5 - disp_reset_pin: GPIO12 - scl_pin: GPIO13 - sda_pin: GPIO14 - interrupt_pin: GPIO15 - reset_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/cst816/test.esp32-c3-ard.yaml b/tests/components/cst816/test.esp32-c3-ard.yaml deleted file mode 100644 index 2f9bd72882..0000000000 --- a/tests/components/cst816/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,12 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - cs_pin: GPIO8 - dc_pin: GPIO9 - disp_reset_pin: GPIO10 - scl_pin: GPIO0 - sda_pin: GPIO1 - interrupt_pin: GPIO2 - reset_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/ct_clamp/test.esp32-ard.yaml b/tests/components/ct_clamp/test.esp32-ard.yaml deleted file mode 100644 index 0a70e3f733..0000000000 --- a/tests/components/ct_clamp/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO39 - -<<: !include common.yaml diff --git a/tests/components/ct_clamp/test.esp32-c3-ard.yaml b/tests/components/ct_clamp/test.esp32-c3-ard.yaml deleted file mode 100644 index a8f29c98ae..0000000000 --- a/tests/components/ct_clamp/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO0 - -<<: !include common.yaml diff --git a/tests/components/current_based/test.esp32-ard.yaml b/tests/components/current_based/test.esp32-ard.yaml deleted file mode 100644 index 2c57d412f6..0000000000 --- a/tests/components/current_based/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - irq_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/current_based/test.esp32-c3-ard.yaml b/tests/components/current_based/test.esp32-c3-ard.yaml deleted file mode 100644 index 799acabd5a..0000000000 --- a/tests/components/current_based/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - irq_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/cwww/test.esp32-ard.yaml b/tests/components/cwww/test.esp32-ard.yaml deleted file mode 100644 index 1831adda6e..0000000000 --- a/tests/components/cwww/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - light_platform: ledc - pin_o1: GPIO16 - pin_o2: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/cwww/test.esp32-c3-ard.yaml b/tests/components/cwww/test.esp32-c3-ard.yaml deleted file mode 100644 index 016f315d9f..0000000000 --- a/tests/components/cwww/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - light_platform: ledc - pin_o1: GPIO6 - pin_o2: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/dac7678/test.esp32-ard.yaml b/tests/components/dac7678/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/dac7678/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/dac7678/test.esp32-c3-ard.yaml b/tests/components/dac7678/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/dac7678/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/daikin/test.esp32-ard.yaml b/tests/components/daikin/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/daikin/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/daikin_arc/test.esp32-ard.yaml b/tests/components/daikin_arc/test.esp32-ard.yaml deleted file mode 100644 index cd59eb0832..0000000000 --- a/tests/components/daikin_arc/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO2 - rx_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/daikin_brc/test.esp32-ard.yaml b/tests/components/daikin_brc/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/daikin_brc/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/daikin_brc/test.esp32-c3-ard.yaml b/tests/components/daikin_brc/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/daikin_brc/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/dallas_temp/test.esp32-ard.yaml b/tests/components/dallas_temp/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/dallas_temp/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/dallas_temp/test.esp32-c3-ard.yaml b/tests/components/dallas_temp/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/dallas_temp/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/daly_bms/test.esp32-ard.yaml b/tests/components/daly_bms/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/daly_bms/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/daly_bms/test.esp32-c3-ard.yaml b/tests/components/daly_bms/test.esp32-c3-ard.yaml deleted file mode 100644 index c79d14c740..0000000000 --- a/tests/components/daly_bms/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/deep_sleep/test.esp32-ard.yaml b/tests/components/deep_sleep/test.esp32-ard.yaml deleted file mode 100644 index 10c17af0f5..0000000000 --- a/tests/components/deep_sleep/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - wakeup_pin: GPIO4 - -<<: !include common.yaml -<<: !include common-esp32.yaml diff --git a/tests/components/deep_sleep/test.esp32-c3-ard.yaml b/tests/components/deep_sleep/test.esp32-c3-ard.yaml deleted file mode 100644 index 10c17af0f5..0000000000 --- a/tests/components/deep_sleep/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - wakeup_pin: GPIO4 - -<<: !include common.yaml -<<: !include common-esp32.yaml diff --git a/tests/components/delonghi/test.esp32-ard.yaml b/tests/components/delonghi/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/delonghi/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/delonghi/test.esp32-c3-ard.yaml b/tests/components/delonghi/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/delonghi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/dfplayer/test.esp32-ard.yaml b/tests/components/dfplayer/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/dfplayer/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/dfplayer/test.esp32-c3-ard.yaml b/tests/components/dfplayer/test.esp32-c3-ard.yaml deleted file mode 100644 index c79d14c740..0000000000 --- a/tests/components/dfplayer/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.esp32-ard.yaml b/tests/components/dfrobot_sen0395/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/dfrobot_sen0395/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.esp32-c3-ard.yaml b/tests/components/dfrobot_sen0395/test.esp32-c3-ard.yaml deleted file mode 100644 index c79d14c740..0000000000 --- a/tests/components/dfrobot_sen0395/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/dht/test.esp32-ard.yaml b/tests/components/dht/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/dht/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/dht/test.esp32-c3-ard.yaml b/tests/components/dht/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/dht/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/dht12/test.esp32-ard.yaml b/tests/components/dht12/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/dht12/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/dht12/test.esp32-c3-ard.yaml b/tests/components/dht12/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/dht12/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/display/test.esp32-ard.yaml b/tests/components/display/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/display/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/dps310/test.esp32-ard.yaml b/tests/components/dps310/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/dps310/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/dps310/test.esp32-c3-ard.yaml b/tests/components/dps310/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/dps310/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ds1307/test.esp32-ard.yaml b/tests/components/ds1307/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ds1307/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ds1307/test.esp32-c3-ard.yaml b/tests/components/ds1307/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ds1307/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ds2484/test.esp32-ard.yaml b/tests/components/ds2484/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ds2484/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ds2484/test.esp32-c3-ard.yaml b/tests/components/ds2484/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ds2484/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/duty_cycle/test.esp32-ard.yaml b/tests/components/duty_cycle/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/duty_cycle/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/duty_cycle/test.esp32-c3-ard.yaml b/tests/components/duty_cycle/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/duty_cycle/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/duty_time/test.esp32-ard.yaml b/tests/components/duty_time/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/duty_time/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/duty_time/test.esp32-c3-ard.yaml b/tests/components/duty_time/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/duty_time/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/e131/test.esp32-ard.yaml b/tests/components/e131/test.esp32-ard.yaml deleted file mode 100644 index 32cf2a64ba..0000000000 --- a/tests/components/e131/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - light_platform: esp32_rmt_led_strip - pin: GPIO2 - -<<: !include common-ard.yaml diff --git a/tests/components/e131/test.esp32-c3-ard.yaml b/tests/components/e131/test.esp32-c3-ard.yaml deleted file mode 100644 index 32cf2a64ba..0000000000 --- a/tests/components/e131/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - light_platform: esp32_rmt_led_strip - pin: GPIO2 - -<<: !include common-ard.yaml diff --git a/tests/components/ee895/test.esp32-ard.yaml b/tests/components/ee895/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ee895/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ee895/test.esp32-c3-ard.yaml b/tests/components/ee895/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ee895/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ektf2232/test.esp32-ard.yaml b/tests/components/ektf2232/test.esp32-ard.yaml deleted file mode 100644 index 7d3f2ca7a2..0000000000 --- a/tests/components/ektf2232/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - display_reset_pin: GPIO13 - interrupt_pin: GPIO14 - touch_reset_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/ektf2232/test.esp32-c3-ard.yaml b/tests/components/ektf2232/test.esp32-c3-ard.yaml deleted file mode 100644 index 4d793a3242..0000000000 --- a/tests/components/ektf2232/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - display_reset_pin: GPIO3 - interrupt_pin: GPIO6 - touch_reset_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/emc2101/test.esp32-ard.yaml b/tests/components/emc2101/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/emc2101/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/emc2101/test.esp32-c3-ard.yaml b/tests/components/emc2101/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/emc2101/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/emmeti/test.esp32-ard.yaml b/tests/components/emmeti/test.esp32-ard.yaml deleted file mode 100644 index 2689ff279e..0000000000 --- a/tests/components/emmeti/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - remote_transmitter_pin: GPIO33 - remote_receiver_pin: GPIO32 - -<<: !include common.yaml diff --git a/tests/components/endstop/test.esp32-ard.yaml b/tests/components/endstop/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/endstop/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/endstop/test.esp32-c3-ard.yaml b/tests/components/endstop/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/endstop/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ens160_i2c/test.esp32-ard.yaml b/tests/components/ens160_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ens160_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ens160_i2c/test.esp32-c3-ard.yaml b/tests/components/ens160_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ens160_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ens160_spi/test.esp32-ard.yaml b/tests/components/ens160_spi/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/ens160_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/ens160_spi/test.esp32-c3-ard.yaml b/tests/components/ens160_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/ens160_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/ens210/test.esp32-ard.yaml b/tests/components/ens210/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ens210/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ens210/test.esp32-c3-ard.yaml b/tests/components/ens210/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ens210/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/es7210/test.esp32-ard.yaml b/tests/components/es7210/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/es7210/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/es7210/test.esp32-c3-ard.yaml b/tests/components/es7210/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/es7210/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/es7243e/test.esp32-ard.yaml b/tests/components/es7243e/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/es7243e/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/es7243e/test.esp32-c3-ard.yaml b/tests/components/es7243e/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/es7243e/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/es8156/test.esp32-ard.yaml b/tests/components/es8156/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/es8156/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/es8156/test.esp32-c3-ard.yaml b/tests/components/es8156/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/es8156/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/es8311/test.esp32-ard.yaml b/tests/components/es8311/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/es8311/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/es8311/test.esp32-c3-ard.yaml b/tests/components/es8311/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/es8311/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/es8388/test.esp32-ard.yaml b/tests/components/es8388/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/es8388/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/es8388/test.esp32-c3-ard.yaml b/tests/components/es8388/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/es8388/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/esp32_ble_client/test.esp32-ard.yaml b/tests/components/esp32_ble_client/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_ble_client/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_ble_client/test.esp32-c3-ard.yaml b/tests/components/esp32_ble_client/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_ble_client/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_ble_server/test.esp32-ard.yaml b/tests/components/esp32_ble_server/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_ble_server/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_ble_server/test.esp32-c3-ard.yaml b/tests/components/esp32_ble_server/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_ble_server/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_camera/test.esp32-ard.yaml b/tests/components/esp32_camera/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_camera/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_camera_web_server/test.esp32-ard.yaml b/tests/components/esp32_camera_web_server/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_camera_web_server/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_can/test.esp32-ard.yaml b/tests/components/esp32_can/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/esp32_can/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/esp32_can/test.esp32-c3-ard.yaml b/tests/components/esp32_can/test.esp32-c3-ard.yaml deleted file mode 100644 index c79d14c740..0000000000 --- a/tests/components/esp32_can/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/esp32_dac/test.esp32-ard.yaml b/tests/components/esp32_dac/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_dac/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_improv/test.esp32-ard.yaml b/tests/components/esp32_improv/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_improv/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_improv/test.esp32-c3-ard.yaml b/tests/components/esp32_improv/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_improv/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml deleted file mode 100644 index 0949b676d5..0000000000 --- a/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - pin1: GPIO13 - pin2: GPIO14 - -packages: - common: !include common.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml deleted file mode 100644 index 6cc0667e77..0000000000 --- a/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - pin1: GPIO3 - pin2: GPIO4 - -packages: - common: !include common.yaml diff --git a/tests/components/esp32_touch/test.esp32-ard.yaml b/tests/components/esp32_touch/test.esp32-ard.yaml deleted file mode 100644 index 25316b8646..0000000000 --- a/tests/components/esp32_touch/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO27 - -<<: !include common.yaml diff --git a/tests/components/esp32_touch/test.esp32-s2-ard.yaml b/tests/components/esp32_touch/test.esp32-s2-ard.yaml deleted file mode 100644 index 575d758fae..0000000000 --- a/tests/components/esp32_touch/test.esp32-s2-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO12 - -<<: !include common-variants.yaml diff --git a/tests/components/esp32_touch/test.esp32-s3-ard.yaml b/tests/components/esp32_touch/test.esp32-s3-ard.yaml deleted file mode 100644 index 575d758fae..0000000000 --- a/tests/components/esp32_touch/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO12 - -<<: !include common-variants.yaml diff --git a/tests/components/esphome/test.esp32-ard.yaml b/tests/components/esphome/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esphome/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esphome/test.esp32-c3-ard.yaml b/tests/components/esphome/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esphome/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ethernet_info/test.esp32-ard.yaml b/tests/components/ethernet_info/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ethernet_info/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/event/test.esp32-ard.yaml b/tests/components/event/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/event/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/event/test.esp32-c3-ard.yaml b/tests/components/event/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/event/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/exposure_notifications/test.esp32-ard.yaml b/tests/components/exposure_notifications/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/exposure_notifications/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/exposure_notifications/test.esp32-c3-ard.yaml b/tests/components/exposure_notifications/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/exposure_notifications/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/external_components/test.esp32-ard.yaml b/tests/components/external_components/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/external_components/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/external_components/test.esp32-c3-ard.yaml b/tests/components/external_components/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/external_components/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ezo/test.esp32-ard.yaml b/tests/components/ezo/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ezo/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ezo/test.esp32-c3-ard.yaml b/tests/components/ezo/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ezo/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.esp32-ard.yaml b/tests/components/ezo_pmp/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ezo_pmp/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.esp32-c3-ard.yaml b/tests/components/ezo_pmp/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ezo_pmp/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/factory_reset/test.esp32-ard.yaml b/tests/components/factory_reset/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/factory_reset/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/factory_reset/test.esp32-c3-ard.yaml b/tests/components/factory_reset/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/factory_reset/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/feedback/test.esp32-ard.yaml b/tests/components/feedback/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/feedback/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/feedback/test.esp32-c3-ard.yaml b/tests/components/feedback/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/feedback/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.esp32-ard.yaml b/tests/components/fingerprint_grow/test.esp32-ard.yaml deleted file mode 100644 index 4aef3d8be6..0000000000 --- a/tests/components/fingerprint_grow/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - sensing_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.esp32-c3-ard.yaml b/tests/components/fingerprint_grow/test.esp32-c3-ard.yaml deleted file mode 100644 index faab50e152..0000000000 --- a/tests/components/fingerprint_grow/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - sensing_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/font/test.esp32-ard.yaml b/tests/components/font/test.esp32-ard.yaml deleted file mode 100644 index d98600a51b..0000000000 --- a/tests/components/font/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - i2c_scl: GPIO16 - i2c_sda: GPIO17 - display_reset_pin: GPIO13 - -packages: - common: !include common.yaml diff --git a/tests/components/font/test.esp32-c3-ard.yaml b/tests/components/font/test.esp32-c3-ard.yaml deleted file mode 100644 index ad14a2e9a6..0000000000 --- a/tests/components/font/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - i2c_scl: GPIO5 - i2c_sda: GPIO4 - display_reset_pin: GPIO3 - -packages: - common: !include common.yaml diff --git a/tests/components/fs3000/test.esp32-ard.yaml b/tests/components/fs3000/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/fs3000/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/fs3000/test.esp32-c3-ard.yaml b/tests/components/fs3000/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/fs3000/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ft5x06/test.esp32-ard.yaml b/tests/components/ft5x06/test.esp32-ard.yaml deleted file mode 100644 index 1ca773e06c..0000000000 --- a/tests/components/ft5x06/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - reset_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/ft5x06/test.esp32-c3-ard.yaml b/tests/components/ft5x06/test.esp32-c3-ard.yaml deleted file mode 100644 index 1e6670c196..0000000000 --- a/tests/components/ft5x06/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - reset_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/ft63x6/test.esp32-ard.yaml b/tests/components/ft63x6/test.esp32-ard.yaml deleted file mode 100644 index 47b5796e8b..0000000000 --- a/tests/components/ft63x6/test.esp32-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO0 - mosi_pin: GPIO2 - scl_pin: GPIO13 - sda_pin: GPIO14 - interrupt_pin: GPIO15 - reset_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/ft63x6/test.esp32-c3-ard.yaml b/tests/components/ft63x6/test.esp32-c3-ard.yaml deleted file mode 100644 index 397ac1e464..0000000000 --- a/tests/components/ft63x6/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - scl_pin: GPIO0 - sda_pin: GPIO1 - interrupt_pin: GPIO2 - reset_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/fujitsu_general/test.esp32-ard.yaml b/tests/components/fujitsu_general/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/fujitsu_general/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/fujitsu_general/test.esp32-c3-ard.yaml b/tests/components/fujitsu_general/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/fujitsu_general/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/gcja5/test.esp32-ard.yaml b/tests/components/gcja5/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/gcja5/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/gcja5/test.esp32-c3-ard.yaml b/tests/components/gcja5/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/gcja5/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/gdk101/test.esp32-ard.yaml b/tests/components/gdk101/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/gdk101/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/gl_r01_i2c/test.esp32-ard.yaml b/tests/components/gl_r01_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/gl_r01_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/gl_r01_i2c/test.esp32-c3-ard.yaml b/tests/components/gl_r01_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/gl_r01_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/globals/test.esp32-ard.yaml b/tests/components/globals/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/globals/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/globals/test.esp32-c3-ard.yaml b/tests/components/globals/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/globals/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/gp2y1010au0f/test.esp32-ard.yaml b/tests/components/gp2y1010au0f/test.esp32-ard.yaml deleted file mode 100644 index d9494a95b7..0000000000 --- a/tests/components/gp2y1010au0f/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - adc_pin: A0 - output_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/gp2y1010au0f/test.esp32-c3-ard.yaml b/tests/components/gp2y1010au0f/test.esp32-c3-ard.yaml deleted file mode 100644 index 0e331c893c..0000000000 --- a/tests/components/gp2y1010au0f/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - adc_pin: GPIO0 - output_pin: GPIO1 - -<<: !include common.yaml diff --git a/tests/components/gp8403/test.esp32-ard.yaml b/tests/components/gp8403/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/gp8403/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/gp8403/test.esp32-c3-ard.yaml b/tests/components/gp8403/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/gp8403/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/gpio/test.esp32-ard.yaml b/tests/components/gpio/test.esp32-ard.yaml deleted file mode 100644 index 09f41abb79..0000000000 --- a/tests/components/gpio/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - binary_sensor_pin: GPIO12 - output_pin: GPIO13 - switch_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/gpio/test.esp32-c3-ard.yaml b/tests/components/gpio/test.esp32-c3-ard.yaml deleted file mode 100644 index fc7c9942d0..0000000000 --- a/tests/components/gpio/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - binary_sensor_pin: GPIO2 - output_pin: GPIO3 - switch_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/gps/test.esp32-ard.yaml b/tests/components/gps/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/gps/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/gps/test.esp32-c3-ard.yaml b/tests/components/gps/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/gps/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/graph/test.esp32-ard.yaml b/tests/components/graph/test.esp32-ard.yaml deleted file mode 100644 index 1ca773e06c..0000000000 --- a/tests/components/graph/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - reset_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/graph/test.esp32-c3-ard.yaml b/tests/components/graph/test.esp32-c3-ard.yaml deleted file mode 100644 index 1e6670c196..0000000000 --- a/tests/components/graph/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - reset_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.esp32-ard.yaml b/tests/components/graphical_display_menu/test.esp32-ard.yaml deleted file mode 100644 index 1ca773e06c..0000000000 --- a/tests/components/graphical_display_menu/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - reset_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.esp32-c3-ard.yaml b/tests/components/graphical_display_menu/test.esp32-c3-ard.yaml deleted file mode 100644 index 1e6670c196..0000000000 --- a/tests/components/graphical_display_menu/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - reset_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/gree/test.esp32-ard.yaml b/tests/components/gree/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/gree/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/gree/test.esp32-c3-ard.yaml b/tests/components/gree/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/gree/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp32-ard.yaml b/tests/components/grove_gas_mc_v2/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/grove_gas_mc_v2/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp32-c3-ard.yaml b/tests/components/grove_gas_mc_v2/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/grove_gas_mc_v2/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.esp32-ard.yaml b/tests/components/grove_tb6612fng/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/grove_tb6612fng/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.esp32-c3-ard.yaml b/tests/components/grove_tb6612fng/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/grove_tb6612fng/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/growatt_solar/test.esp32-ard.yaml b/tests/components/growatt_solar/test.esp32-ard.yaml deleted file mode 100644 index bd767a8ece..0000000000 --- a/tests/components/growatt_solar/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - flow_control_pin: GPIO13 - -<<: !include common.yaml diff --git a/tests/components/growatt_solar/test.esp32-c3-ard.yaml b/tests/components/growatt_solar/test.esp32-c3-ard.yaml deleted file mode 100644 index 452031a5aa..0000000000 --- a/tests/components/growatt_solar/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - flow_control_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/gt911/test.esp32-ard.yaml b/tests/components/gt911/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/gt911/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/gt911/test.esp32-c3-ard.yaml b/tests/components/gt911/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/gt911/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/haier/test.esp32-ard.yaml b/tests/components/haier/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/haier/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/haier/test.esp32-c3-ard.yaml b/tests/components/haier/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/haier/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/havells_solar/test.esp32-ard.yaml b/tests/components/havells_solar/test.esp32-ard.yaml deleted file mode 100644 index bd767a8ece..0000000000 --- a/tests/components/havells_solar/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - flow_control_pin: GPIO13 - -<<: !include common.yaml diff --git a/tests/components/havells_solar/test.esp32-c3-ard.yaml b/tests/components/havells_solar/test.esp32-c3-ard.yaml deleted file mode 100644 index 452031a5aa..0000000000 --- a/tests/components/havells_solar/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - flow_control_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/hbridge/test.esp32-ard.yaml b/tests/components/hbridge/test.esp32-ard.yaml deleted file mode 100644 index e50d537749..0000000000 --- a/tests/components/hbridge/test.esp32-ard.yaml +++ /dev/null @@ -1,17 +0,0 @@ -substitutions: - pwm_platform: ledc - output1_pin: "14" - output2_pin: "15" - output3_pin: "12" - output4_pin: "13" - hbridge_on_pin: "4" - hbridge_off_pin: "5" - -packages: - common: !include common.yaml - -switch: - - id: !extend switch_hbridge - pulse_length: 60ms - wait_time: 10ms - optimistic: false diff --git a/tests/components/hbridge/test.esp32-c3-ard.yaml b/tests/components/hbridge/test.esp32-c3-ard.yaml deleted file mode 100644 index b9e8738442..0000000000 --- a/tests/components/hbridge/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,16 +0,0 @@ -substitutions: - pwm_platform: "ledc" - output1_pin: "4" - output2_pin: "5" - output3_pin: "6" - output4_pin: "7" - hbridge_on_pin: "2" - hbridge_off_pin: "3" - -packages: - common: !include common.yaml - -switch: - - id: !extend switch_hbridge - wait_time: 10ms - optimistic: true diff --git a/tests/components/hdc1080/test.esp32-ard.yaml b/tests/components/hdc1080/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/hdc1080/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/hdc1080/test.esp32-c3-ard.yaml b/tests/components/hdc1080/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/hdc1080/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/he60r/test.esp32-ard.yaml b/tests/components/he60r/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/he60r/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/he60r/test.esp32-c3-ard.yaml b/tests/components/he60r/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/he60r/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp32-ard.yaml b/tests/components/hitachi_ac344/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/hitachi_ac344/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp32-c3-ard.yaml b/tests/components/hitachi_ac344/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/hitachi_ac344/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp32-ard.yaml b/tests/components/hitachi_ac424/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/hitachi_ac424/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp32-c3-ard.yaml b/tests/components/hitachi_ac424/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/hitachi_ac424/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/hlw8012/test.esp32-ard.yaml b/tests/components/hlw8012/test.esp32-ard.yaml deleted file mode 100644 index 8b42b21b54..0000000000 --- a/tests/components/hlw8012/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - sel_pin: GPIO12 - cf_pin: GPIO13 - cf1_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/hlw8012/test.esp32-c3-ard.yaml b/tests/components/hlw8012/test.esp32-c3-ard.yaml deleted file mode 100644 index 8b0d069ce2..0000000000 --- a/tests/components/hlw8012/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - sel_pin: GPIO2 - cf_pin: GPIO3 - cf1_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/hm3301/test.esp32-ard.yaml b/tests/components/hm3301/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/hm3301/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/hm3301/test.esp32-c3-ard.yaml b/tests/components/hm3301/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/hm3301/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/hmc5883l/test.esp32-ard.yaml b/tests/components/hmc5883l/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/hmc5883l/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/hmc5883l/test.esp32-c3-ard.yaml b/tests/components/hmc5883l/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/hmc5883l/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/homeassistant/test.esp32-ard.yaml b/tests/components/homeassistant/test.esp32-ard.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/homeassistant/test.esp32-ard.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/homeassistant/test.esp32-c3-ard.yaml b/tests/components/homeassistant/test.esp32-c3-ard.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/homeassistant/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.esp32-ard.yaml b/tests/components/honeywell_hih_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/honeywell_hih_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.esp32-c3-ard.yaml b/tests/components/honeywell_hih_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/honeywell_hih_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/honeywellabp/test.esp32-ard.yaml b/tests/components/honeywellabp/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/honeywellabp/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/honeywellabp/test.esp32-c3-ard.yaml b/tests/components/honeywellabp/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/honeywellabp/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.esp32-ard.yaml b/tests/components/honeywellabp2_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/honeywellabp2_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.esp32-c3-ard.yaml b/tests/components/honeywellabp2_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/honeywellabp2_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-ard.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-ard.yaml deleted file mode 100644 index 811f6b72a6..0000000000 --- a/tests/components/hrxl_maxsonar_wr/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-ard.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/hte501/test.esp32-ard.yaml b/tests/components/hte501/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/hte501/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/hte501/test.esp32-c3-ard.yaml b/tests/components/hte501/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/hte501/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/htu21d/test.esp32-ard.yaml b/tests/components/htu21d/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/htu21d/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/htu21d/test.esp32-c3-ard.yaml b/tests/components/htu21d/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/htu21d/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/htu31d/test.esp32-ard.yaml b/tests/components/htu31d/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/htu31d/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/htu31d/test.esp32-c3-ard.yaml b/tests/components/htu31d/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/htu31d/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/hx711/test.esp32-ard.yaml b/tests/components/hx711/test.esp32-ard.yaml deleted file mode 100644 index 6423867395..0000000000 --- a/tests/components/hx711/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clk_pin: GPIO16 - dout_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/hx711/test.esp32-c3-ard.yaml b/tests/components/hx711/test.esp32-c3-ard.yaml deleted file mode 100644 index 08a6e705c0..0000000000 --- a/tests/components/hx711/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clk_pin: GPIO5 - dout_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.esp32-ard.yaml b/tests/components/hydreon_rgxx/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/hydreon_rgxx/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.esp32-c3-ard.yaml b/tests/components/hydreon_rgxx/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/hydreon_rgxx/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/hyt271/test.esp32-ard.yaml b/tests/components/hyt271/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/hyt271/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/hyt271/test.esp32-c3-ard.yaml b/tests/components/hyt271/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/hyt271/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/i2c_device/test.esp32-ard.yaml b/tests/components/i2c_device/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/i2c_device/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/i2c_device/test.esp32-c3-ard.yaml b/tests/components/i2c_device/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/i2c_device/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/i2s_audio/test.esp32-ard.yaml b/tests/components/i2s_audio/test.esp32-ard.yaml deleted file mode 100644 index ce751d7d4a..0000000000 --- a/tests/components/i2s_audio/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - i2s_bclk_pin: GPIO15 - i2s_lrclk_pin: GPIO16 - i2s_mclk_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/i2s_audio/test.esp32-c3-ard.yaml b/tests/components/i2s_audio/test.esp32-c3-ard.yaml deleted file mode 100644 index 5490846ae9..0000000000 --- a/tests/components/i2s_audio/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - i2s_bclk_pin: GPIO5 - i2s_lrclk_pin: GPIO6 - i2s_mclk_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/iaqcore/test.esp32-ard.yaml b/tests/components/iaqcore/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/iaqcore/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/iaqcore/test.esp32-c3-ard.yaml b/tests/components/iaqcore/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/iaqcore/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ili9xxx/test.esp32-ard.yaml b/tests/components/ili9xxx/test.esp32-ard.yaml deleted file mode 100644 index 2e006d2521..0000000000 --- a/tests/components/ili9xxx/test.esp32-ard.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin1: GPIO12 - dc_pin1: GPIO13 - reset_pin1: GPIO14 - cs_pin2: GPIO25 - dc_pin2: GPIO26 - reset_pin2: GPIO27 - -<<: !include common.yaml diff --git a/tests/components/ili9xxx/test.esp32-c3-ard.yaml b/tests/components/ili9xxx/test.esp32-c3-ard.yaml deleted file mode 100644 index 3037785e81..0000000000 --- a/tests/components/ili9xxx/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,12 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin1: GPIO8 - dc_pin1: GPIO9 - reset_pin1: GPIO10 - cs_pin2: GPIO2 - dc_pin2: GPIO3 - reset_pin2: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ina219/test.esp32-ard.yaml b/tests/components/ina219/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ina219/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ina219/test.esp32-c3-ard.yaml b/tests/components/ina219/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ina219/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ina226/test.esp32-ard.yaml b/tests/components/ina226/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ina226/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ina226/test.esp32-c3-ard.yaml b/tests/components/ina226/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ina226/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ina260/test.esp32-ard.yaml b/tests/components/ina260/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ina260/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ina260/test.esp32-c3-ard.yaml b/tests/components/ina260/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ina260/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ina2xx_i2c/test.esp32-ard.yaml b/tests/components/ina2xx_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ina2xx_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ina2xx_i2c/test.esp32-c3-ard.yaml b/tests/components/ina2xx_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ina2xx_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ina2xx_spi/test.esp32-ard.yaml b/tests/components/ina2xx_spi/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/ina2xx_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/ina2xx_spi/test.esp32-c3-ard.yaml b/tests/components/ina2xx_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/ina2xx_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/ina3221/test.esp32-ard.yaml b/tests/components/ina3221/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ina3221/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ina3221/test.esp32-c3-ard.yaml b/tests/components/ina3221/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ina3221/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/inkbird_ibsth1_mini/test.esp32-ard.yaml b/tests/components/inkbird_ibsth1_mini/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/inkbird_ibsth1_mini/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/inkbird_ibsth1_mini/test.esp32-c3-ard.yaml b/tests/components/inkbird_ibsth1_mini/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/inkbird_ibsth1_mini/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/inkplate/test.esp32-ard.yaml b/tests/components/inkplate/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/inkplate/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/integration/test.esp32-ard.yaml b/tests/components/integration/test.esp32-ard.yaml deleted file mode 100644 index f84495e521..0000000000 --- a/tests/components/integration/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: A0 - -<<: !include common-esp32.yaml diff --git a/tests/components/integration/test.esp32-c3-ard.yaml b/tests/components/integration/test.esp32-c3-ard.yaml deleted file mode 100644 index 5105e645f3..0000000000 --- a/tests/components/integration/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO1 - -<<: !include common-esp32.yaml diff --git a/tests/components/internal_temperature/test.esp32-ard.yaml b/tests/components/internal_temperature/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/internal_temperature/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-c3-ard.yaml b/tests/components/internal_temperature/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/internal_temperature/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-s2-ard.yaml b/tests/components/internal_temperature/test.esp32-s2-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/internal_temperature/test.esp32-s2-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-s3-ard.yaml b/tests/components/internal_temperature/test.esp32-s3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/internal_temperature/test.esp32-s3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/interval/test.esp32-ard.yaml b/tests/components/interval/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/interval/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/interval/test.esp32-c3-ard.yaml b/tests/components/interval/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/interval/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.esp32-ard.yaml b/tests/components/jsn_sr04t/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/jsn_sr04t/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.esp32-c3-ard.yaml b/tests/components/jsn_sr04t/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/jsn_sr04t/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/kamstrup_kmp/test.esp32-ard.yaml b/tests/components/kamstrup_kmp/test.esp32-ard.yaml deleted file mode 100644 index adc2c4d24a..0000000000 --- a/tests/components/kamstrup_kmp/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - uart_tx_pin: GPIO1 - uart_rx_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/key_collector/test.esp32-ard.yaml b/tests/components/key_collector/test.esp32-ard.yaml deleted file mode 100644 index de144aa46b..0000000000 --- a/tests/components/key_collector/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - pin_r0: GPIO12 - pin_r1: GPIO13 - pin_c0: GPIO14 - pin_c1: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/key_collector/test.esp32-c3-ard.yaml b/tests/components/key_collector/test.esp32-c3-ard.yaml deleted file mode 100644 index b580ab7843..0000000000 --- a/tests/components/key_collector/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - pin_r0: GPIO2 - pin_r1: GPIO3 - pin_c0: GPIO4 - pin_c1: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/kmeteriso/test.esp32-ard.yaml b/tests/components/kmeteriso/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/kmeteriso/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/kmeteriso/test.esp32-c3-ard.yaml b/tests/components/kmeteriso/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/kmeteriso/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/kuntze/test.esp32-ard.yaml b/tests/components/kuntze/test.esp32-ard.yaml deleted file mode 100644 index bd767a8ece..0000000000 --- a/tests/components/kuntze/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - flow_control_pin: GPIO13 - -<<: !include common.yaml diff --git a/tests/components/kuntze/test.esp32-c3-ard.yaml b/tests/components/kuntze/test.esp32-c3-ard.yaml deleted file mode 100644 index 452031a5aa..0000000000 --- a/tests/components/kuntze/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - flow_control_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp32-ard.yaml b/tests/components/lc709203f/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/lc709203f/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp32-c3-ard.yaml b/tests/components/lc709203f/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/lc709203f/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/lcd_gpio/test.esp32-ard.yaml b/tests/components/lcd_gpio/test.esp32-ard.yaml deleted file mode 100644 index 9c2af456b5..0000000000 --- a/tests/components/lcd_gpio/test.esp32-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - d0_pin: GPIO12 - d1_pin: GPIO13 - d2_pin: GPIO14 - d3_pin: GPIO15 - enable_pin: GPIO16 - rs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/lcd_gpio/test.esp32-c3-ard.yaml b/tests/components/lcd_gpio/test.esp32-c3-ard.yaml deleted file mode 100644 index b6b05f3ab4..0000000000 --- a/tests/components/lcd_gpio/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - d0_pin: GPIO1 - d1_pin: GPIO2 - d2_pin: GPIO3 - d3_pin: GPIO4 - enable_pin: GPIO5 - rs_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/lcd_menu/test.esp32-ard.yaml b/tests/components/lcd_menu/test.esp32-ard.yaml deleted file mode 100644 index 9c2af456b5..0000000000 --- a/tests/components/lcd_menu/test.esp32-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - d0_pin: GPIO12 - d1_pin: GPIO13 - d2_pin: GPIO14 - d3_pin: GPIO15 - enable_pin: GPIO16 - rs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/lcd_menu/test.esp32-c3-ard.yaml b/tests/components/lcd_menu/test.esp32-c3-ard.yaml deleted file mode 100644 index b6b05f3ab4..0000000000 --- a/tests/components/lcd_menu/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - d0_pin: GPIO1 - d1_pin: GPIO2 - d2_pin: GPIO3 - d3_pin: GPIO4 - enable_pin: GPIO5 - rs_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.esp32-ard.yaml b/tests/components/lcd_pcf8574/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/lcd_pcf8574/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.esp32-c3-ard.yaml b/tests/components/lcd_pcf8574/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/lcd_pcf8574/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ld2410/test.esp32-ard.yaml b/tests/components/ld2410/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/ld2410/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/ld2410/test.esp32-c3-ard.yaml b/tests/components/ld2410/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/ld2410/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/ld2412/test.esp32-ard.yaml b/tests/components/ld2412/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/ld2412/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/ld2412/test.esp32-c3-ard.yaml b/tests/components/ld2412/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/ld2412/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/ld2420/test.esp32-ard.yaml b/tests/components/ld2420/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/ld2420/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/ld2420/test.esp32-c3-ard.yaml b/tests/components/ld2420/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/ld2420/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/ld2450/test.esp32-ard.yaml b/tests/components/ld2450/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/ld2450/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/ld2450/test.esp32-c3-ard.yaml b/tests/components/ld2450/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/ld2450/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/ledc/test.esp32-ard.yaml b/tests/components/ledc/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ledc/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ledc/test.esp32-c3-ard.yaml b/tests/components/ledc/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ledc/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/light/test.esp32-ard.yaml b/tests/components/light/test.esp32-ard.yaml deleted file mode 100644 index 925197182c..0000000000 --- a/tests/components/light/test.esp32-ard.yaml +++ /dev/null @@ -1,21 +0,0 @@ -output: - - platform: gpio - id: test_binary - pin: 12 - - platform: ledc - id: test_ledc_1 - pin: 13 - - platform: ledc - id: test_ledc_2 - pin: 14 - - platform: ledc - id: test_ledc_3 - pin: 15 - - platform: ledc - id: test_ledc_4 - pin: 16 - - platform: ledc - id: test_ledc_5 - pin: 17 - -<<: !include common.yaml diff --git a/tests/components/light/test.esp32-c3-ard.yaml b/tests/components/light/test.esp32-c3-ard.yaml deleted file mode 100644 index 317d5748a3..0000000000 --- a/tests/components/light/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,21 +0,0 @@ -output: - - platform: gpio - id: test_binary - pin: 0 - - platform: ledc - id: test_ledc_1 - pin: 1 - - platform: ledc - id: test_ledc_2 - pin: 2 - - platform: ledc - id: test_ledc_3 - pin: 3 - - platform: ledc - id: test_ledc_4 - pin: 4 - - platform: ledc - id: test_ledc_5 - pin: 5 - -<<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.esp32-ard.yaml b/tests/components/lilygo_t5_47/test.esp32-ard.yaml deleted file mode 100644 index 342f0b6d8b..0000000000 --- a/tests/components/lilygo_t5_47/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - scl_pin: GPIO13 - sda_pin: GPIO14 - interrupt_pin: GPIO15 - reset_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.esp32-c3-ard.yaml b/tests/components/lilygo_t5_47/test.esp32-c3-ard.yaml deleted file mode 100644 index 061a98ce24..0000000000 --- a/tests/components/lilygo_t5_47/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - scl_pin: GPIO0 - sda_pin: GPIO1 - interrupt_pin: GPIO2 - reset_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/lm75b/test.esp32-ard.yaml b/tests/components/lm75b/test.esp32-ard.yaml deleted file mode 100644 index 43264df633..0000000000 --- a/tests/components/lm75b/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO15 - sda_pin: GPIO13 - -<<: !include common.yaml diff --git a/tests/components/lm75b/test.esp32-c3-ard.yaml b/tests/components/lm75b/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/lm75b/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/lock/test.esp32-ard.yaml b/tests/components/lock/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/lock/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/lock/test.esp32-c3-ard.yaml b/tests/components/lock/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/lock/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/lps22/test.esp32-ard.yaml b/tests/components/lps22/test.esp32-ard.yaml deleted file mode 100644 index 0da6a9577e..0000000000 --- a/tests/components/lps22/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_lps22 - scl: 16 - sda: 17 - -<<: !include common.yaml diff --git a/tests/components/lps22/test.esp32-c3-ard.yaml b/tests/components/lps22/test.esp32-c3-ard.yaml deleted file mode 100644 index 6091393d31..0000000000 --- a/tests/components/lps22/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_lps22 - scl: 5 - sda: 4 - -<<: !include common.yaml diff --git a/tests/components/ltr390/test.esp32-ard.yaml b/tests/components/ltr390/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ltr390/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ltr390/test.esp32-c3-ard.yaml b/tests/components/ltr390/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ltr390/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ltr501/test.esp32-ard.yaml b/tests/components/ltr501/test.esp32-ard.yaml deleted file mode 100644 index 4c710c74fe..0000000000 --- a/tests/components/ltr501/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_ltr501 - scl: 16 - sda: 17 - -<<: !include common.yaml diff --git a/tests/components/ltr501/test.esp32-c3-ard.yaml b/tests/components/ltr501/test.esp32-c3-ard.yaml deleted file mode 100644 index 9e7de2768d..0000000000 --- a/tests/components/ltr501/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_ltr501 - scl: 5 - sda: 4 - -<<: !include common.yaml diff --git a/tests/components/ltr_als_ps/test.esp32-ard.yaml b/tests/components/ltr_als_ps/test.esp32-ard.yaml deleted file mode 100644 index 2349292a64..0000000000 --- a/tests/components/ltr_als_ps/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_als_ps - scl: 16 - sda: 17 - -<<: !include common.yaml diff --git a/tests/components/ltr_als_ps/test.esp32-c3-ard.yaml b/tests/components/ltr_als_ps/test.esp32-c3-ard.yaml deleted file mode 100644 index d64d70f018..0000000000 --- a/tests/components/ltr_als_ps/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_als_ps - scl: 5 - sda: 4 - -<<: !include common.yaml diff --git a/tests/components/lvgl/test.esp32-ard.yaml b/tests/components/lvgl/test.esp32-ard.yaml deleted file mode 100644 index f85bedbde6..0000000000 --- a/tests/components/lvgl/test.esp32-ard.yaml +++ /dev/null @@ -1,58 +0,0 @@ -spi: - clk_pin: 14 - mosi_pin: 13 - -i2c: - sda: GPIO18 - scl: GPIO19 - -display: - - platform: ili9xxx - model: st7789v - id: tft_display - dimensions: - width: 240 - height: 320 - transform: - swap_xy: false - mirror_x: true - mirror_y: true - data_rate: 80MHz - cs_pin: GPIO22 - dc_pin: GPIO21 - auto_clear_enabled: false - invert_colors: false - update_interval: never - -binary_sensor: - - platform: gpio - internal: true - id: up_button - pin: - number: GPIO38 - inverted: true - - platform: gpio - internal: true - id: down_button - pin: - number: GPIO37 - inverted: true - - platform: gpio - internal: true - id: select_button - pin: - number: GPIO39 - inverted: true -lvgl: - draw_rounding: 8 - encoders: - group: switches - initial_focus: button_button - enter_button: select_button - sensor: - left_button: up_button - right_button: down_button - -packages: - lvgl: !include lvgl-package.yaml - xvgl: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.esp32-ard.yaml b/tests/components/m5stack_8angle/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/m5stack_8angle/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.esp32-c3-ard.yaml b/tests/components/m5stack_8angle/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/m5stack_8angle/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mapping/test.esp32-ard.yaml b/tests/components/mapping/test.esp32-ard.yaml deleted file mode 100644 index a76bf9349b..0000000000 --- a/tests/components/mapping/test.esp32-ard.yaml +++ /dev/null @@ -1,17 +0,0 @@ -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 - -packages: - map: !include common.yaml - -display: - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 12 - dc_pin: 13 - reset_pin: 21 - invert_colors: false diff --git a/tests/components/mapping/test.esp32-c3-ard.yaml b/tests/components/mapping/test.esp32-c3-ard.yaml deleted file mode 100644 index f95dd4f30d..0000000000 --- a/tests/components/mapping/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,17 +0,0 @@ -spi: - - id: spi_main_lcd - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 - -display: - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - invert_colors: false - -packages: - map: !include common.yaml diff --git a/tests/components/matrix_keypad/test.esp32-ard.yaml b/tests/components/matrix_keypad/test.esp32-ard.yaml deleted file mode 100644 index 70bb70638d..0000000000 --- a/tests/components/matrix_keypad/test.esp32-ard.yaml +++ /dev/null @@ -1,15 +0,0 @@ -packages: - common: !include common.yaml - -matrix_keypad: - id: keypad - rows: - - pin: 12 - - pin: 13 - columns: - - pin: 14 - - pin: 15 - keys: "1234" - has_pulldowns: true - on_key: - - lambda: ESP_LOGI("KEY", "key %d pressed", x); diff --git a/tests/components/matrix_keypad/test.esp32-c3-ard.yaml b/tests/components/matrix_keypad/test.esp32-c3-ard.yaml deleted file mode 100644 index 75d9c0b263..0000000000 --- a/tests/components/matrix_keypad/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,15 +0,0 @@ -packages: - common: !include common.yaml - -matrix_keypad: - id: keypad - rows: - - pin: 1 - - pin: 2 - columns: - - pin: 3 - - pin: 4 - keys: "1234" - has_pulldowns: true - on_key: - - lambda: ESP_LOGI("KEY", "key %d pressed", x); diff --git a/tests/components/max17043/test.esp32-ard.yaml b/tests/components/max17043/test.esp32-ard.yaml deleted file mode 100644 index c6615f51cd..0000000000 --- a/tests/components/max17043/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - sda_pin: GPIO21 - scl_pin: GPIO22 - -<<: !include common.yaml diff --git a/tests/components/max17043/test.esp32-c3-ard.yaml b/tests/components/max17043/test.esp32-c3-ard.yaml deleted file mode 100644 index 9a1477d4b9..0000000000 --- a/tests/components/max17043/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - sda_pin: GPIO8 - scl_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/max31855/test.esp32-ard.yaml b/tests/components/max31855/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/max31855/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/max31855/test.esp32-c3-ard.yaml b/tests/components/max31855/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/max31855/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/max31856/test.esp32-ard.yaml b/tests/components/max31856/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/max31856/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/max31856/test.esp32-c3-ard.yaml b/tests/components/max31856/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/max31856/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/max31865/test.esp32-ard.yaml b/tests/components/max31865/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/max31865/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/max31865/test.esp32-c3-ard.yaml b/tests/components/max31865/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/max31865/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/max44009/test.esp32-ard.yaml b/tests/components/max44009/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/max44009/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/max44009/test.esp32-c3-ard.yaml b/tests/components/max44009/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/max44009/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/max6675/test.esp32-ard.yaml b/tests/components/max6675/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/max6675/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/max6675/test.esp32-c3-ard.yaml b/tests/components/max6675/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/max6675/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/max6956/test.esp32-ard.yaml b/tests/components/max6956/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/max6956/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/max6956/test.esp32-c3-ard.yaml b/tests/components/max6956/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/max6956/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/max7219/test.esp32-ard.yaml b/tests/components/max7219/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/max7219/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/max7219/test.esp32-c3-ard.yaml b/tests/components/max7219/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/max7219/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/max7219digit/test.esp32-ard.yaml b/tests/components/max7219digit/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/max7219digit/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/max7219digit/test.esp32-c3-ard.yaml b/tests/components/max7219digit/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/max7219digit/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/max9611/test.esp32-ard.yaml b/tests/components/max9611/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/max9611/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/max9611/test.esp32-c3-ard.yaml b/tests/components/max9611/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/max9611/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mcp23008/test.esp32-ard.yaml b/tests/components/mcp23008/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mcp23008/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mcp23008/test.esp32-c3-ard.yaml b/tests/components/mcp23008/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mcp23008/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mcp23016/test.esp32-ard.yaml b/tests/components/mcp23016/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mcp23016/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mcp23016/test.esp32-c3-ard.yaml b/tests/components/mcp23016/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mcp23016/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mcp23017/test.esp32-ard.yaml b/tests/components/mcp23017/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mcp23017/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mcp23017/test.esp32-c3-ard.yaml b/tests/components/mcp23017/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mcp23017/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mcp23s08/test.esp32-ard.yaml b/tests/components/mcp23s08/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/mcp23s08/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/mcp23s08/test.esp32-c3-ard.yaml b/tests/components/mcp23s08/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/mcp23s08/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/mcp23s17/test.esp32-ard.yaml b/tests/components/mcp23s17/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/mcp23s17/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/mcp23s17/test.esp32-c3-ard.yaml b/tests/components/mcp23s17/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/mcp23s17/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/mcp2515/test.esp32-ard.yaml b/tests/components/mcp2515/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/mcp2515/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/mcp2515/test.esp32-c3-ard.yaml b/tests/components/mcp2515/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/mcp2515/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/mcp3008/test.esp32-ard.yaml b/tests/components/mcp3008/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/mcp3008/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/mcp3008/test.esp32-c3-ard.yaml b/tests/components/mcp3008/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/mcp3008/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/mcp3204/test.esp32-ard.yaml b/tests/components/mcp3204/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/mcp3204/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/mcp3204/test.esp32-c3-ard.yaml b/tests/components/mcp3204/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/mcp3204/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/mcp4461/test.esp32-ard.yaml b/tests/components/mcp4461/test.esp32-ard.yaml deleted file mode 100644 index c5deb7ca0a..0000000000 --- a/tests/components/mcp4461/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - sda_pin: GPIO16 - scl_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mcp4461/test.esp32-c3-ard.yaml b/tests/components/mcp4461/test.esp32-c3-ard.yaml deleted file mode 100644 index a87353b78b..0000000000 --- a/tests/components/mcp4461/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - sda_pin: GPIO4 - scl_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/mcp4725/test.esp32-ard.yaml b/tests/components/mcp4725/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mcp4725/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mcp4725/test.esp32-c3-ard.yaml b/tests/components/mcp4725/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mcp4725/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mcp4728/test.esp32-ard.yaml b/tests/components/mcp4728/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mcp4728/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mcp4728/test.esp32-c3-ard.yaml b/tests/components/mcp4728/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mcp4728/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mcp47a1/test.esp32-ard.yaml b/tests/components/mcp47a1/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mcp47a1/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mcp47a1/test.esp32-c3-ard.yaml b/tests/components/mcp47a1/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mcp47a1/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mcp9600/test.esp32-ard.yaml b/tests/components/mcp9600/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mcp9600/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mcp9600/test.esp32-c3-ard.yaml b/tests/components/mcp9600/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mcp9600/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mcp9808/test.esp32-ard.yaml b/tests/components/mcp9808/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mcp9808/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mcp9808/test.esp32-c3-ard.yaml b/tests/components/mcp9808/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mcp9808/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/media_player/test.esp32-ard.yaml b/tests/components/media_player/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/media_player/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mhz19/test.esp32-ard.yaml b/tests/components/mhz19/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/mhz19/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/mhz19/test.esp32-c3-ard.yaml b/tests/components/mhz19/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/mhz19/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/micronova/test.esp32-ard.yaml b/tests/components/micronova/test.esp32-ard.yaml deleted file mode 100644 index 35d041e047..0000000000 --- a/tests/components/micronova/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - enable_rx_pin: GPIO13 - -<<: !include common.yaml diff --git a/tests/components/micronova/test.esp32-c3-ard.yaml b/tests/components/micronova/test.esp32-c3-ard.yaml deleted file mode 100644 index 993071999f..0000000000 --- a/tests/components/micronova/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - enable_rx_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/microphone/test.esp32-ard.yaml b/tests/components/microphone/test.esp32-ard.yaml deleted file mode 100644 index 392df582cc..0000000000 --- a/tests/components/microphone/test.esp32-ard.yaml +++ /dev/null @@ -1,13 +0,0 @@ -substitutions: - i2s_bclk_pin: GPIO15 - i2s_lrclk_pin: GPIO16 - i2s_mclk_pin: GPIO17 - i2s_din_pin: GPIO33 - -<<: !include common.yaml - -microphone: - - platform: i2s_audio - id: mic_id_adc - adc_pin: 32 - adc_type: internal diff --git a/tests/components/microphone/test.esp32-c3-ard.yaml b/tests/components/microphone/test.esp32-c3-ard.yaml deleted file mode 100644 index c28dc553f5..0000000000 --- a/tests/components/microphone/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - i2s_bclk_pin: GPIO6 - i2s_lrclk_pin: GPIO7 - i2s_mclk_pin: GPIO8 - i2s_din_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/mics_4514/test.esp32-ard.yaml b/tests/components/mics_4514/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mics_4514/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mics_4514/test.esp32-c3-ard.yaml b/tests/components/mics_4514/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mics_4514/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/midea_ir/test.esp32-ard.yaml b/tests/components/midea_ir/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/midea_ir/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/midea_ir/test.esp32-c3-ard.yaml b/tests/components/midea_ir/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/midea_ir/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mipi_spi/test.esp32-ard.yaml b/tests/components/mipi_spi/test.esp32-ard.yaml deleted file mode 100644 index a5ef77dabc..0000000000 --- a/tests/components/mipi_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,15 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - dc_pin: GPIO14 - cs_pin: GPIO13 - enable_pin: GPIO19 - reset_pin: GPIO20 - -display: - - platform: mipi_spi - model: LANBON-L8 - -packages: - display: !include common.yaml diff --git a/tests/components/mipi_spi/test.esp32-c3-ard.yaml b/tests/components/mipi_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index c17748c569..0000000000 --- a/tests/components/mipi_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,10 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - dc_pin: GPIO21 - cs_pin: GPIO18 - enable_pin: GPIO19 - reset_pin: GPIO20 - -<<: !include common.yaml diff --git a/tests/components/mitsubishi/test.esp32-ard.yaml b/tests/components/mitsubishi/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/mitsubishi/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mitsubishi/test.esp32-c3-ard.yaml b/tests/components/mitsubishi/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/mitsubishi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mixer/test.esp32-ard.yaml b/tests/components/mixer/test.esp32-ard.yaml deleted file mode 100644 index 96d2d37458..0000000000 --- a/tests/components/mixer/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - lrclk_pin: GPIO16 - bclk_pin: GPIO17 - mclk_pin: GPIO15 - dout_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/mixer/test.esp32-c3-ard.yaml b/tests/components/mixer/test.esp32-c3-ard.yaml deleted file mode 100644 index f1721f0862..0000000000 --- a/tests/components/mixer/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - lrclk_pin: GPIO4 - bclk_pin: GPIO5 - mclk_pin: GPIO6 - dout_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/mixer/test.esp32-s3-ard.yaml b/tests/components/mixer/test.esp32-s3-ard.yaml deleted file mode 100644 index f1721f0862..0000000000 --- a/tests/components/mixer/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - lrclk_pin: GPIO4 - bclk_pin: GPIO5 - mclk_pin: GPIO6 - dout_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp32-ard.yaml b/tests/components/mlx90393/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mlx90393/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp32-c3-ard.yaml b/tests/components/mlx90393/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mlx90393/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp32-s3-ard.yaml b/tests/components/mlx90393/test.esp32-s3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mlx90393/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mlx90614/test.esp32-ard.yaml b/tests/components/mlx90614/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mlx90614/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mlx90614/test.esp32-c3-ard.yaml b/tests/components/mlx90614/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mlx90614/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mmc5603/test.esp32-ard.yaml b/tests/components/mmc5603/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mmc5603/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mmc5603/test.esp32-c3-ard.yaml b/tests/components/mmc5603/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mmc5603/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mmc5983/test.esp32-ard.yaml b/tests/components/mmc5983/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mmc5983/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mmc5983/test.esp32-c3-ard.yaml b/tests/components/mmc5983/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mmc5983/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/modbus/test.esp32-ard.yaml b/tests/components/modbus/test.esp32-ard.yaml deleted file mode 100644 index bd767a8ece..0000000000 --- a/tests/components/modbus/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 - flow_control_pin: GPIO13 - -<<: !include common.yaml diff --git a/tests/components/modbus/test.esp32-c3-ard.yaml b/tests/components/modbus/test.esp32-c3-ard.yaml deleted file mode 100644 index 452031a5aa..0000000000 --- a/tests/components/modbus/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - flow_control_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/modbus_controller/test.esp32-ard.yaml b/tests/components/modbus_controller/test.esp32-ard.yaml deleted file mode 100644 index 548b8c0666..0000000000 --- a/tests/components/modbus_controller/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - client_tx_pin: GPIO12 - client_rx_pin: GPIO14 - server_tx_pin: GPIO16 - server_rx_pin: GPIO17 - flow_control_pin: GPIO13 - -<<: !include common.yaml diff --git a/tests/components/modbus_controller/test.esp32-c3-ard.yaml b/tests/components/modbus_controller/test.esp32-c3-ard.yaml deleted file mode 100644 index f5b770ff58..0000000000 --- a/tests/components/modbus_controller/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - client_tx_pin: GPIO4 - client_rx_pin: GPIO5 - server_tx_pin: GPIO6 - server_rx_pin: GPIO7 - flow_control_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/monochromatic/test.esp32-ard.yaml b/tests/components/monochromatic/test.esp32-ard.yaml deleted file mode 100644 index feabf013fd..0000000000 --- a/tests/components/monochromatic/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - light_platform: ledc - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/monochromatic/test.esp32-c3-ard.yaml b/tests/components/monochromatic/test.esp32-c3-ard.yaml deleted file mode 100644 index feabf013fd..0000000000 --- a/tests/components/monochromatic/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - light_platform: ledc - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/mopeka_ble/test.esp32-ard.yaml b/tests/components/mopeka_ble/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/mopeka_ble/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mopeka_ble/test.esp32-c3-ard.yaml b/tests/components/mopeka_ble/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/mopeka_ble/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mopeka_pro_check/test.esp32-ard.yaml b/tests/components/mopeka_pro_check/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/mopeka_pro_check/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mopeka_pro_check/test.esp32-c3-ard.yaml b/tests/components/mopeka_pro_check/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/mopeka_pro_check/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mopeka_std_check/test.esp32-ard.yaml b/tests/components/mopeka_std_check/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/mopeka_std_check/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mopeka_std_check/test.esp32-c3-ard.yaml b/tests/components/mopeka_std_check/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/mopeka_std_check/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.esp32-ard.yaml b/tests/components/mpl3115a2/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mpl3115a2/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.esp32-c3-ard.yaml b/tests/components/mpl3115a2/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mpl3115a2/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mpr121/test.esp32-ard.yaml b/tests/components/mpr121/test.esp32-ard.yaml deleted file mode 100644 index 1037d5d35b..0000000000 --- a/tests/components/mpr121/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - i2c_scl: GPIO16 - i2c_sda: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mpr121/test.esp32-c3-ard.yaml b/tests/components/mpr121/test.esp32-c3-ard.yaml deleted file mode 100644 index d7ae0d5161..0000000000 --- a/tests/components/mpr121/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - i2c_scl: GPIO5 - i2c_sda: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mpu6050/test.esp32-ard.yaml b/tests/components/mpu6050/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mpu6050/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mpu6050/test.esp32-c3-ard.yaml b/tests/components/mpu6050/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mpu6050/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mpu6886/test.esp32-ard.yaml b/tests/components/mpu6886/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/mpu6886/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/mpu6886/test.esp32-c3-ard.yaml b/tests/components/mpu6886/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/mpu6886/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/mqtt/test.esp32-ard.yaml b/tests/components/mqtt/test.esp32-ard.yaml deleted file mode 100644 index 4c70fb37d9..0000000000 --- a/tests/components/mqtt/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - verify_ssl: "false" - -packages: - common: !include common.yaml - update: !include common-update.yaml diff --git a/tests/components/mqtt/test.esp32-c3-ard.yaml b/tests/components/mqtt/test.esp32-c3-ard.yaml deleted file mode 100644 index 4c70fb37d9..0000000000 --- a/tests/components/mqtt/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - verify_ssl: "false" - -packages: - common: !include common.yaml - update: !include common-update.yaml diff --git a/tests/components/mqtt_subscribe/test.esp32-ard.yaml b/tests/components/mqtt_subscribe/test.esp32-ard.yaml deleted file mode 100644 index 28589ee06c..0000000000 --- a/tests/components/mqtt_subscribe/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-ard.yaml diff --git a/tests/components/mqtt_subscribe/test.esp32-c3-ard.yaml b/tests/components/mqtt_subscribe/test.esp32-c3-ard.yaml deleted file mode 100644 index 28589ee06c..0000000000 --- a/tests/components/mqtt_subscribe/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-ard.yaml diff --git a/tests/components/ms5611/test.esp32-ard.yaml b/tests/components/ms5611/test.esp32-ard.yaml deleted file mode 100644 index 1037d5d35b..0000000000 --- a/tests/components/ms5611/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - i2c_scl: GPIO16 - i2c_sda: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ms5611/test.esp32-c3-ard.yaml b/tests/components/ms5611/test.esp32-c3-ard.yaml deleted file mode 100644 index d7ae0d5161..0000000000 --- a/tests/components/ms5611/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - i2c_scl: GPIO5 - i2c_sda: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/msa3xx/test.esp32-ard.yaml b/tests/components/msa3xx/test.esp32-ard.yaml deleted file mode 100644 index 7202e7b9bf..0000000000 --- a/tests/components/msa3xx/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_msa3xx - scl: GPIO16 - sda: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/msa3xx/test.esp32-c3-ard.yaml b/tests/components/msa3xx/test.esp32-c3-ard.yaml deleted file mode 100644 index b972ce8cdb..0000000000 --- a/tests/components/msa3xx/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_msa3xx - scl: GPIO5 - sda: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/my9231/test.esp32-ard.yaml b/tests/components/my9231/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/my9231/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/my9231/test.esp32-c3-ard.yaml b/tests/components/my9231/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/my9231/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/nau7802/test.esp32-ard.yaml b/tests/components/nau7802/test.esp32-ard.yaml deleted file mode 100644 index 73a4aa4251..0000000000 --- a/tests/components/nau7802/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_nau7802 - scl: 16 - sda: 17 - -<<: !include common.yaml diff --git a/tests/components/nau7802/test.esp32-c3-ard.yaml b/tests/components/nau7802/test.esp32-c3-ard.yaml deleted file mode 100644 index 769468f9ec..0000000000 --- a/tests/components/nau7802/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -i2c: - - id: i2c_nau7802 - scl: 5 - sda: 4 - -<<: !include common.yaml diff --git a/tests/components/noblex/test.esp32-ard.yaml b/tests/components/noblex/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/noblex/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/noblex/test.esp32-c3-ard.yaml b/tests/components/noblex/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/noblex/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/npi19/test.esp32-ard.yaml b/tests/components/npi19/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/npi19/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/npi19/test.esp32-s3-ard.yaml b/tests/components/npi19/test.esp32-s3-ard.yaml deleted file mode 100644 index 4942e3c2b3..0000000000 --- a/tests/components/npi19/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-ard.yaml b/tests/components/ntc/test.esp32-ard.yaml deleted file mode 100644 index 06864605a6..0000000000 --- a/tests/components/ntc/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO32 - -<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-c3-ard.yaml b/tests/components/ntc/test.esp32-c3-ard.yaml deleted file mode 100644 index 37fb325f4a..0000000000 --- a/tests/components/ntc/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-s2-ard.yaml b/tests/components/ntc/test.esp32-s2-ard.yaml deleted file mode 100644 index 37fb325f4a..0000000000 --- a/tests/components/ntc/test.esp32-s2-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-s3-ard.yaml b/tests/components/ntc/test.esp32-s3-ard.yaml deleted file mode 100644 index 37fb325f4a..0000000000 --- a/tests/components/ntc/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/online_image/test.esp32-ard.yaml b/tests/components/online_image/test.esp32-ard.yaml deleted file mode 100644 index 4111cbd0ad..0000000000 --- a/tests/components/online_image/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -<<: !include common-esp32.yaml - -http_request: - verify_ssl: false diff --git a/tests/components/opt3001/test.esp32-ard.yaml b/tests/components/opt3001/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/opt3001/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/opt3001/test.esp32-c3-ard.yaml b/tests/components/opt3001/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/opt3001/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/output/test.esp32-ard.yaml b/tests/components/output/test.esp32-ard.yaml deleted file mode 100644 index 7687f2a7c8..0000000000 --- a/tests/components/output/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO12 - -<<: !include common.yaml diff --git a/tests/components/output/test.esp32-c3-ard.yaml b/tests/components/output/test.esp32-c3-ard.yaml deleted file mode 100644 index 2227643703..0000000000 --- a/tests/components/output/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO1 - -<<: !include common.yaml diff --git a/tests/components/packages/test.esp32-ard.yaml b/tests/components/packages/test.esp32-ard.yaml deleted file mode 100644 index 9e4ceb09d6..0000000000 --- a/tests/components/packages/test.esp32-ard.yaml +++ /dev/null @@ -1,11 +0,0 @@ -packages: - - sensor: - - platform: template - id: inline_sensor - - !include package.yaml - - github://esphome/esphome/tests/components/template/common.yaml@dev - - url: https://github.com/esphome/esphome - path: tests/components/absolute_humidity - file: common.yaml - ref: dev - refresh: 1d diff --git a/tests/components/packet_transport/test.esp32-ard.yaml b/tests/components/packet_transport/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/packet_transport/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/packet_transport/test.esp32-c3-ard.yaml b/tests/components/packet_transport/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/packet_transport/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/partition/test.esp32-ard.yaml b/tests/components/partition/test.esp32-ard.yaml deleted file mode 100644 index 32cf2a64ba..0000000000 --- a/tests/components/partition/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - light_platform: esp32_rmt_led_strip - pin: GPIO2 - -<<: !include common-ard.yaml diff --git a/tests/components/partition/test.esp32-c3-ard.yaml b/tests/components/partition/test.esp32-c3-ard.yaml deleted file mode 100644 index 32cf2a64ba..0000000000 --- a/tests/components/partition/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - light_platform: esp32_rmt_led_strip - pin: GPIO2 - -<<: !include common-ard.yaml diff --git a/tests/components/pca6416a/test.esp32-ard.yaml b/tests/components/pca6416a/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/pca6416a/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/pca6416a/test.esp32-c3-ard.yaml b/tests/components/pca6416a/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/pca6416a/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/pca9554/test.esp32-ard.yaml b/tests/components/pca9554/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/pca9554/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/pca9554/test.esp32-c3-ard.yaml b/tests/components/pca9554/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/pca9554/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/pca9685/test.esp32-ard.yaml b/tests/components/pca9685/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/pca9685/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/pca9685/test.esp32-c3-ard.yaml b/tests/components/pca9685/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/pca9685/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/pcd8544/test.esp32-ard.yaml b/tests/components/pcd8544/test.esp32-ard.yaml deleted file mode 100644 index 09e9db5a38..0000000000 --- a/tests/components/pcd8544/test.esp32-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO18 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/pcd8544/test.esp32-c3-ard.yaml b/tests/components/pcd8544/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/pcd8544/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/pcf85063/test.esp32-ard.yaml b/tests/components/pcf85063/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/pcf85063/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/pcf85063/test.esp32-c3-ard.yaml b/tests/components/pcf85063/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/pcf85063/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/pcf8563/test.esp32-ard.yaml b/tests/components/pcf8563/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/pcf8563/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/pcf8563/test.esp32-c3-ard.yaml b/tests/components/pcf8563/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/pcf8563/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/pcf8574/test.esp32-ard.yaml b/tests/components/pcf8574/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/pcf8574/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/pcf8574/test.esp32-c3-ard.yaml b/tests/components/pcf8574/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/pcf8574/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/pi4ioe5v6408/test.esp32-ard.yaml b/tests/components/pi4ioe5v6408/test.esp32-ard.yaml deleted file mode 100644 index 55e6edfbf3..0000000000 --- a/tests/components/pi4ioe5v6408/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - i2c_sda: GPIO21 - i2c_scl: GPIO22 - -<<: !include common.yaml diff --git a/tests/components/pid/test.esp32-ard.yaml b/tests/components/pid/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pid/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pid/test.esp32-c3-ard.yaml b/tests/components/pid/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pid/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pipsolar/test.esp32-ard.yaml b/tests/components/pipsolar/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/pipsolar/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/pipsolar/test.esp32-c3-ard.yaml b/tests/components/pipsolar/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/pipsolar/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/pm1006/test.esp32-ard.yaml b/tests/components/pm1006/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/pm1006/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/pm1006/test.esp32-c3-ard.yaml b/tests/components/pm1006/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/pm1006/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/pm2005/test.esp32-ard.yaml b/tests/components/pm2005/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/pm2005/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/pm2005/test.esp32-c3-ard.yaml b/tests/components/pm2005/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/pm2005/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/pmsa003i/test.esp32-ard.yaml b/tests/components/pmsa003i/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/pmsa003i/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/pmsa003i/test.esp32-c3-ard.yaml b/tests/components/pmsa003i/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/pmsa003i/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/pmsx003/test.esp32-ard.yaml b/tests/components/pmsx003/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/pmsx003/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/pmsx003/test.esp32-c3-ard.yaml b/tests/components/pmsx003/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/pmsx003/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/pmwcs3/test.esp32-ard.yaml b/tests/components/pmwcs3/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/pmwcs3/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/pmwcs3/test.esp32-c3-ard.yaml b/tests/components/pmwcs3/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/pmwcs3/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.esp32-ard.yaml b/tests/components/pn532_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/pn532_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.esp32-c3-ard.yaml b/tests/components/pn532_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/pn532_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/pn532_spi/test.esp32-ard.yaml b/tests/components/pn532_spi/test.esp32-ard.yaml deleted file mode 100644 index bce56f398a..0000000000 --- a/tests/components/pn532_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO18 - cs_pin: GPIO12 - -<<: !include common.yaml diff --git a/tests/components/pn532_spi/test.esp32-c3-ard.yaml b/tests/components/pn532_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/pn532_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.esp32-ard.yaml b/tests/components/pn7150_i2c/test.esp32-ard.yaml deleted file mode 100644 index 1643bec317..0000000000 --- a/tests/components/pn7150_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - irq_pin: GPIO14 - ven_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.esp32-c3-ard.yaml b/tests/components/pn7150_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index 2067143411..0000000000 --- a/tests/components/pn7150_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - irq_pin: GPIO6 - ven_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.esp32-ard.yaml b/tests/components/pn7160_i2c/test.esp32-ard.yaml deleted file mode 100644 index 1643bec317..0000000000 --- a/tests/components/pn7160_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - irq_pin: GPIO14 - ven_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.esp32-c3-ard.yaml b/tests/components/pn7160_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index 2067143411..0000000000 --- a/tests/components/pn7160_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - irq_pin: GPIO6 - ven_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.esp32-ard.yaml b/tests/components/pn7160_spi/test.esp32-ard.yaml deleted file mode 100644 index f6073d0416..0000000000 --- a/tests/components/pn7160_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO18 - cs_pin: GPIO12 - irq_pin: GPIO13 - ven_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.esp32-c3-ard.yaml b/tests/components/pn7160_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index f8a07fad2f..0000000000 --- a/tests/components/pn7160_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - irq_pin: GPIO9 - ven_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/power_supply/test.esp32-ard.yaml b/tests/components/power_supply/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/power_supply/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/power_supply/test.esp32-c3-ard.yaml b/tests/components/power_supply/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/power_supply/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/prometheus/test.esp32-ard.yaml b/tests/components/prometheus/test.esp32-ard.yaml deleted file mode 100644 index 9eedaabd82..0000000000 --- a/tests/components/prometheus/test.esp32-ard.yaml +++ /dev/null @@ -1,38 +0,0 @@ -substitutions: - verify_ssl: "false" - pin: GPIO5 - -<<: !include common.yaml - -i2s_audio: - i2s_lrclk_pin: 1 - i2s_bclk_pin: 2 - i2s_mclk_pin: 3 - -media_player: - - platform: i2s_audio - name: "Media Player" - dac_type: external - i2s_dout_pin: 18 - mute_pin: 19 - on_state: - - media_player.play: - - media_player.play_media: http://localhost/media.mp3 - - media_player.play_media: !lambda 'return "http://localhost/media.mp3";' - on_idle: - - media_player.pause: - on_play: - - media_player.stop: - on_pause: - - media_player.toggle: - - wait_until: - media_player.is_idle: - - wait_until: - media_player.is_playing: - - wait_until: - media_player.is_announcing: - - wait_until: - media_player.is_paused: - - media_player.volume_up: - - media_player.volume_down: - - media_player.volume_set: 50% diff --git a/tests/components/prometheus/test.esp32-c3-ard.yaml b/tests/components/prometheus/test.esp32-c3-ard.yaml deleted file mode 100644 index f00bca5947..0000000000 --- a/tests/components/prometheus/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - verify_ssl: "false" - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/psram/test.esp32-ard.yaml b/tests/components/psram/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/psram/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/psram/test.esp32-s2-ard.yaml b/tests/components/psram/test.esp32-s2-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/psram/test.esp32-s2-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/psram/test.esp32-s3-ard.yaml b/tests/components/psram/test.esp32-s3-ard.yaml deleted file mode 100644 index cfd39f77fe..0000000000 --- a/tests/components/psram/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,3 +0,0 @@ -psram: - mode: octal - speed: 80MHz diff --git a/tests/components/pulse_counter/test.esp32-ard.yaml b/tests/components/pulse_counter/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pulse_counter/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pulse_counter/test.esp32-c3-ard.yaml b/tests/components/pulse_counter/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pulse_counter/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pulse_meter/test.esp32-ard.yaml b/tests/components/pulse_meter/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pulse_meter/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pulse_meter/test.esp32-c3-ard.yaml b/tests/components/pulse_meter/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pulse_meter/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pulse_width/test.esp32-ard.yaml b/tests/components/pulse_width/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pulse_width/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pulse_width/test.esp32-c3-ard.yaml b/tests/components/pulse_width/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pulse_width/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pvvx_mithermometer/test.esp32-ard.yaml b/tests/components/pvvx_mithermometer/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pvvx_mithermometer/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pvvx_mithermometer/test.esp32-c3-ard.yaml b/tests/components/pvvx_mithermometer/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pvvx_mithermometer/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pylontech/test.esp32-ard.yaml b/tests/components/pylontech/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/pylontech/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/pylontech/test.esp32-c3-ard.yaml b/tests/components/pylontech/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/pylontech/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/pzem004t/test.esp32-ard.yaml b/tests/components/pzem004t/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/pzem004t/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/pzem004t/test.esp32-c3-ard.yaml b/tests/components/pzem004t/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/pzem004t/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/pzemac/test.esp32-ard.yaml b/tests/components/pzemac/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/pzemac/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/pzemac/test.esp32-c3-ard.yaml b/tests/components/pzemac/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/pzemac/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/pzemdc/test.esp32-ard.yaml b/tests/components/pzemdc/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/pzemdc/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/pzemdc/test.esp32-c3-ard.yaml b/tests/components/pzemdc/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/pzemdc/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/qmc5883l/test.esp32-ard.yaml b/tests/components/qmc5883l/test.esp32-ard.yaml deleted file mode 100644 index 2cf2041501..0000000000 --- a/tests/components/qmc5883l/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - drdy_pin: GPIO18 - -<<: !include common.yaml diff --git a/tests/components/qmc5883l/test.esp32-c3-ard.yaml b/tests/components/qmc5883l/test.esp32-c3-ard.yaml deleted file mode 100644 index 677501d15a..0000000000 --- a/tests/components/qmc5883l/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - drdy_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/qmp6988/test.esp32-ard.yaml b/tests/components/qmp6988/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/qmp6988/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/qmp6988/test.esp32-c3-ard.yaml b/tests/components/qmp6988/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/qmp6988/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/qr_code/test.esp32-ard.yaml b/tests/components/qr_code/test.esp32-ard.yaml deleted file mode 100644 index bad5241f79..0000000000 --- a/tests/components/qr_code/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/qr_code/test.esp32-c3-ard.yaml b/tests/components/qr_code/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/qr_code/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.esp32-ard.yaml b/tests/components/qwiic_pir/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/qwiic_pir/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.esp32-c3-ard.yaml b/tests/components/qwiic_pir/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/qwiic_pir/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/radon_eye_ble/test.esp32-ard.yaml b/tests/components/radon_eye_ble/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/radon_eye_ble/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/radon_eye_ble/test.esp32-c3-ard.yaml b/tests/components/radon_eye_ble/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/radon_eye_ble/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/radon_eye_rd200/test.esp32-ard.yaml b/tests/components/radon_eye_rd200/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/radon_eye_rd200/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/radon_eye_rd200/test.esp32-c3-ard.yaml b/tests/components/radon_eye_rd200/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/radon_eye_rd200/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.esp32-ard.yaml b/tests/components/rc522_i2c/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/rc522_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.esp32-c3-ard.yaml b/tests/components/rc522_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/rc522_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/rc522_spi/test.esp32-ard.yaml b/tests/components/rc522_spi/test.esp32-ard.yaml deleted file mode 100644 index 54e027a614..0000000000 --- a/tests/components/rc522_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/rc522_spi/test.esp32-c3-ard.yaml b/tests/components/rc522_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/rc522_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/rdm6300/test.esp32-ard.yaml b/tests/components/rdm6300/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/rdm6300/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/rdm6300/test.esp32-c3-ard.yaml b/tests/components/rdm6300/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/rdm6300/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/remote_receiver/test.esp32-ard.yaml b/tests/components/remote_receiver/test.esp32-ard.yaml deleted file mode 100644 index 10dd767598..0000000000 --- a/tests/components/remote_receiver/test.esp32-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - pin: GPIO2 - clock_resolution: "2000000" - filter_symbols: "2" - receive_symbols: "4" - rmt_symbols: "64" - -packages: - common: !include esp32-common.yaml diff --git a/tests/components/remote_receiver/test.esp32-c3-ard.yaml b/tests/components/remote_receiver/test.esp32-c3-ard.yaml deleted file mode 100644 index 10dd767598..0000000000 --- a/tests/components/remote_receiver/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - pin: GPIO2 - clock_resolution: "2000000" - filter_symbols: "2" - receive_symbols: "4" - rmt_symbols: "64" - -packages: - common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp32-ard.yaml b/tests/components/remote_transmitter/test.esp32-ard.yaml deleted file mode 100644 index 0522f4d181..0000000000 --- a/tests/components/remote_transmitter/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - pin: GPIO2 - clock_resolution: "2000000" - rmt_symbols: "64" - -packages: - common: !include esp32-common.yaml diff --git a/tests/components/remote_transmitter/test.esp32-c3-ard.yaml b/tests/components/remote_transmitter/test.esp32-c3-ard.yaml deleted file mode 100644 index 0522f4d181..0000000000 --- a/tests/components/remote_transmitter/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - pin: GPIO2 - clock_resolution: "2000000" - rmt_symbols: "64" - -packages: - common: !include esp32-common.yaml diff --git a/tests/components/resampler/test.esp32-ard.yaml b/tests/components/resampler/test.esp32-ard.yaml deleted file mode 100644 index 96d2d37458..0000000000 --- a/tests/components/resampler/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - lrclk_pin: GPIO16 - bclk_pin: GPIO17 - mclk_pin: GPIO15 - dout_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/resampler/test.esp32-c3-ard.yaml b/tests/components/resampler/test.esp32-c3-ard.yaml deleted file mode 100644 index f1721f0862..0000000000 --- a/tests/components/resampler/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - lrclk_pin: GPIO4 - bclk_pin: GPIO5 - mclk_pin: GPIO6 - dout_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/resampler/test.esp32-s3-ard.yaml b/tests/components/resampler/test.esp32-s3-ard.yaml deleted file mode 100644 index f1721f0862..0000000000 --- a/tests/components/resampler/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - lrclk_pin: GPIO4 - bclk_pin: GPIO5 - mclk_pin: GPIO6 - dout_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-ard.yaml b/tests/components/resistance/test.esp32-ard.yaml deleted file mode 100644 index 06864605a6..0000000000 --- a/tests/components/resistance/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO32 - -<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-c3-ard.yaml b/tests/components/resistance/test.esp32-c3-ard.yaml deleted file mode 100644 index 37fb325f4a..0000000000 --- a/tests/components/resistance/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-s2-ard.yaml b/tests/components/resistance/test.esp32-s2-ard.yaml deleted file mode 100644 index 1910f325ae..0000000000 --- a/tests/components/resistance/test.esp32-s2-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO1 - -<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-s3-ard.yaml b/tests/components/resistance/test.esp32-s3-ard.yaml deleted file mode 100644 index 1910f325ae..0000000000 --- a/tests/components/resistance/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO1 - -<<: !include common.yaml diff --git a/tests/components/restart/test.esp32-ard.yaml b/tests/components/restart/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/restart/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/restart/test.esp32-c3-ard.yaml b/tests/components/restart/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/restart/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/rf_bridge/test.esp32-ard.yaml b/tests/components/rf_bridge/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/rf_bridge/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/rf_bridge/test.esp32-c3-ard.yaml b/tests/components/rf_bridge/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/rf_bridge/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/rgb/test.esp32-ard.yaml b/tests/components/rgb/test.esp32-ard.yaml deleted file mode 100644 index d78ccec952..0000000000 --- a/tests/components/rgb/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO12 - pin2: GPIO13 - pin3: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/rgb/test.esp32-c3-ard.yaml b/tests/components/rgb/test.esp32-c3-ard.yaml deleted file mode 100644 index 1fe4a4bb90..0000000000 --- a/tests/components/rgb/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO2 - pin2: GPIO3 - pin3: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/rgbct/test.esp32-ard.yaml b/tests/components/rgbct/test.esp32-ard.yaml deleted file mode 100644 index 1ecc626e9c..0000000000 --- a/tests/components/rgbct/test.esp32-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO12 - pin2: GPIO13 - pin3: GPIO14 - pin4: GPIO15 - pin5: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/rgbct/test.esp32-c3-ard.yaml b/tests/components/rgbct/test.esp32-c3-ard.yaml deleted file mode 100644 index 27a1fbca4d..0000000000 --- a/tests/components/rgbct/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO2 - pin2: GPIO3 - pin3: GPIO4 - pin4: GPIO5 - pin5: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/rgbw/test.esp32-ard.yaml b/tests/components/rgbw/test.esp32-ard.yaml deleted file mode 100644 index ea8efd1a34..0000000000 --- a/tests/components/rgbw/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO12 - pin2: GPIO13 - pin3: GPIO14 - pin4: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/rgbw/test.esp32-c3-ard.yaml b/tests/components/rgbw/test.esp32-c3-ard.yaml deleted file mode 100644 index b44734344e..0000000000 --- a/tests/components/rgbw/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO2 - pin2: GPIO3 - pin3: GPIO4 - pin4: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/rgbww/test.esp32-ard.yaml b/tests/components/rgbww/test.esp32-ard.yaml deleted file mode 100644 index 1ecc626e9c..0000000000 --- a/tests/components/rgbww/test.esp32-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO12 - pin2: GPIO13 - pin3: GPIO14 - pin4: GPIO15 - pin5: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/rgbww/test.esp32-c3-ard.yaml b/tests/components/rgbww/test.esp32-c3-ard.yaml deleted file mode 100644 index 27a1fbca4d..0000000000 --- a/tests/components/rgbww/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO2 - pin2: GPIO3 - pin3: GPIO4 - pin4: GPIO5 - pin5: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/rotary_encoder/test.esp32-ard.yaml b/tests/components/rotary_encoder/test.esp32-ard.yaml deleted file mode 100644 index 48a624aa37..0000000000 --- a/tests/components/rotary_encoder/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - pin_a: GPIO12 - pin_b: GPIO13 - pin_reset: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/rotary_encoder/test.esp32-c3-ard.yaml b/tests/components/rotary_encoder/test.esp32-c3-ard.yaml deleted file mode 100644 index b71a454bdd..0000000000 --- a/tests/components/rotary_encoder/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - pin_a: GPIO2 - pin_b: GPIO3 - pin_reset: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/rtttl/test.esp32-ard.yaml b/tests/components/rtttl/test.esp32-ard.yaml deleted file mode 100644 index 26da1ce1d6..0000000000 --- a/tests/components/rtttl/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/rtttl/test.esp32-c3-ard.yaml b/tests/components/rtttl/test.esp32-c3-ard.yaml deleted file mode 100644 index 7476963591..0000000000 --- a/tests/components/rtttl/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/runtime_stats/test.esp32-ard.yaml b/tests/components/runtime_stats/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/runtime_stats/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ruuvi_ble/test.esp32-ard.yaml b/tests/components/ruuvi_ble/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ruuvi_ble/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ruuvi_ble/test.esp32-c3-ard.yaml b/tests/components/ruuvi_ble/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ruuvi_ble/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ruuvitag/test.esp32-ard.yaml b/tests/components/ruuvitag/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ruuvitag/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ruuvitag/test.esp32-c3-ard.yaml b/tests/components/ruuvitag/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ruuvitag/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/safe_mode/test-enabled.esp32-ard.yaml b/tests/components/safe_mode/test-enabled.esp32-ard.yaml deleted file mode 100644 index 97fd63d70e..0000000000 --- a/tests/components/safe_mode/test-enabled.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-enabled.yaml diff --git a/tests/components/safe_mode/test-enabled.esp32-c3-ard.yaml b/tests/components/safe_mode/test-enabled.esp32-c3-ard.yaml deleted file mode 100644 index 97fd63d70e..0000000000 --- a/tests/components/safe_mode/test-enabled.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-enabled.yaml diff --git a/tests/components/scd30/test.esp32-ard.yaml b/tests/components/scd30/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/scd30/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/scd30/test.esp32-c3-ard.yaml b/tests/components/scd30/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/scd30/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/scd4x/test.esp32-ard.yaml b/tests/components/scd4x/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/scd4x/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/scd4x/test.esp32-c3-ard.yaml b/tests/components/scd4x/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/scd4x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/script/test.esp32-ard.yaml b/tests/components/script/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/script/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/script/test.esp32-c3-ard.yaml b/tests/components/script/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/script/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sdm_meter/test.esp32-ard.yaml b/tests/components/sdm_meter/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/sdm_meter/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/sdm_meter/test.esp32-c3-ard.yaml b/tests/components/sdm_meter/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/sdm_meter/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/sdp3x/test.esp32-ard.yaml b/tests/components/sdp3x/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sdp3x/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sdp3x/test.esp32-c3-ard.yaml b/tests/components/sdp3x/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sdp3x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sds011/test.esp32-ard.yaml b/tests/components/sds011/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/sds011/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/sds011/test.esp32-c3-ard.yaml b/tests/components/sds011/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/sds011/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/seeed_mr24hpc1/test.esp32-c3-ard.yaml b/tests/components/seeed_mr24hpc1/test.esp32-c3-ard.yaml deleted file mode 100644 index 4fb884abf4..0000000000 --- a/tests/components/seeed_mr24hpc1/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - uart_tx_pin: GPIO5 - uart_rx_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/seeed_mr60bha2/test.esp32-c3-ard.yaml b/tests/components/seeed_mr60bha2/test.esp32-c3-ard.yaml deleted file mode 100644 index 4fb884abf4..0000000000 --- a/tests/components/seeed_mr60bha2/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - uart_tx_pin: GPIO5 - uart_rx_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/seeed_mr60fda2/test.esp32-c3-ard.yaml b/tests/components/seeed_mr60fda2/test.esp32-c3-ard.yaml deleted file mode 100644 index 4fb884abf4..0000000000 --- a/tests/components/seeed_mr60fda2/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - uart_tx_pin: GPIO5 - uart_rx_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/selec_meter/test.esp32-ard.yaml b/tests/components/selec_meter/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/selec_meter/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/selec_meter/test.esp32-c3-ard.yaml b/tests/components/selec_meter/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/selec_meter/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/sen0321/test.esp32-ard.yaml b/tests/components/sen0321/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sen0321/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sen0321/test.esp32-c3-ard.yaml b/tests/components/sen0321/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sen0321/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sen21231/test.esp32-ard.yaml b/tests/components/sen21231/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sen21231/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sen21231/test.esp32-c3-ard.yaml b/tests/components/sen21231/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sen21231/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sen5x/test.esp32-ard.yaml b/tests/components/sen5x/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sen5x/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sen5x/test.esp32-c3-ard.yaml b/tests/components/sen5x/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sen5x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/senseair/test.esp32-ard.yaml b/tests/components/senseair/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/senseair/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/senseair/test.esp32-c3-ard.yaml b/tests/components/senseair/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/senseair/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/servo/test.esp32-ard.yaml b/tests/components/servo/test.esp32-ard.yaml deleted file mode 100644 index 26da1ce1d6..0000000000 --- a/tests/components/servo/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/servo/test.esp32-c3-ard.yaml b/tests/components/servo/test.esp32-c3-ard.yaml deleted file mode 100644 index 7476963591..0000000000 --- a/tests/components/servo/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sfa30/test.esp32-ard.yaml b/tests/components/sfa30/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sfa30/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sfa30/test.esp32-c3-ard.yaml b/tests/components/sfa30/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sfa30/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sgp30/test.esp32-ard.yaml b/tests/components/sgp30/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sgp30/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sgp30/test.esp32-c3-ard.yaml b/tests/components/sgp30/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sgp30/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sgp4x/test.esp32-ard.yaml b/tests/components/sgp4x/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sgp4x/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sgp4x/test.esp32-c3-ard.yaml b/tests/components/sgp4x/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sgp4x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sht3xd/test.esp32-ard.yaml b/tests/components/sht3xd/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sht3xd/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sht3xd/test.esp32-c3-ard.yaml b/tests/components/sht3xd/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sht3xd/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sht4x/test.esp32-ard.yaml b/tests/components/sht4x/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sht4x/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sht4x/test.esp32-c3-ard.yaml b/tests/components/sht4x/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sht4x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/shtcx/test.esp32-ard.yaml b/tests/components/shtcx/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/shtcx/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/shtcx/test.esp32-c3-ard.yaml b/tests/components/shtcx/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/shtcx/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/shutdown/test.esp32-ard.yaml b/tests/components/shutdown/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/shutdown/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/shutdown/test.esp32-c3-ard.yaml b/tests/components/shutdown/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/shutdown/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sigma_delta_output/test.esp32-ard.yaml b/tests/components/sigma_delta_output/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sigma_delta_output/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sigma_delta_output/test.esp32-c3-ard.yaml b/tests/components/sigma_delta_output/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sigma_delta_output/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sim800l/test.esp32-ard.yaml b/tests/components/sim800l/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/sim800l/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/sim800l/test.esp32-c3-ard.yaml b/tests/components/sim800l/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/sim800l/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/slow_pwm/test.esp32-ard.yaml b/tests/components/slow_pwm/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/slow_pwm/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/slow_pwm/test.esp32-c3-ard.yaml b/tests/components/slow_pwm/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/slow_pwm/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sm16716/test.esp32-ard.yaml b/tests/components/sm16716/test.esp32-ard.yaml deleted file mode 100644 index d295973e3f..0000000000 --- a/tests/components/sm16716/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sm16716/test.esp32-c3-ard.yaml b/tests/components/sm16716/test.esp32-c3-ard.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/sm16716/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sm2135/test.esp32-ard.yaml b/tests/components/sm2135/test.esp32-ard.yaml deleted file mode 100644 index d295973e3f..0000000000 --- a/tests/components/sm2135/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sm2135/test.esp32-c3-ard.yaml b/tests/components/sm2135/test.esp32-c3-ard.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/sm2135/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sm2235/test.esp32-ard.yaml b/tests/components/sm2235/test.esp32-ard.yaml deleted file mode 100644 index d295973e3f..0000000000 --- a/tests/components/sm2235/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sm2235/test.esp32-c3-ard.yaml b/tests/components/sm2235/test.esp32-c3-ard.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/sm2235/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sm2335/test.esp32-ard.yaml b/tests/components/sm2335/test.esp32-ard.yaml deleted file mode 100644 index d295973e3f..0000000000 --- a/tests/components/sm2335/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sm2335/test.esp32-c3-ard.yaml b/tests/components/sm2335/test.esp32-c3-ard.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/sm2335/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sm300d2/test.esp32-ard.yaml b/tests/components/sm300d2/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/sm300d2/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/sm300d2/test.esp32-c3-ard.yaml b/tests/components/sm300d2/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/sm300d2/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/sml/test.esp32-ard.yaml b/tests/components/sml/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/sml/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/sml/test.esp32-c3-ard.yaml b/tests/components/sml/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/sml/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/smt100/test.esp32-ard.yaml b/tests/components/smt100/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/smt100/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/smt100/test.esp32-c3-ard.yaml b/tests/components/smt100/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/smt100/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/sn74hc165/test.esp32-ard.yaml b/tests/components/sn74hc165/test.esp32-ard.yaml deleted file mode 100644 index 27f963312f..0000000000 --- a/tests/components/sn74hc165/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clock_pin: GPIO13 - data_pin: GPIO14 - load_pin: GPIO15 - clock_inhibit_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/sn74hc165/test.esp32-c3-ard.yaml b/tests/components/sn74hc165/test.esp32-c3-ard.yaml deleted file mode 100644 index 0a3db917b7..0000000000 --- a/tests/components/sn74hc165/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clock_pin: GPIO3 - data_pin: GPIO4 - load_pin: GPIO5 - clock_inhibit_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/sn74hc595/test.esp32-ard.yaml b/tests/components/sn74hc595/test.esp32-ard.yaml deleted file mode 100644 index a4bab64862..0000000000 --- a/tests/components/sn74hc595/test.esp32-ard.yaml +++ /dev/null @@ -1,12 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO18 - clock_pin: GPIO15 - data_pin: GPIO14 - latch_pin1: GPIO21 - oe_pin1: GPIO22 - latch_pin2: GPIO23 - oe_pin2: GPIO25 - -<<: !include common.yaml diff --git a/tests/components/sn74hc595/test.esp32-c3-ard.yaml b/tests/components/sn74hc595/test.esp32-c3-ard.yaml deleted file mode 100644 index 14c928be88..0000000000 --- a/tests/components/sn74hc595/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,12 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO8 - clock_pin: GPIO5 - data_pin: GPIO4 - latch_pin1: GPIO1 - oe_pin1: GPIO2 - latch_pin2: GPIO3 - oe_pin2: GPIO9 - -<<: !include common.yaml diff --git a/tests/components/sntp/test.esp32-ard.yaml b/tests/components/sntp/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sntp/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sntp/test.esp32-c3-ard.yaml b/tests/components/sntp/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sntp/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.esp32-ard.yaml b/tests/components/sonoff_d1/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/sonoff_d1/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.esp32-c3-ard.yaml b/tests/components/sonoff_d1/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/sonoff_d1/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/sound_level/test.esp32-ard.yaml b/tests/components/sound_level/test.esp32-ard.yaml deleted file mode 100644 index c6d1bfa330..0000000000 --- a/tests/components/sound_level/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - i2s_bclk_pin: GPIO25 - i2s_lrclk_pin: GPIO26 - i2s_dout_pin: GPIO27 - -<<: !include common.yaml diff --git a/tests/components/sound_level/test.esp32-c3-ard.yaml b/tests/components/sound_level/test.esp32-c3-ard.yaml deleted file mode 100644 index aeb7d9f0af..0000000000 --- a/tests/components/sound_level/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - i2s_bclk_pin: GPIO6 - i2s_lrclk_pin: GPIO7 - i2s_dout_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/sound_level/test.esp32-s3-ard.yaml b/tests/components/sound_level/test.esp32-s3-ard.yaml deleted file mode 100644 index 9c1f32d5bd..0000000000 --- a/tests/components/sound_level/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - i2s_bclk_pin: GPIO4 - i2s_lrclk_pin: GPIO5 - i2s_dout_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/speed/test.esp32-ard.yaml b/tests/components/speed/test.esp32-ard.yaml deleted file mode 100644 index 26da1ce1d6..0000000000 --- a/tests/components/speed/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/speed/test.esp32-c3-ard.yaml b/tests/components/speed/test.esp32-c3-ard.yaml deleted file mode 100644 index 7476963591..0000000000 --- a/tests/components/speed/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/spi_device/test.esp32-ard.yaml b/tests/components/spi_device/test.esp32-ard.yaml deleted file mode 100644 index 448e54fea6..0000000000 --- a/tests/components/spi_device/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/spi_device/test.esp32-c3-ard.yaml b/tests/components/spi_device/test.esp32-c3-ard.yaml deleted file mode 100644 index bfa12b1755..0000000000 --- a/tests/components/spi_device/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.esp32-ard.yaml b/tests/components/spi_led_strip/test.esp32-ard.yaml deleted file mode 100644 index 8906602ef4..0000000000 --- a/tests/components/spi_led_strip/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.esp32-c3-ard.yaml b/tests/components/spi_led_strip/test.esp32-c3-ard.yaml deleted file mode 100644 index a85b587070..0000000000 --- a/tests/components/spi_led_strip/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/sprinkler/test.esp32-ard.yaml b/tests/components/sprinkler/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sprinkler/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sprinkler/test.esp32-c3-ard.yaml b/tests/components/sprinkler/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sprinkler/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sps30/test.esp32-ard.yaml b/tests/components/sps30/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sps30/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sps30/test.esp32-c3-ard.yaml b/tests/components/sps30/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sps30/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.esp32-ard.yaml b/tests/components/ssd1306_i2c/test.esp32-ard.yaml deleted file mode 100644 index 1ca773e06c..0000000000 --- a/tests/components/ssd1306_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - reset_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.esp32-c3-ard.yaml b/tests/components/ssd1306_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index 4eaff7fa4a..0000000000 --- a/tests/components/ssd1306_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - reset_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.esp32-ard.yaml b/tests/components/ssd1306_spi/test.esp32-ard.yaml deleted file mode 100644 index bad5241f79..0000000000 --- a/tests/components/ssd1306_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1306_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/ssd1306_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.esp32-ard.yaml b/tests/components/ssd1322_spi/test.esp32-ard.yaml deleted file mode 100644 index bad5241f79..0000000000 --- a/tests/components/ssd1322_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1322_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/ssd1322_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.esp32-ard.yaml b/tests/components/ssd1325_spi/test.esp32-ard.yaml deleted file mode 100644 index bad5241f79..0000000000 --- a/tests/components/ssd1325_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1325_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/ssd1325_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.esp32-ard.yaml b/tests/components/ssd1327_i2c/test.esp32-ard.yaml deleted file mode 100644 index 1ca773e06c..0000000000 --- a/tests/components/ssd1327_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - reset_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.esp32-c3-ard.yaml b/tests/components/ssd1327_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index 4eaff7fa4a..0000000000 --- a/tests/components/ssd1327_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - reset_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.esp32-ard.yaml b/tests/components/ssd1327_spi/test.esp32-ard.yaml deleted file mode 100644 index bad5241f79..0000000000 --- a/tests/components/ssd1327_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1327_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/ssd1327_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.esp32-ard.yaml b/tests/components/ssd1331_spi/test.esp32-ard.yaml deleted file mode 100644 index bad5241f79..0000000000 --- a/tests/components/ssd1331_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1331_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/ssd1331_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.esp32-ard.yaml b/tests/components/ssd1351_spi/test.esp32-ard.yaml deleted file mode 100644 index bad5241f79..0000000000 --- a/tests/components/ssd1351_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1351_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/ssd1351_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.esp32-ard.yaml b/tests/components/st7567_i2c/test.esp32-ard.yaml deleted file mode 100644 index 1ca773e06c..0000000000 --- a/tests/components/st7567_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - reset_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.esp32-c3-ard.yaml b/tests/components/st7567_i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index 4eaff7fa4a..0000000000 --- a/tests/components/st7567_i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - reset_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/st7567_spi/test.esp32-ard.yaml b/tests/components/st7567_spi/test.esp32-ard.yaml deleted file mode 100644 index bad5241f79..0000000000 --- a/tests/components/st7567_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/st7567_spi/test.esp32-c3-ard.yaml b/tests/components/st7567_spi/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/st7567_spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/st7735/test.esp32-ard.yaml b/tests/components/st7735/test.esp32-ard.yaml deleted file mode 100644 index bad5241f79..0000000000 --- a/tests/components/st7735/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/st7735/test.esp32-c3-ard.yaml b/tests/components/st7735/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/st7735/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/st7789v/test.esp32-ard.yaml b/tests/components/st7789v/test.esp32-ard.yaml deleted file mode 100644 index bad5241f79..0000000000 --- a/tests/components/st7789v/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - dc_pin: GPIO13 - reset_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/st7789v/test.esp32-c3-ard.yaml b/tests/components/st7789v/test.esp32-c3-ard.yaml deleted file mode 100644 index c5c932c92c..0000000000 --- a/tests/components/st7789v/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/st7920/test.esp32-ard.yaml b/tests/components/st7920/test.esp32-ard.yaml deleted file mode 100644 index 04d2633d2b..0000000000 --- a/tests/components/st7920/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO12 - -<<: !include common.yaml diff --git a/tests/components/st7920/test.esp32-c3-ard.yaml b/tests/components/st7920/test.esp32-c3-ard.yaml deleted file mode 100644 index 2415ba5dc6..0000000000 --- a/tests/components/st7920/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - cs_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/statsD/test.esp32-ard.yaml b/tests/components/statsD/test.esp32-ard.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/statsD/test.esp32-ard.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/statsD/test.esp32-c3-ard.yaml b/tests/components/statsD/test.esp32-c3-ard.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/statsD/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/status/test.esp32-ard.yaml b/tests/components/status/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/status/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/status/test.esp32-c3-ard.yaml b/tests/components/status/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/status/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/status_led/test.esp32-ard.yaml b/tests/components/status_led/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/status_led/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/status_led/test.esp32-c3-ard.yaml b/tests/components/status_led/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/status_led/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/stepper/test.esp32-ard.yaml b/tests/components/stepper/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/stepper/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/stepper/test.esp32-c3-ard.yaml b/tests/components/stepper/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/stepper/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sts3x/test.esp32-ard.yaml b/tests/components/sts3x/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sts3x/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sts3x/test.esp32-c3-ard.yaml b/tests/components/sts3x/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sts3x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sun/test.esp32-ard.yaml b/tests/components/sun/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sun/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sun/test.esp32-c3-ard.yaml b/tests/components/sun/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sun/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.esp32-ard.yaml b/tests/components/sun_gtil2/test.esp32-ard.yaml deleted file mode 100644 index ad420099ff..0000000000 --- a/tests/components/sun_gtil2/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.esp32-c3-ard.yaml b/tests/components/sun_gtil2/test.esp32-c3-ard.yaml deleted file mode 100644 index b8a6b85616..0000000000 --- a/tests/components/sun_gtil2/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/switch/test.esp32-ard.yaml b/tests/components/switch/test.esp32-ard.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/switch/test.esp32-ard.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/switch/test.esp32-c3-ard.yaml b/tests/components/switch/test.esp32-c3-ard.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/switch/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/sx126x/test.esp32-ard.yaml b/tests/components/sx126x/test.esp32-ard.yaml deleted file mode 100644 index 9770f52229..0000000000 --- a/tests/components/sx126x/test.esp32-ard.yaml +++ /dev/null @@ -1,10 +0,0 @@ -substitutions: - clk_pin: GPIO5 - mosi_pin: GPIO27 - miso_pin: GPIO19 - cs_pin: GPIO18 - rst_pin: GPIO23 - busy_pin: GPIO25 - dio1_pin: GPIO26 - -<<: !include common.yaml diff --git a/tests/components/sx126x/test.esp32-c3-ard.yaml b/tests/components/sx126x/test.esp32-c3-ard.yaml deleted file mode 100644 index 91450e24ce..0000000000 --- a/tests/components/sx126x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,10 +0,0 @@ -substitutions: - clk_pin: GPIO5 - mosi_pin: GPIO18 - miso_pin: GPIO19 - cs_pin: GPIO1 - rst_pin: GPIO2 - busy_pin: GPIO4 - dio1_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/sx127x/test.esp32-ard.yaml b/tests/components/sx127x/test.esp32-ard.yaml deleted file mode 100644 index 71270462a2..0000000000 --- a/tests/components/sx127x/test.esp32-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO5 - mosi_pin: GPIO27 - miso_pin: GPIO19 - cs_pin: GPIO18 - rst_pin: GPIO23 - dio0_pin: GPIO26 - -<<: !include common.yaml diff --git a/tests/components/sx127x/test.esp32-c3-ard.yaml b/tests/components/sx127x/test.esp32-c3-ard.yaml deleted file mode 100644 index 36535a950d..0000000000 --- a/tests/components/sx127x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO5 - mosi_pin: GPIO18 - miso_pin: GPIO19 - cs_pin: GPIO1 - rst_pin: GPIO2 - dio0_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/sx1509/test.esp32-ard.yaml b/tests/components/sx1509/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/sx1509/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/sx1509/test.esp32-c3-ard.yaml b/tests/components/sx1509/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/sx1509/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/syslog/test.esp32-ard.yaml b/tests/components/syslog/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/syslog/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/syslog/test.esp32-c3-ard.yaml b/tests/components/syslog/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/syslog/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/t6615/test.esp32-ard.yaml b/tests/components/t6615/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/t6615/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/t6615/test.esp32-c3-ard.yaml b/tests/components/t6615/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/t6615/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/tc74/test.esp32-ard.yaml b/tests/components/tc74/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tc74/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tc74/test.esp32-c3-ard.yaml b/tests/components/tc74/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tc74/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tca9548a/test.esp32-ard.yaml b/tests/components/tca9548a/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tca9548a/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tca9548a/test.esp32-c3-ard.yaml b/tests/components/tca9548a/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tca9548a/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tca9555/test.esp32-ard.yaml b/tests/components/tca9555/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tca9555/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tca9555/test.esp32-c3-ard.yaml b/tests/components/tca9555/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tca9555/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tcl112/test.esp32-ard.yaml b/tests/components/tcl112/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/tcl112/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/tcl112/test.esp32-c3-ard.yaml b/tests/components/tcl112/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/tcl112/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/tcs34725/test.esp32-ard.yaml b/tests/components/tcs34725/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tcs34725/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tcs34725/test.esp32-c3-ard.yaml b/tests/components/tcs34725/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tcs34725/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tee501/test.esp32-ard.yaml b/tests/components/tee501/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tee501/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tee501/test.esp32-c3-ard.yaml b/tests/components/tee501/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tee501/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/teleinfo/test.esp32-ard.yaml b/tests/components/teleinfo/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/teleinfo/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/teleinfo/test.esp32-c3-ard.yaml b/tests/components/teleinfo/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/teleinfo/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/tem3200/test.esp32-ard.yaml b/tests/components/tem3200/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/tem3200/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/tem3200/test.esp32-s3-ard.yaml b/tests/components/tem3200/test.esp32-s3-ard.yaml deleted file mode 100644 index 4942e3c2b3..0000000000 --- a/tests/components/tem3200/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -<<: !include common.yaml diff --git a/tests/components/template/test.esp32-ard.yaml b/tests/components/template/test.esp32-ard.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/template/test.esp32-ard.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/template/test.esp32-c3-ard.yaml b/tests/components/template/test.esp32-c3-ard.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/template/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/thermostat/test.esp32-ard.yaml b/tests/components/thermostat/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/thermostat/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/thermostat/test.esp32-c3-ard.yaml b/tests/components/thermostat/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/thermostat/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/time_based/test.esp32-ard.yaml b/tests/components/time_based/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/time_based/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/time_based/test.esp32-c3-ard.yaml b/tests/components/time_based/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/time_based/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/tlc59208f/test.esp32-ard.yaml b/tests/components/tlc59208f/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tlc59208f/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tlc59208f/test.esp32-c3-ard.yaml b/tests/components/tlc59208f/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tlc59208f/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tlc5947/test.esp32-ard.yaml b/tests/components/tlc5947/test.esp32-ard.yaml deleted file mode 100644 index 52411bc1e9..0000000000 --- a/tests/components/tlc5947/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clock_pin: GPIO15 - data_pin: GPIO14 - lat_pin: GPIO13 - -packages: - common: !include common.yaml diff --git a/tests/components/tlc5947/test.esp32-c3-ard.yaml b/tests/components/tlc5947/test.esp32-c3-ard.yaml deleted file mode 100644 index 4694c43642..0000000000 --- a/tests/components/tlc5947/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - lat_pin: GPIO3 - -packages: - common: !include common.yaml diff --git a/tests/components/tlc5971/test.esp32-ard.yaml b/tests/components/tlc5971/test.esp32-ard.yaml deleted file mode 100644 index 52411bc1e9..0000000000 --- a/tests/components/tlc5971/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clock_pin: GPIO15 - data_pin: GPIO14 - lat_pin: GPIO13 - -packages: - common: !include common.yaml diff --git a/tests/components/tlc5971/test.esp32-c3-ard.yaml b/tests/components/tlc5971/test.esp32-c3-ard.yaml deleted file mode 100644 index d898a21d46..0000000000 --- a/tests/components/tlc5971/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -packages: - common: !include common.yaml diff --git a/tests/components/tlc5971/test.esp32-s2-ard.yaml b/tests/components/tlc5971/test.esp32-s2-ard.yaml deleted file mode 100644 index 7bba0b0117..0000000000 --- a/tests/components/tlc5971/test.esp32-s2-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - clock_pin: GPIO36 - data_pin: GPIO35 - -packages: - common: !include common.yaml diff --git a/tests/components/tm1621/test.esp32-ard.yaml b/tests/components/tm1621/test.esp32-ard.yaml deleted file mode 100644 index 0441e4bffe..0000000000 --- a/tests/components/tm1621/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - cs_pin: GPIO16 - data_pin: GPIO17 - read_pin: GPIO12 - write_pin: GPIO13 - -<<: !include common.yaml diff --git a/tests/components/tm1621/test.esp32-c3-ard.yaml b/tests/components/tm1621/test.esp32-c3-ard.yaml deleted file mode 100644 index 562ced7485..0000000000 --- a/tests/components/tm1621/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - cs_pin: GPIO6 - data_pin: GPIO7 - read_pin: GPIO2 - write_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/tm1637/test.esp32-ard.yaml b/tests/components/tm1637/test.esp32-ard.yaml deleted file mode 100644 index 2c5786c47c..0000000000 --- a/tests/components/tm1637/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clk_pin: GPIO14 - dio_pin: GPIO13 - -<<: !include common.yaml diff --git a/tests/components/tm1637/test.esp32-c3-ard.yaml b/tests/components/tm1637/test.esp32-c3-ard.yaml deleted file mode 100644 index 96f6708a3b..0000000000 --- a/tests/components/tm1637/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clk_pin: GPIO4 - dio_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/tm1638/test.esp32-ard.yaml b/tests/components/tm1638/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/tm1638/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/tm1638/test.esp32-c3-ard.yaml b/tests/components/tm1638/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/tm1638/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/tm1651/test.esp32-ard.yaml b/tests/components/tm1651/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/tm1651/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/tm1651/test.esp32-c3-ard.yaml b/tests/components/tm1651/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/tm1651/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/tmp102/test.esp32-ard.yaml b/tests/components/tmp102/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tmp102/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tmp102/test.esp32-c3-ard.yaml b/tests/components/tmp102/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tmp102/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tmp1075/test.esp32-ard.yaml b/tests/components/tmp1075/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tmp1075/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tmp1075/test.esp32-c3-ard.yaml b/tests/components/tmp1075/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tmp1075/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tmp117/test.esp32-ard.yaml b/tests/components/tmp117/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tmp117/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tmp117/test.esp32-c3-ard.yaml b/tests/components/tmp117/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tmp117/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tof10120/test.esp32-ard.yaml b/tests/components/tof10120/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tof10120/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tof10120/test.esp32-c3-ard.yaml b/tests/components/tof10120/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tof10120/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tormatic/test.esp32-ard.yaml b/tests/components/tormatic/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/tormatic/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/tormatic/test.esp32-c3-ard.yaml b/tests/components/tormatic/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/tormatic/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/toshiba/test.esp32-ard.yaml b/tests/components/toshiba/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/toshiba/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/toshiba/test.esp32-c3-ard.yaml b/tests/components/toshiba/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/toshiba/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/total_daily_energy/test.esp32-ard.yaml b/tests/components/total_daily_energy/test.esp32-ard.yaml deleted file mode 100644 index 8b42b21b54..0000000000 --- a/tests/components/total_daily_energy/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - sel_pin: GPIO12 - cf_pin: GPIO13 - cf1_pin: GPIO14 - -<<: !include common.yaml diff --git a/tests/components/total_daily_energy/test.esp32-c3-ard.yaml b/tests/components/total_daily_energy/test.esp32-c3-ard.yaml deleted file mode 100644 index 8b0d069ce2..0000000000 --- a/tests/components/total_daily_energy/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - sel_pin: GPIO2 - cf_pin: GPIO3 - cf1_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tsl2561/test.esp32-ard.yaml b/tests/components/tsl2561/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tsl2561/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tsl2561/test.esp32-c3-ard.yaml b/tests/components/tsl2561/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tsl2561/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tsl2591/test.esp32-ard.yaml b/tests/components/tsl2591/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/tsl2591/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/tsl2591/test.esp32-c3-ard.yaml b/tests/components/tsl2591/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/tsl2591/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tt21100/test.esp32-ard.yaml b/tests/components/tt21100/test.esp32-ard.yaml deleted file mode 100644 index 05598719f9..0000000000 --- a/tests/components/tt21100/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - disp_reset_pin: GPIO12 - scl_pin: GPIO13 - sda_pin: GPIO14 - interrupt_pin: GPIO15 - reset_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/tt21100/test.esp32-c3-ard.yaml b/tests/components/tt21100/test.esp32-c3-ard.yaml deleted file mode 100644 index 36a8ce2778..0000000000 --- a/tests/components/tt21100/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - disp_reset_pin: GPIO10 - scl_pin: GPIO0 - sda_pin: GPIO1 - interrupt_pin: GPIO2 - reset_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.esp32-ard.yaml b/tests/components/ttp229_bsf/test.esp32-ard.yaml deleted file mode 100644 index 80ed75293f..0000000000 --- a/tests/components/ttp229_bsf/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sdo_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.esp32-c3-ard.yaml b/tests/components/ttp229_bsf/test.esp32-c3-ard.yaml deleted file mode 100644 index 135b213edc..0000000000 --- a/tests/components/ttp229_bsf/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sdo_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.esp32-ard.yaml b/tests/components/ttp229_lsf/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ttp229_lsf/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.esp32-c3-ard.yaml b/tests/components/ttp229_lsf/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ttp229_lsf/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tuya/test.esp32-ard.yaml b/tests/components/tuya/test.esp32-ard.yaml deleted file mode 100644 index 926a46cf73..0000000000 --- a/tests/components/tuya/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - status_pin: GPIO12 - -<<: !include common.yaml diff --git a/tests/components/tuya/test.esp32-c3-ard.yaml b/tests/components/tuya/test.esp32-c3-ard.yaml deleted file mode 100644 index c62a0b10f6..0000000000 --- a/tests/components/tuya/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - status_pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/tx20/test.esp32-ard.yaml b/tests/components/tx20/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/tx20/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/tx20/test.esp32-c3-ard.yaml b/tests/components/tx20/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/tx20/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/uart/test-uart_max_with_usb_cdc.esp32-c3-ard.yaml b/tests/components/uart/test-uart_max_with_usb_cdc.esp32-c3-ard.yaml deleted file mode 100644 index 602766869c..0000000000 --- a/tests/components/uart/test-uart_max_with_usb_cdc.esp32-c3-ard.yaml +++ /dev/null @@ -1,36 +0,0 @@ -<<: !include ../logger/common-usb_cdc.yaml - -esphome: - on_boot: - then: - - uart.write: - id: uart_1 - data: 'Hello World' - - uart.write: - id: uart_1 - data: [0x00, 0x20, 0x42] - -uart: - - id: uart_1 - tx_pin: 4 - rx_pin: 5 - flow_control_pin: 6 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 - - - id: uart_2 - tx_pin: 7 - rx_pin: 8 - flow_control_pin: 9 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 diff --git a/tests/components/uart/test-uart_max_with_usb_cdc.esp32-s2-ard.yaml b/tests/components/uart/test-uart_max_with_usb_cdc.esp32-s2-ard.yaml deleted file mode 100644 index 602766869c..0000000000 --- a/tests/components/uart/test-uart_max_with_usb_cdc.esp32-s2-ard.yaml +++ /dev/null @@ -1,36 +0,0 @@ -<<: !include ../logger/common-usb_cdc.yaml - -esphome: - on_boot: - then: - - uart.write: - id: uart_1 - data: 'Hello World' - - uart.write: - id: uart_1 - data: [0x00, 0x20, 0x42] - -uart: - - id: uart_1 - tx_pin: 4 - rx_pin: 5 - flow_control_pin: 6 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 - - - id: uart_2 - tx_pin: 7 - rx_pin: 8 - flow_control_pin: 9 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 diff --git a/tests/components/uart/test-uart_max_with_usb_cdc.esp32-s3-ard.yaml b/tests/components/uart/test-uart_max_with_usb_cdc.esp32-s3-ard.yaml deleted file mode 100644 index 4af255e1e4..0000000000 --- a/tests/components/uart/test-uart_max_with_usb_cdc.esp32-s3-ard.yaml +++ /dev/null @@ -1,48 +0,0 @@ -<<: !include ../logger/common-usb_cdc.yaml - -esphome: - on_boot: - then: - - uart.write: - id: uart_1 - data: 'Hello World' - - uart.write: - id: uart_1 - data: [0x00, 0x20, 0x42] - -uart: - - id: uart_1 - tx_pin: 4 - rx_pin: 5 - flow_control_pin: 6 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 - - - id: uart_2 - tx_pin: 7 - rx_pin: 8 - flow_control_pin: 9 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 - - - id: uart_3 - tx_pin: 10 - rx_pin: 11 - flow_control_pin: 12 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 diff --git a/tests/components/uart/test.esp32-ard.yaml b/tests/components/uart/test.esp32-ard.yaml deleted file mode 100644 index a201185309..0000000000 --- a/tests/components/uart/test.esp32-ard.yaml +++ /dev/null @@ -1,18 +0,0 @@ -esphome: - on_boot: - then: - - uart.write: 'Hello World' - - uart.write: [0x00, 0x20, 0x42] - -uart: - - id: uart_uart - tx_pin: 17 - rx_pin: 16 - flow_control_pin: 4 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 diff --git a/tests/components/uart/test.esp32-c3-ard.yaml b/tests/components/uart/test.esp32-c3-ard.yaml deleted file mode 100644 index b053290a8b..0000000000 --- a/tests/components/uart/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,18 +0,0 @@ -esphome: - on_boot: - then: - - uart.write: 'Hello World' - - uart.write: [0x00, 0x20, 0x42] - -uart: - - id: uart_uart - tx_pin: 4 - rx_pin: 5 - flow_control_pin: 6 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 diff --git a/tests/components/udp/test.esp32-ard.yaml b/tests/components/udp/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/udp/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/udp/test.esp32-c3-ard.yaml b/tests/components/udp/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/udp/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ufire_ec/test.esp32-ard.yaml b/tests/components/ufire_ec/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ufire_ec/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ufire_ec/test.esp32-c3-ard.yaml b/tests/components/ufire_ec/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ufire_ec/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ufire_ise/test.esp32-ard.yaml b/tests/components/ufire_ise/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/ufire_ise/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/ufire_ise/test.esp32-c3-ard.yaml b/tests/components/ufire_ise/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/ufire_ise/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/uln2003/test.esp32-ard.yaml b/tests/components/uln2003/test.esp32-ard.yaml deleted file mode 100644 index ee4cff0923..0000000000 --- a/tests/components/uln2003/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - pin_a: GPIO12 - pin_b: GPIO13 - pin_c: GPIO14 - pin_d: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/uln2003/test.esp32-c3-ard.yaml b/tests/components/uln2003/test.esp32-c3-ard.yaml deleted file mode 100644 index 11d16a4d5d..0000000000 --- a/tests/components/uln2003/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - pin_a: GPIO0 - pin_b: GPIO1 - pin_c: GPIO2 - pin_d: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/ultrasonic/test.esp32-ard.yaml b/tests/components/ultrasonic/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ultrasonic/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ultrasonic/test.esp32-c3-ard.yaml b/tests/components/ultrasonic/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ultrasonic/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/update/test.esp32-ard.yaml b/tests/components/update/test.esp32-ard.yaml deleted file mode 100644 index c1937b5a10..0000000000 --- a/tests/components/update/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - verify_ssl: "false" - -<<: !include common.yaml diff --git a/tests/components/uponor_smatrix/test.esp32-ard.yaml b/tests/components/uponor_smatrix/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/uponor_smatrix/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/uponor_smatrix/test.esp32-c3-ard.yaml b/tests/components/uponor_smatrix/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/uponor_smatrix/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/uptime/test.esp32-ard.yaml b/tests/components/uptime/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/uptime/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/uptime/test.esp32-c3-ard.yaml b/tests/components/uptime/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/uptime/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/vbus/test.esp32-ard.yaml b/tests/components/vbus/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/vbus/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/vbus/test.esp32-c3-ard.yaml b/tests/components/vbus/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/vbus/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/veml3235/test.esp32-ard.yaml b/tests/components/veml3235/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/veml3235/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/veml3235/test.esp32-c3-ard.yaml b/tests/components/veml3235/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/veml3235/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/veml7700/test.esp32-ard.yaml b/tests/components/veml7700/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/veml7700/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/veml7700/test.esp32-c3-ard.yaml b/tests/components/veml7700/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/veml7700/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/version/test.esp32-ard.yaml b/tests/components/version/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/version/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/version/test.esp32-c3-ard.yaml b/tests/components/version/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/version/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/vl53l0x/test.esp32-ard.yaml b/tests/components/vl53l0x/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/vl53l0x/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/vl53l0x/test.esp32-c3-ard.yaml b/tests/components/vl53l0x/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/vl53l0x/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/voice_assistant/test.esp32-ard.yaml b/tests/components/voice_assistant/test.esp32-ard.yaml deleted file mode 100644 index f6e553f9dc..0000000000 --- a/tests/components/voice_assistant/test.esp32-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - i2s_lrclk_pin: GPIO16 - i2s_bclk_pin: GPIO17 - i2s_mclk_pin: GPIO15 - i2s_din_pin: GPIO13 - i2s_dout_pin: GPIO12 - -<<: !include common.yaml diff --git a/tests/components/voice_assistant/test.esp32-c3-ard.yaml b/tests/components/voice_assistant/test.esp32-c3-ard.yaml deleted file mode 100644 index f596d927cb..0000000000 --- a/tests/components/voice_assistant/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - i2s_lrclk_pin: GPIO6 - i2s_bclk_pin: GPIO7 - i2s_mclk_pin: GPIO5 - i2s_din_pin: GPIO3 - i2s_dout_pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/wake_on_lan/test.esp32-ard.yaml b/tests/components/wake_on_lan/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wake_on_lan/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wake_on_lan/test.esp32-c3-ard.yaml b/tests/components/wake_on_lan/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wake_on_lan/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp32-ard.yaml b/tests/components/waveshare_epaper/test.esp32-ard.yaml deleted file mode 100644 index c36345b984..0000000000 --- a/tests/components/waveshare_epaper/test.esp32-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO18 - mosi_pin: GPIO23 - cs_pin: GPIO25 - dc_pin: GPIO26 - busy_pin: GPIO27 - reset_pin: GPIO32 - -<<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml b/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml deleted file mode 100644 index 4cb230f6f2..0000000000 --- a/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - cs_pin: GPIO4 - dc_pin: GPIO1 - busy_pin: GPIO2 - reset_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/whirlpool/test.esp32-ard.yaml b/tests/components/whirlpool/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/whirlpool/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/whirlpool/test.esp32-c3-ard.yaml b/tests/components/whirlpool/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/whirlpool/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/whynter/test.esp32-ard.yaml b/tests/components/whynter/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/whynter/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/whynter/test.esp32-c3-ard.yaml b/tests/components/whynter/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/whynter/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/wiegand/test.esp32-ard.yaml b/tests/components/wiegand/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wiegand/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wiegand/test.esp32-c3-ard.yaml b/tests/components/wiegand/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wiegand/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wifi/test-eap.esp32-ard.yaml b/tests/components/wifi/test-eap.esp32-ard.yaml deleted file mode 100644 index 9177e5de10..0000000000 --- a/tests/components/wifi/test-eap.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-eap.yaml diff --git a/tests/components/wifi/test.esp32-ard.yaml b/tests/components/wifi/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wifi/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wifi/test.esp32-c3-ard.yaml b/tests/components/wifi/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wifi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wifi_info/test.esp32-ard.yaml b/tests/components/wifi_info/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wifi_info/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wifi_info/test.esp32-c3-ard.yaml b/tests/components/wifi_info/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wifi_info/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wifi_signal/test.esp32-ard.yaml b/tests/components/wifi_signal/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wifi_signal/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wifi_signal/test.esp32-c3-ard.yaml b/tests/components/wifi_signal/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wifi_signal/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wireguard/test.esp32-ard.yaml b/tests/components/wireguard/test.esp32-ard.yaml deleted file mode 100644 index 2798f8e566..0000000000 --- a/tests/components/wireguard/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -<<: !include common.yaml - -network: - enable_ipv6: true diff --git a/tests/components/wireguard/test.esp32-c3-ard.yaml b/tests/components/wireguard/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wireguard/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wk2132_i2c/test.esp32-ard.yaml b/tests/components/wk2132_i2c/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/wk2132_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/wk2132_i2c/test.esp32-s3-ard.yaml b/tests/components/wk2132_i2c/test.esp32-s3-ard.yaml deleted file mode 100644 index 4942e3c2b3..0000000000 --- a/tests/components/wk2132_i2c/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -<<: !include common.yaml diff --git a/tests/components/wk2132_spi/test.esp32-ard.yaml b/tests/components/wk2132_spi/test.esp32-ard.yaml deleted file mode 100644 index 76e7138ab0..0000000000 --- a/tests/components/wk2132_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO18 - miso_pin: GPIO19 - mosi_pin: GPIO23 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/wk2132_spi/test.esp32-s3-ard.yaml b/tests/components/wk2132_spi/test.esp32-s3-ard.yaml deleted file mode 100644 index b0aadf620a..0000000000 --- a/tests/components/wk2132_spi/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO40 - miso_pin: GPIO41 - mosi_pin: GPIO6 - cs_pin: GPIO19 - -<<: !include common.yaml diff --git a/tests/components/wk2168_i2c/test.esp32-ard.yaml b/tests/components/wk2168_i2c/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/wk2168_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/wk2168_i2c/test.esp32-s3-ard.yaml b/tests/components/wk2168_i2c/test.esp32-s3-ard.yaml deleted file mode 100644 index 4942e3c2b3..0000000000 --- a/tests/components/wk2168_i2c/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -<<: !include common.yaml diff --git a/tests/components/wk2168_spi/test.esp32-ard.yaml b/tests/components/wk2168_spi/test.esp32-ard.yaml deleted file mode 100644 index 76e7138ab0..0000000000 --- a/tests/components/wk2168_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO18 - miso_pin: GPIO19 - mosi_pin: GPIO23 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/wk2168_spi/test.esp32-s3-ard.yaml b/tests/components/wk2168_spi/test.esp32-s3-ard.yaml deleted file mode 100644 index b0aadf620a..0000000000 --- a/tests/components/wk2168_spi/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO40 - miso_pin: GPIO41 - mosi_pin: GPIO6 - cs_pin: GPIO19 - -<<: !include common.yaml diff --git a/tests/components/wk2204_i2c/test.esp32-ard.yaml b/tests/components/wk2204_i2c/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/wk2204_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/wk2204_i2c/test.esp32-s3-ard.yaml b/tests/components/wk2204_i2c/test.esp32-s3-ard.yaml deleted file mode 100644 index 4942e3c2b3..0000000000 --- a/tests/components/wk2204_i2c/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -<<: !include common.yaml diff --git a/tests/components/wk2204_spi/test.esp32-ard.yaml b/tests/components/wk2204_spi/test.esp32-ard.yaml deleted file mode 100644 index 76e7138ab0..0000000000 --- a/tests/components/wk2204_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO18 - miso_pin: GPIO19 - mosi_pin: GPIO23 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/wk2204_spi/test.esp32-s3-ard.yaml b/tests/components/wk2204_spi/test.esp32-s3-ard.yaml deleted file mode 100644 index b0aadf620a..0000000000 --- a/tests/components/wk2204_spi/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO40 - miso_pin: GPIO41 - mosi_pin: GPIO6 - cs_pin: GPIO19 - -<<: !include common.yaml diff --git a/tests/components/wk2212_i2c/test.esp32-ard.yaml b/tests/components/wk2212_i2c/test.esp32-ard.yaml deleted file mode 100644 index 3b761d3fc1..0000000000 --- a/tests/components/wk2212_i2c/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 - -<<: !include common.yaml diff --git a/tests/components/wk2212_i2c/test.esp32-s3-ard.yaml b/tests/components/wk2212_i2c/test.esp32-s3-ard.yaml deleted file mode 100644 index 4942e3c2b3..0000000000 --- a/tests/components/wk2212_i2c/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -<<: !include common.yaml diff --git a/tests/components/wk2212_spi/test.esp32-ard.yaml b/tests/components/wk2212_spi/test.esp32-ard.yaml deleted file mode 100644 index 76e7138ab0..0000000000 --- a/tests/components/wk2212_spi/test.esp32-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO18 - miso_pin: GPIO19 - mosi_pin: GPIO23 - cs_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/wk2212_spi/test.esp32-s3-ard.yaml b/tests/components/wk2212_spi/test.esp32-s3-ard.yaml deleted file mode 100644 index b0aadf620a..0000000000 --- a/tests/components/wk2212_spi/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clk_pin: GPIO40 - miso_pin: GPIO41 - mosi_pin: GPIO6 - cs_pin: GPIO19 - -<<: !include common.yaml diff --git a/tests/components/wl_134/test.esp32-ard.yaml b/tests/components/wl_134/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/wl_134/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/wl_134/test.esp32-c3-ard.yaml b/tests/components/wl_134/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/wl_134/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/wts01/test.esp32-ard.yaml b/tests/components/wts01/test.esp32-ard.yaml deleted file mode 100644 index 4904e1f54f..0000000000 --- a/tests/components/wts01/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO16 - rx_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/wts01/test.esp32-c3-ard.yaml b/tests/components/wts01/test.esp32-c3-ard.yaml deleted file mode 100644 index 00cec5b3b8..0000000000 --- a/tests/components/wts01/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO6 - rx_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/x9c/test.esp32-ard.yaml b/tests/components/x9c/test.esp32-ard.yaml deleted file mode 100644 index 6dfe3a67eb..0000000000 --- a/tests/components/x9c/test.esp32-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO13 - inc_pin: GPIO14 - ud_pin: GPIO15 - -<<: !include common.yaml diff --git a/tests/components/x9c/test.esp32-c3-ard.yaml b/tests/components/x9c/test.esp32-c3-ard.yaml deleted file mode 100644 index b06e15a98c..0000000000 --- a/tests/components/x9c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO3 - inc_pin: GPIO4 - ud_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.esp32-ard.yaml b/tests/components/xgzp68xx/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/xgzp68xx/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.esp32-c3-ard.yaml b/tests/components/xgzp68xx/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/xgzp68xx/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/xiaomi_ble/test.esp32-ard.yaml b/tests/components/xiaomi_ble/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_ble/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_ble/test.esp32-c3-ard.yaml b/tests/components/xiaomi_ble/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_ble/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgd1/test.esp32-ard.yaml b/tests/components/xiaomi_cgd1/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_cgd1/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgd1/test.esp32-c3-ard.yaml b/tests/components/xiaomi_cgd1/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_cgd1/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgdk2/test.esp32-ard.yaml b/tests/components/xiaomi_cgdk2/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_cgdk2/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgdk2/test.esp32-c3-ard.yaml b/tests/components/xiaomi_cgdk2/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_cgdk2/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgg1/test.esp32-ard.yaml b/tests/components/xiaomi_cgg1/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_cgg1/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgg1/test.esp32-c3-ard.yaml b/tests/components/xiaomi_cgg1/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_cgg1/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgpr1/test.esp32-ard.yaml b/tests/components/xiaomi_cgpr1/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_cgpr1/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgpr1/test.esp32-c3-ard.yaml b/tests/components/xiaomi_cgpr1/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_cgpr1/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_gcls002/test.esp32-ard.yaml b/tests/components/xiaomi_gcls002/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_gcls002/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_gcls002/test.esp32-c3-ard.yaml b/tests/components/xiaomi_gcls002/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_gcls002/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_hhccjcy01/test.esp32-ard.yaml b/tests/components/xiaomi_hhccjcy01/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_hhccjcy01/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_hhccjcy01/test.esp32-c3-ard.yaml b/tests/components/xiaomi_hhccjcy01/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_hhccjcy01/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_hhccpot002/test.esp32-ard.yaml b/tests/components/xiaomi_hhccpot002/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_hhccpot002/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_hhccpot002/test.esp32-c3-ard.yaml b/tests/components/xiaomi_hhccpot002/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_hhccpot002/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_jqjcy01ym/test.esp32-ard.yaml b/tests/components/xiaomi_jqjcy01ym/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_jqjcy01ym/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_jqjcy01ym/test.esp32-c3-ard.yaml b/tests/components/xiaomi_jqjcy01ym/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_jqjcy01ym/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02/test.esp32-ard.yaml b/tests/components/xiaomi_lywsd02/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_lywsd02/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02/test.esp32-c3-ard.yaml b/tests/components/xiaomi_lywsd02/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_lywsd02/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02mmc/test.esp32-ard.yaml b/tests/components/xiaomi_lywsd02mmc/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_lywsd02mmc/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-ard.yaml b/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd03mmc/test.esp32-ard.yaml b/tests/components/xiaomi_lywsd03mmc/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_lywsd03mmc/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd03mmc/test.esp32-c3-ard.yaml b/tests/components/xiaomi_lywsd03mmc/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_lywsd03mmc/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsdcgq/test.esp32-ard.yaml b/tests/components/xiaomi_lywsdcgq/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_lywsdcgq/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsdcgq/test.esp32-c3-ard.yaml b/tests/components/xiaomi_lywsdcgq/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_lywsdcgq/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_mhoc303/test.esp32-ard.yaml b/tests/components/xiaomi_mhoc303/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_mhoc303/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_mhoc303/test.esp32-c3-ard.yaml b/tests/components/xiaomi_mhoc303/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_mhoc303/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_mhoc401/test.esp32-ard.yaml b/tests/components/xiaomi_mhoc401/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_mhoc401/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_mhoc401/test.esp32-c3-ard.yaml b/tests/components/xiaomi_mhoc401/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_mhoc401/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_miscale/test.esp32-ard.yaml b/tests/components/xiaomi_miscale/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_miscale/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_miscale/test.esp32-c3-ard.yaml b/tests/components/xiaomi_miscale/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_miscale/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_mjyd02yla/test.esp32-ard.yaml b/tests/components/xiaomi_mjyd02yla/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_mjyd02yla/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_mjyd02yla/test.esp32-c3-ard.yaml b/tests/components/xiaomi_mjyd02yla/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_mjyd02yla/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_mue4094rt/test.esp32-ard.yaml b/tests/components/xiaomi_mue4094rt/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_mue4094rt/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_mue4094rt/test.esp32-c3-ard.yaml b/tests/components/xiaomi_mue4094rt/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_mue4094rt/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_rtcgq02lm/test.esp32-ard.yaml b/tests/components/xiaomi_rtcgq02lm/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_rtcgq02lm/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_rtcgq02lm/test.esp32-c3-ard.yaml b/tests/components/xiaomi_rtcgq02lm/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_rtcgq02lm/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_wx08zm/test.esp32-ard.yaml b/tests/components/xiaomi_wx08zm/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_wx08zm/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_wx08zm/test.esp32-c3-ard.yaml b/tests/components/xiaomi_wx08zm/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_wx08zm/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-ard.yaml b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-ard.yaml b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xl9535/test.esp32-ard.yaml b/tests/components/xl9535/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/xl9535/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/xl9535/test.esp32-c3-ard.yaml b/tests/components/xl9535/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/xl9535/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/xpt2046/test.esp32-ard.yaml b/tests/components/xpt2046/test.esp32-ard.yaml deleted file mode 100644 index b39174947b..0000000000 --- a/tests/components/xpt2046/test.esp32-ard.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - clk_pin: GPIO17 - mosi_pin: GPIO18 - miso_pin: GPIO19 - dc_pin: GPIO13 - cs_pin: GPIO14 - disp_cs_pin: GPIO4 - interrupt_pin: GPIO21 - reset_pin: GPIO22 - -<<: !include common.yaml diff --git a/tests/components/xpt2046/test.esp32-c3-ard.yaml b/tests/components/xpt2046/test.esp32-c3-ard.yaml deleted file mode 100644 index 79b84902ac..0000000000 --- a/tests/components/xpt2046/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - clk_pin: GPIO4 - mosi_pin: GPIO5 - miso_pin: GPIO6 - dc_pin: GPIO7 - cs_pin: GPIO0 - disp_cs_pin: GPIO1 - interrupt_pin: GPIO3 - reset_pin: GPIO10 - -<<: !include common.yaml diff --git a/tests/components/yashima/test.esp32-ard.yaml b/tests/components/yashima/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/yashima/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/yashima/test.esp32-c3-ard.yaml b/tests/components/yashima/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/yashima/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/zhlt01/test.esp32-ard.yaml b/tests/components/zhlt01/test.esp32-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/zhlt01/test.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/zhlt01/test.esp32-c3-ard.yaml b/tests/components/zhlt01/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/zhlt01/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.esp32-ard.yaml b/tests/components/zio_ultrasonic/test.esp32-ard.yaml deleted file mode 100644 index 63c3bd6afd..0000000000 --- a/tests/components/zio_ultrasonic/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.esp32-c3-ard.yaml b/tests/components/zio_ultrasonic/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/zio_ultrasonic/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/zwave_proxy/test.esp32-ard.yaml b/tests/components/zwave_proxy/test.esp32-ard.yaml deleted file mode 100644 index f486544afa..0000000000 --- a/tests/components/zwave_proxy/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - -<<: !include common.yaml diff --git a/tests/components/zwave_proxy/test.esp32-c3-ard.yaml b/tests/components/zwave_proxy/test.esp32-c3-ard.yaml deleted file mode 100644 index b516342f3b..0000000000 --- a/tests/components/zwave_proxy/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/zyaura/test.esp32-ard.yaml b/tests/components/zyaura/test.esp32-ard.yaml deleted file mode 100644 index d295973e3f..0000000000 --- a/tests/components/zyaura/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 - -<<: !include common.yaml diff --git a/tests/components/zyaura/test.esp32-c3-ard.yaml b/tests/components/zyaura/test.esp32-c3-ard.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/zyaura/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml From 061e55f8c5de1689eb6e96b357b9e98cdf80ea49 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 08:45:45 -1000 Subject: [PATCH 008/526] [ci][ethernet][tests] Remove redundant Arduino tests for ethernet PHYs (#11137) --- tests/components/ethernet/test-dm9051.esp32-ard.yaml | 1 - tests/components/ethernet/test-dp83848.esp32-ard.yaml | 1 - tests/components/ethernet/test-ip101.esp32-ard.yaml | 1 - tests/components/ethernet/test-ksz8081.esp32-ard.yaml | 1 - tests/components/ethernet/test-ksz8081rna.esp32-ard.yaml | 1 - tests/components/ethernet/test-lan8670.esp32-ard.yaml | 1 - tests/components/ethernet/test-lan8720.esp32-ard.yaml | 1 - tests/components/ethernet/test-rtl8201.esp32-ard.yaml | 1 - tests/components/ethernet/test-w5500.esp32-ard.yaml | 1 - 9 files changed, 9 deletions(-) delete mode 100644 tests/components/ethernet/test-dm9051.esp32-ard.yaml delete mode 100644 tests/components/ethernet/test-dp83848.esp32-ard.yaml delete mode 100644 tests/components/ethernet/test-ip101.esp32-ard.yaml delete mode 100644 tests/components/ethernet/test-ksz8081.esp32-ard.yaml delete mode 100644 tests/components/ethernet/test-ksz8081rna.esp32-ard.yaml delete mode 100644 tests/components/ethernet/test-lan8670.esp32-ard.yaml delete mode 100644 tests/components/ethernet/test-lan8720.esp32-ard.yaml delete mode 100644 tests/components/ethernet/test-rtl8201.esp32-ard.yaml delete mode 100644 tests/components/ethernet/test-w5500.esp32-ard.yaml diff --git a/tests/components/ethernet/test-dm9051.esp32-ard.yaml b/tests/components/ethernet/test-dm9051.esp32-ard.yaml deleted file mode 100644 index 23e3b97740..0000000000 --- a/tests/components/ethernet/test-dm9051.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-dm9051.yaml diff --git a/tests/components/ethernet/test-dp83848.esp32-ard.yaml b/tests/components/ethernet/test-dp83848.esp32-ard.yaml deleted file mode 100644 index 906bfba17c..0000000000 --- a/tests/components/ethernet/test-dp83848.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-dp83848.yaml diff --git a/tests/components/ethernet/test-ip101.esp32-ard.yaml b/tests/components/ethernet/test-ip101.esp32-ard.yaml deleted file mode 100644 index e52329d7ea..0000000000 --- a/tests/components/ethernet/test-ip101.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-ip101.yaml diff --git a/tests/components/ethernet/test-ksz8081.esp32-ard.yaml b/tests/components/ethernet/test-ksz8081.esp32-ard.yaml deleted file mode 100644 index 8f3c750c77..0000000000 --- a/tests/components/ethernet/test-ksz8081.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-ksz8081.yaml diff --git a/tests/components/ethernet/test-ksz8081rna.esp32-ard.yaml b/tests/components/ethernet/test-ksz8081rna.esp32-ard.yaml deleted file mode 100644 index a48e591996..0000000000 --- a/tests/components/ethernet/test-ksz8081rna.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-ksz8081rna.yaml diff --git a/tests/components/ethernet/test-lan8670.esp32-ard.yaml b/tests/components/ethernet/test-lan8670.esp32-ard.yaml deleted file mode 100644 index 914a06ae88..0000000000 --- a/tests/components/ethernet/test-lan8670.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-lan8670.yaml diff --git a/tests/components/ethernet/test-lan8720.esp32-ard.yaml b/tests/components/ethernet/test-lan8720.esp32-ard.yaml deleted file mode 100644 index 3df9ac874a..0000000000 --- a/tests/components/ethernet/test-lan8720.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-lan8720.yaml diff --git a/tests/components/ethernet/test-rtl8201.esp32-ard.yaml b/tests/components/ethernet/test-rtl8201.esp32-ard.yaml deleted file mode 100644 index e69f88dc94..0000000000 --- a/tests/components/ethernet/test-rtl8201.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-rtl8201.yaml diff --git a/tests/components/ethernet/test-w5500.esp32-ard.yaml b/tests/components/ethernet/test-w5500.esp32-ard.yaml deleted file mode 100644 index 36f1b5365f..0000000000 --- a/tests/components/ethernet/test-w5500.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-w5500.yaml From 309e8b4c9256105645a481f9541baaa4cdf4dcf0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 09:17:04 -1000 Subject: [PATCH 009/526] [ci][improv_serial][tests] Remove redundant ESP32 Arduino test files (#11138) --- tests/components/improv_serial/test-uart0.esp32-ard.yaml | 1 - tests/components/improv_serial/test-uart0.esp32-c3-ard.yaml | 1 - tests/components/improv_serial/test-uart0.esp32-s2-ard.yaml | 1 - tests/components/improv_serial/test-uart0.esp32-s3-ard.yaml | 1 - tests/components/improv_serial/test-usb_cdc.esp32-c3-ard.yaml | 1 - tests/components/improv_serial/test-usb_cdc.esp32-s2-ard.yaml | 1 - tests/components/improv_serial/test-usb_cdc.esp32-s3-ard.yaml | 1 - 7 files changed, 7 deletions(-) delete mode 100644 tests/components/improv_serial/test-uart0.esp32-ard.yaml delete mode 100644 tests/components/improv_serial/test-uart0.esp32-c3-ard.yaml delete mode 100644 tests/components/improv_serial/test-uart0.esp32-s2-ard.yaml delete mode 100644 tests/components/improv_serial/test-uart0.esp32-s3-ard.yaml delete mode 100644 tests/components/improv_serial/test-usb_cdc.esp32-c3-ard.yaml delete mode 100644 tests/components/improv_serial/test-usb_cdc.esp32-s2-ard.yaml delete mode 100644 tests/components/improv_serial/test-usb_cdc.esp32-s3-ard.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-ard.yaml b/tests/components/improv_serial/test-uart0.esp32-ard.yaml deleted file mode 100644 index ef8c799241..0000000000 --- a/tests/components/improv_serial/test-uart0.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-c3-ard.yaml b/tests/components/improv_serial/test-uart0.esp32-c3-ard.yaml deleted file mode 100644 index ef8c799241..0000000000 --- a/tests/components/improv_serial/test-uart0.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-s2-ard.yaml b/tests/components/improv_serial/test-uart0.esp32-s2-ard.yaml deleted file mode 100644 index ef8c799241..0000000000 --- a/tests/components/improv_serial/test-uart0.esp32-s2-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-s3-ard.yaml b/tests/components/improv_serial/test-uart0.esp32-s3-ard.yaml deleted file mode 100644 index ef8c799241..0000000000 --- a/tests/components/improv_serial/test-uart0.esp32-s3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-usb_cdc.esp32-c3-ard.yaml b/tests/components/improv_serial/test-usb_cdc.esp32-c3-ard.yaml deleted file mode 100644 index cfdaec9771..0000000000 --- a/tests/components/improv_serial/test-usb_cdc.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-usb_cdc.yaml diff --git a/tests/components/improv_serial/test-usb_cdc.esp32-s2-ard.yaml b/tests/components/improv_serial/test-usb_cdc.esp32-s2-ard.yaml deleted file mode 100644 index cfdaec9771..0000000000 --- a/tests/components/improv_serial/test-usb_cdc.esp32-s2-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-usb_cdc.yaml diff --git a/tests/components/improv_serial/test-usb_cdc.esp32-s3-ard.yaml b/tests/components/improv_serial/test-usb_cdc.esp32-s3-ard.yaml deleted file mode 100644 index cfdaec9771..0000000000 --- a/tests/components/improv_serial/test-usb_cdc.esp32-s3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-usb_cdc.yaml From 5bbc2ab482eac93c5e25e324588dc47fc1d67546 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:40:40 +0000 Subject: [PATCH 010/526] Bump pyupgrade from 3.20.0 to 3.21.0 (#11139) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 76a305367a..51b8e6f8ed 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,7 +1,7 @@ pylint==3.3.9 flake8==7.3.0 # also change in .pre-commit-config.yaml when updating ruff==0.14.0 # also change in .pre-commit-config.yaml when updating -pyupgrade==3.20.0 # also change in .pre-commit-config.yaml when updating +pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating pre-commit # Unit tests From 5ca407e27c10492a6bb5bae16a6b8977ac9a99a3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 10:01:58 -1000 Subject: [PATCH 011/526] [mdns] Store TXT record values in flash to reduce heap usage (#11114) --- .../dashboard_import/dashboard_import.cpp | 2 +- .../dashboard_import/dashboard_import.h | 2 +- esphome/components/mdns/__init__.py | 56 ++++++--- esphome/components/mdns/mdns_component.cpp | 107 +++++++++--------- esphome/components/mdns/mdns_component.h | 13 ++- esphome/components/mdns/mdns_esp32.cpp | 12 +- esphome/components/mdns/mdns_esp8266.cpp | 2 +- esphome/components/mdns/mdns_libretiny.cpp | 3 +- esphome/components/mdns/mdns_rp2040.cpp | 3 +- esphome/components/openthread/openthread.cpp | 8 +- esphome/core/defines.h | 1 + .../mdns/test-comprehensive.esp8266-ard.yaml | 3 + 12 files changed, 119 insertions(+), 93 deletions(-) diff --git a/esphome/components/dashboard_import/dashboard_import.cpp b/esphome/components/dashboard_import/dashboard_import.cpp index 6875fd61a5..c04696fd53 100644 --- a/esphome/components/dashboard_import/dashboard_import.cpp +++ b/esphome/components/dashboard_import/dashboard_import.cpp @@ -5,7 +5,7 @@ namespace dashboard_import { static std::string g_package_import_url; // NOLINT -std::string get_package_import_url() { return g_package_import_url; } +const std::string &get_package_import_url() { return g_package_import_url; } void set_package_import_url(std::string url) { g_package_import_url = std::move(url); } } // namespace dashboard_import diff --git a/esphome/components/dashboard_import/dashboard_import.h b/esphome/components/dashboard_import/dashboard_import.h index 0ca2994aab..edcda6b803 100644 --- a/esphome/components/dashboard_import/dashboard_import.h +++ b/esphome/components/dashboard_import/dashboard_import.h @@ -5,7 +5,7 @@ namespace esphome { namespace dashboard_import { -std::string get_package_import_url(); +const std::string &get_package_import_url(); void set_package_import_url(std::string url); } // namespace dashboard_import diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 3fa4d2ebef..6e148092fe 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -58,14 +58,6 @@ CONFIG_SCHEMA = cv.All( ) -def mdns_txt_record(key: str, value: str): - return cg.StructInitializer( - MDNSTXTRecord, - ("key", cg.RawExpression(f"MDNS_STR({cg.safe_exp(key)})")), - ("value", value), - ) - - def mdns_service( service: str, proto: str, port: int, txt_records: list[dict[str, str]] ): @@ -107,23 +99,53 @@ async def to_code(config): # Ensure at least 1 service (fallback service) cg.add_define("MDNS_SERVICE_COUNT", max(1, service_count)) + # Calculate compile-time dynamic TXT value count + # Dynamic values are those that cannot be stored in flash at compile time + dynamic_txt_count = 0 + if "api" in CORE.config: + # Always: get_mac_address() + dynamic_txt_count += 1 + # User-provided templatable TXT values (only lambdas, not static strings) + dynamic_txt_count += sum( + 1 + for service in config[CONF_SERVICES] + for txt_value in service[CONF_TXT].values() + if cg.is_template(txt_value) + ) + + # Ensure at least 1 to avoid zero-size array + cg.add_define("MDNS_DYNAMIC_TXT_COUNT", max(1, dynamic_txt_count)) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) for service in config[CONF_SERVICES]: - txt = [ - cg.StructInitializer( - MDNSTXTRecord, - ("key", cg.RawExpression(f"MDNS_STR({cg.safe_exp(txt_key)})")), - ("value", await cg.templatable(txt_value, [], cg.std_string)), - ) - for txt_key, txt_value in service[CONF_TXT].items() - ] + # Build the txt records list for the service + txt_records = [] + for txt_key, txt_value in service[CONF_TXT].items(): + if cg.is_template(txt_value): + # It's a lambda - evaluate and store using helper + templated_value = await cg.templatable(txt_value, [], cg.std_string) + safe_key = cg.safe_exp(txt_key) + dynamic_call = f"{var}->add_dynamic_txt_value(({templated_value})())" + txt_records.append( + cg.RawExpression( + f"{{MDNS_STR({safe_key}), MDNS_STR({dynamic_call})}}" + ) + ) + else: + # It's a static string - use directly in flash, no need to store in vector + txt_records.append( + cg.RawExpression( + f"{{MDNS_STR({cg.safe_exp(txt_key)}), MDNS_STR({cg.safe_exp(txt_value)})}}" + ) + ) + exp = mdns_service( service[CONF_SERVICE], service[CONF_PROTOCOL], await cg.templatable(service[CONF_PORT], [], cg.uint16), - txt, + txt_records, ) cg.add(var.add_extra_service(exp)) diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index 8945053b7d..9cb664c3c3 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -9,21 +9,9 @@ #include // Macro to define strings in PROGMEM on ESP8266, regular memory on other platforms #define MDNS_STATIC_CONST_CHAR(name, value) static const char name[] PROGMEM = value -// Helper to convert PROGMEM string to std::string for TemplatableValue -// Only define this function if we have services that will use it -#if defined(USE_API) || defined(USE_PROMETHEUS) || defined(USE_WEBSERVER) || defined(USE_MDNS_EXTRA_SERVICES) -static std::string mdns_str_value(PGM_P str) { - char buf[64]; - strncpy_P(buf, str, sizeof(buf) - 1); - buf[sizeof(buf) - 1] = '\0'; - return std::string(buf); -} -#define MDNS_STR_VALUE(name) mdns_str_value(name) -#endif #else // On non-ESP8266 platforms, use regular const char* #define MDNS_STATIC_CONST_CHAR(name, value) static constexpr const char name[] = value -#define MDNS_STR_VALUE(name) std::string(name) #endif #ifdef USE_API @@ -43,30 +31,10 @@ static const char *const TAG = "mdns"; #endif // Define all constant strings using the macro -MDNS_STATIC_CONST_CHAR(SERVICE_ESPHOMELIB, "_esphomelib"); MDNS_STATIC_CONST_CHAR(SERVICE_TCP, "_tcp"); -MDNS_STATIC_CONST_CHAR(SERVICE_PROMETHEUS, "_prometheus-http"); -MDNS_STATIC_CONST_CHAR(SERVICE_HTTP, "_http"); -MDNS_STATIC_CONST_CHAR(TXT_FRIENDLY_NAME, "friendly_name"); -MDNS_STATIC_CONST_CHAR(TXT_VERSION, "version"); -MDNS_STATIC_CONST_CHAR(TXT_MAC, "mac"); -MDNS_STATIC_CONST_CHAR(TXT_PLATFORM, "platform"); -MDNS_STATIC_CONST_CHAR(TXT_BOARD, "board"); -MDNS_STATIC_CONST_CHAR(TXT_NETWORK, "network"); -MDNS_STATIC_CONST_CHAR(TXT_API_ENCRYPTION, "api_encryption"); -MDNS_STATIC_CONST_CHAR(TXT_API_ENCRYPTION_SUPPORTED, "api_encryption_supported"); -MDNS_STATIC_CONST_CHAR(TXT_PROJECT_NAME, "project_name"); -MDNS_STATIC_CONST_CHAR(TXT_PROJECT_VERSION, "project_version"); -MDNS_STATIC_CONST_CHAR(TXT_PACKAGE_IMPORT_URL, "package_import_url"); - -MDNS_STATIC_CONST_CHAR(PLATFORM_ESP8266, "ESP8266"); -MDNS_STATIC_CONST_CHAR(PLATFORM_ESP32, "ESP32"); -MDNS_STATIC_CONST_CHAR(PLATFORM_RP2040, "RP2040"); - -MDNS_STATIC_CONST_CHAR(NETWORK_WIFI, "wifi"); -MDNS_STATIC_CONST_CHAR(NETWORK_ETHERNET, "ethernet"); -MDNS_STATIC_CONST_CHAR(NETWORK_THREAD, "thread"); +// Wrap build-time defines into flash storage +MDNS_STATIC_CONST_CHAR(VALUE_VERSION, ESPHOME_VERSION); void MDNSComponent::compile_records_() { this->hostname_ = App.get_name(); @@ -75,6 +43,15 @@ void MDNSComponent::compile_records_() { // in mdns/__init__.py. If you add a new service here, update both locations. #ifdef USE_API + MDNS_STATIC_CONST_CHAR(SERVICE_ESPHOMELIB, "_esphomelib"); + MDNS_STATIC_CONST_CHAR(TXT_FRIENDLY_NAME, "friendly_name"); + MDNS_STATIC_CONST_CHAR(TXT_VERSION, "version"); + MDNS_STATIC_CONST_CHAR(TXT_MAC, "mac"); + MDNS_STATIC_CONST_CHAR(TXT_PLATFORM, "platform"); + MDNS_STATIC_CONST_CHAR(TXT_BOARD, "board"); + MDNS_STATIC_CONST_CHAR(TXT_NETWORK, "network"); + MDNS_STATIC_CONST_CHAR(VALUE_BOARD, ESPHOME_BOARD); + if (api::global_api_server != nullptr) { auto &service = this->services_.emplace_next(); service.service_type = MDNS_STR(SERVICE_ESPHOMELIB); @@ -109,52 +86,66 @@ void MDNSComponent::compile_records_() { txt_records.reserve(txt_count); if (!friendly_name_empty) { - txt_records.push_back({MDNS_STR(TXT_FRIENDLY_NAME), friendly_name}); + txt_records.push_back({MDNS_STR(TXT_FRIENDLY_NAME), MDNS_STR(friendly_name.c_str())}); } - txt_records.push_back({MDNS_STR(TXT_VERSION), ESPHOME_VERSION}); - txt_records.push_back({MDNS_STR(TXT_MAC), get_mac_address()}); + txt_records.push_back({MDNS_STR(TXT_VERSION), MDNS_STR(VALUE_VERSION)}); + txt_records.push_back({MDNS_STR(TXT_MAC), MDNS_STR(this->add_dynamic_txt_value(get_mac_address()))}); #ifdef USE_ESP8266 - txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR_VALUE(PLATFORM_ESP8266)}); + MDNS_STATIC_CONST_CHAR(PLATFORM_ESP8266, "ESP8266"); + txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_ESP8266)}); #elif defined(USE_ESP32) - txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR_VALUE(PLATFORM_ESP32)}); + MDNS_STATIC_CONST_CHAR(PLATFORM_ESP32, "ESP32"); + txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_ESP32)}); #elif defined(USE_RP2040) - txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR_VALUE(PLATFORM_RP2040)}); + MDNS_STATIC_CONST_CHAR(PLATFORM_RP2040, "RP2040"); + txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(PLATFORM_RP2040)}); #elif defined(USE_LIBRETINY) - txt_records.push_back({MDNS_STR(TXT_PLATFORM), lt_cpu_get_model_name()}); + txt_records.push_back({MDNS_STR(TXT_PLATFORM), MDNS_STR(lt_cpu_get_model_name())}); #endif - txt_records.push_back({MDNS_STR(TXT_BOARD), ESPHOME_BOARD}); + txt_records.push_back({MDNS_STR(TXT_BOARD), MDNS_STR(VALUE_BOARD)}); #if defined(USE_WIFI) - txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR_VALUE(NETWORK_WIFI)}); + MDNS_STATIC_CONST_CHAR(NETWORK_WIFI, "wifi"); + txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_WIFI)}); #elif defined(USE_ETHERNET) - txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR_VALUE(NETWORK_ETHERNET)}); + MDNS_STATIC_CONST_CHAR(NETWORK_ETHERNET, "ethernet"); + txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_ETHERNET)}); #elif defined(USE_OPENTHREAD) - txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR_VALUE(NETWORK_THREAD)}); + MDNS_STATIC_CONST_CHAR(NETWORK_THREAD, "thread"); + txt_records.push_back({MDNS_STR(TXT_NETWORK), MDNS_STR(NETWORK_THREAD)}); #endif #ifdef USE_API_NOISE + MDNS_STATIC_CONST_CHAR(TXT_API_ENCRYPTION, "api_encryption"); + MDNS_STATIC_CONST_CHAR(TXT_API_ENCRYPTION_SUPPORTED, "api_encryption_supported"); MDNS_STATIC_CONST_CHAR(NOISE_ENCRYPTION, "Noise_NNpsk0_25519_ChaChaPoly_SHA256"); - if (api::global_api_server->get_noise_ctx()->has_psk()) { - txt_records.push_back({MDNS_STR(TXT_API_ENCRYPTION), MDNS_STR_VALUE(NOISE_ENCRYPTION)}); - } else { - txt_records.push_back({MDNS_STR(TXT_API_ENCRYPTION_SUPPORTED), MDNS_STR_VALUE(NOISE_ENCRYPTION)}); - } + bool has_psk = api::global_api_server->get_noise_ctx()->has_psk(); + const char *encryption_key = has_psk ? TXT_API_ENCRYPTION : TXT_API_ENCRYPTION_SUPPORTED; + txt_records.push_back({MDNS_STR(encryption_key), MDNS_STR(NOISE_ENCRYPTION)}); #endif #ifdef ESPHOME_PROJECT_NAME - txt_records.push_back({MDNS_STR(TXT_PROJECT_NAME), ESPHOME_PROJECT_NAME}); - txt_records.push_back({MDNS_STR(TXT_PROJECT_VERSION), ESPHOME_PROJECT_VERSION}); + MDNS_STATIC_CONST_CHAR(TXT_PROJECT_NAME, "project_name"); + MDNS_STATIC_CONST_CHAR(TXT_PROJECT_VERSION, "project_version"); + MDNS_STATIC_CONST_CHAR(VALUE_PROJECT_NAME, ESPHOME_PROJECT_NAME); + MDNS_STATIC_CONST_CHAR(VALUE_PROJECT_VERSION, ESPHOME_PROJECT_VERSION); + txt_records.push_back({MDNS_STR(TXT_PROJECT_NAME), MDNS_STR(VALUE_PROJECT_NAME)}); + txt_records.push_back({MDNS_STR(TXT_PROJECT_VERSION), MDNS_STR(VALUE_PROJECT_VERSION)}); #endif // ESPHOME_PROJECT_NAME #ifdef USE_DASHBOARD_IMPORT - txt_records.push_back({MDNS_STR(TXT_PACKAGE_IMPORT_URL), dashboard_import::get_package_import_url()}); + MDNS_STATIC_CONST_CHAR(TXT_PACKAGE_IMPORT_URL, "package_import_url"); + txt_records.push_back( + {MDNS_STR(TXT_PACKAGE_IMPORT_URL), MDNS_STR(dashboard_import::get_package_import_url().c_str())}); #endif } #endif // USE_API #ifdef USE_PROMETHEUS + MDNS_STATIC_CONST_CHAR(SERVICE_PROMETHEUS, "_prometheus-http"); + auto &prom_service = this->services_.emplace_next(); prom_service.service_type = MDNS_STR(SERVICE_PROMETHEUS); prom_service.proto = MDNS_STR(SERVICE_TCP); @@ -162,6 +153,8 @@ void MDNSComponent::compile_records_() { #endif #ifdef USE_WEBSERVER + MDNS_STATIC_CONST_CHAR(SERVICE_HTTP, "_http"); + auto &web_service = this->services_.emplace_next(); web_service.service_type = MDNS_STR(SERVICE_HTTP); web_service.proto = MDNS_STR(SERVICE_TCP); @@ -169,13 +162,16 @@ void MDNSComponent::compile_records_() { #endif #if !defined(USE_API) && !defined(USE_PROMETHEUS) && !defined(USE_WEBSERVER) && !defined(USE_MDNS_EXTRA_SERVICES) + MDNS_STATIC_CONST_CHAR(SERVICE_HTTP, "_http"); + MDNS_STATIC_CONST_CHAR(TXT_VERSION, "version"); + // Publish "http" service if not using native API or any other services // This is just to have *some* mDNS service so that .local resolution works auto &fallback_service = this->services_.emplace_next(); fallback_service.service_type = MDNS_STR(SERVICE_HTTP); fallback_service.proto = MDNS_STR(SERVICE_TCP); fallback_service.port = USE_WEBSERVER_PORT; - fallback_service.txt_records.push_back({MDNS_STR(TXT_VERSION), ESPHOME_VERSION}); + fallback_service.txt_records.push_back({MDNS_STR(TXT_VERSION), MDNS_STR(VALUE_VERSION)}); #endif } @@ -190,8 +186,7 @@ void MDNSComponent::dump_config() { ESP_LOGV(TAG, " - %s, %s, %d", MDNS_STR_ARG(service.service_type), MDNS_STR_ARG(service.proto), const_cast &>(service.port).value()); for (const auto &record : service.txt_records) { - ESP_LOGV(TAG, " TXT: %s = %s", MDNS_STR_ARG(record.key), - const_cast &>(record.value).value().c_str()); + ESP_LOGV(TAG, " TXT: %s = %s", MDNS_STR_ARG(record.key), MDNS_STR_ARG(record.value)); } } #endif diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index b1f73fbb32..141e42d976 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -27,7 +27,7 @@ struct MDNSString; struct MDNSTXTRecord { const MDNSString *key; - TemplatableValue value; + const MDNSString *value; }; struct MDNSService { @@ -59,6 +59,17 @@ class MDNSComponent : public Component { void on_shutdown() override; + /// Add a dynamic TXT value and return pointer to it for use in MDNSTXTRecord + const char *add_dynamic_txt_value(const std::string &value) { + this->dynamic_txt_values_.push_back(value); + return this->dynamic_txt_values_[this->dynamic_txt_values_.size() - 1].c_str(); + } + + /// Storage for runtime-generated TXT values (MAC address, user lambdas) + /// Pre-sized at compile time via MDNS_DYNAMIC_TXT_COUNT to avoid heap allocations. + /// Static/compile-time values (version, board, etc.) are stored directly in flash and don't use this. + StaticVector dynamic_txt_values_; + protected: StaticVector services_{}; std::string hostname_; diff --git a/esphome/components/mdns/mdns_esp32.cpp b/esphome/components/mdns/mdns_esp32.cpp index 40d305a1e6..e77c0b9b05 100644 --- a/esphome/components/mdns/mdns_esp32.cpp +++ b/esphome/components/mdns/mdns_esp32.cpp @@ -2,7 +2,6 @@ #if defined(USE_ESP32) && defined(USE_MDNS) #include -#include #include "esphome/core/hal.h" #include "esphome/core/log.h" #include "mdns_component.h" @@ -29,21 +28,16 @@ void MDNSComponent::setup() { std::vector txt_records; for (const auto &record : service.txt_records) { mdns_txt_item_t it{}; - // key is a compile-time string literal in flash, no need to strdup + // key and value are either compile-time string literals in flash or pointers to dynamic_txt_values_ + // Both remain valid for the lifetime of this function, and ESP-IDF makes internal copies it.key = MDNS_STR_ARG(record.key); - // value is a temporary from TemplatableValue, must strdup to keep it alive - it.value = strdup(const_cast &>(record.value).value().c_str()); + it.value = MDNS_STR_ARG(record.value); txt_records.push_back(it); } uint16_t port = const_cast &>(service.port).value(); err = mdns_service_add(nullptr, MDNS_STR_ARG(service.service_type), MDNS_STR_ARG(service.proto), port, txt_records.data(), txt_records.size()); - // free records - for (const auto &it : txt_records) { - free((void *) it.value); // NOLINT(cppcoreguidelines-no-malloc) - } - if (err != ESP_OK) { ESP_LOGW(TAG, "Failed to register service %s: %s", MDNS_STR_ARG(service.service_type), esp_err_to_name(err)); } diff --git a/esphome/components/mdns/mdns_esp8266.cpp b/esphome/components/mdns/mdns_esp8266.cpp index f1c8909807..f3779042ed 100644 --- a/esphome/components/mdns/mdns_esp8266.cpp +++ b/esphome/components/mdns/mdns_esp8266.cpp @@ -33,7 +33,7 @@ void MDNSComponent::setup() { MDNS.addService(FPSTR(service_type), FPSTR(proto), port); for (const auto &record : service.txt_records) { MDNS.addServiceTxt(FPSTR(service_type), FPSTR(proto), FPSTR(MDNS_STR_ARG(record.key)), - const_cast &>(record.value).value().c_str()); + FPSTR(MDNS_STR_ARG(record.value))); } } } diff --git a/esphome/components/mdns/mdns_libretiny.cpp b/esphome/components/mdns/mdns_libretiny.cpp index 9010ca2bc6..5540bf361a 100644 --- a/esphome/components/mdns/mdns_libretiny.cpp +++ b/esphome/components/mdns/mdns_libretiny.cpp @@ -32,8 +32,7 @@ void MDNSComponent::setup() { uint16_t port_ = const_cast &>(service.port).value(); MDNS.addService(service_type, proto, port_); for (const auto &record : service.txt_records) { - MDNS.addServiceTxt(service_type, proto, MDNS_STR_ARG(record.key), - const_cast &>(record.value).value().c_str()); + MDNS.addServiceTxt(service_type, proto, MDNS_STR_ARG(record.key), MDNS_STR_ARG(record.value)); } } } diff --git a/esphome/components/mdns/mdns_rp2040.cpp b/esphome/components/mdns/mdns_rp2040.cpp index 039453f501..5ad006f5d4 100644 --- a/esphome/components/mdns/mdns_rp2040.cpp +++ b/esphome/components/mdns/mdns_rp2040.cpp @@ -32,8 +32,7 @@ void MDNSComponent::setup() { uint16_t port = const_cast &>(service.port).value(); MDNS.addService(service_type, proto, port); for (const auto &record : service.txt_records) { - MDNS.addServiceTxt(service_type, proto, MDNS_STR_ARG(record.key), - const_cast &>(record.value).value().c_str()); + MDNS.addServiceTxt(service_type, proto, MDNS_STR_ARG(record.key), MDNS_STR_ARG(record.value)); } } } diff --git a/esphome/components/openthread/openthread.cpp b/esphome/components/openthread/openthread.cpp index bc5dcadef6..b2c2519c08 100644 --- a/esphome/components/openthread/openthread.cpp +++ b/esphome/components/openthread/openthread.cpp @@ -180,10 +180,12 @@ void OpenThreadSrpComponent::setup() { entry->mService.mNumTxtEntries = service.txt_records.size(); for (size_t i = 0; i < service.txt_records.size(); i++) { const auto &txt = service.txt_records[i]; - auto value = const_cast &>(txt.value).value(); + // Value is either a compile-time string literal in flash or a pointer to dynamic_txt_values_ + // OpenThread SRP client expects the data to persist, so we strdup it + const char *value_str = MDNS_STR_ARG(txt.value); txt_entries[i].mKey = MDNS_STR_ARG(txt.key); - txt_entries[i].mValue = reinterpret_cast(strdup(value.c_str())); - txt_entries[i].mValueLength = value.size(); + txt_entries[i].mValue = reinterpret_cast(strdup(value_str)); + txt_entries[i].mValueLength = strlen(value_str); } entry->mService.mTxtEntries = txt_entries; entry->mService.mNumTxtEntries = service.txt_records.size(); diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 2317c0ed32..0f1d1bcf28 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -84,6 +84,7 @@ #define USE_LVGL_TOUCHSCREEN #define USE_MDNS #define MDNS_SERVICE_COUNT 3 +#define MDNS_DYNAMIC_TXT_COUNT 3 #define USE_MEDIA_PLAYER #define USE_NEXTION_TFT_UPLOAD #define USE_NUMBER diff --git a/tests/components/mdns/test-comprehensive.esp8266-ard.yaml b/tests/components/mdns/test-comprehensive.esp8266-ard.yaml index 02767833a3..3129ca3143 100644 --- a/tests/components/mdns/test-comprehensive.esp8266-ard.yaml +++ b/tests/components/mdns/test-comprehensive.esp8266-ard.yaml @@ -25,6 +25,9 @@ mdns: - service: _http protocol: _tcp port: 80 + txt: + version: "1.0" + path: "/" # OTA should run at priority 54 (after mdns) ota: From 87d2c9868fcbdb941979bef9652eb8336de235e4 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 9 Oct 2025 17:27:36 -0400 Subject: [PATCH 012/526] [esp32] Update IDF 5.5 and Arduino 3.3 to use 55.03.31-1 (#11120) --- esphome/components/esp32/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 3cbcdb99b4..0bb0f46bb3 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -329,7 +329,8 @@ ARDUINO_FRAMEWORK_VERSION_LOOKUP = { "dev": cv.Version(3, 3, 1), } ARDUINO_PLATFORM_VERSION_LOOKUP = { - cv.Version(3, 3, 1): cv.Version(55, 3, 31), + cv.Version(3, 3, 2): cv.Version(55, 3, 31, "1"), + cv.Version(3, 3, 1): cv.Version(55, 3, 31, "1"), cv.Version(3, 3, 0): cv.Version(55, 3, 30, "2"), cv.Version(3, 2, 1): cv.Version(54, 3, 21, "2"), cv.Version(3, 2, 0): cv.Version(54, 3, 20), @@ -347,8 +348,8 @@ ESP_IDF_FRAMEWORK_VERSION_LOOKUP = { "dev": cv.Version(5, 5, 1), } ESP_IDF_PLATFORM_VERSION_LOOKUP = { - cv.Version(5, 5, 1): cv.Version(55, 3, 31), - cv.Version(5, 5, 0): cv.Version(55, 3, 31), + cv.Version(5, 5, 1): cv.Version(55, 3, 31, "1"), + cv.Version(5, 5, 0): cv.Version(55, 3, 31, "1"), cv.Version(5, 4, 2): cv.Version(54, 3, 21, "2"), cv.Version(5, 4, 1): cv.Version(54, 3, 21, "2"), cv.Version(5, 4, 0): cv.Version(54, 3, 21, "2"), @@ -363,8 +364,8 @@ ESP_IDF_PLATFORM_VERSION_LOOKUP = { # - https://github.com/pioarduino/platform-espressif32/releases PLATFORM_VERSION_LOOKUP = { "recommended": cv.Version(54, 3, 21, "2"), - "latest": cv.Version(55, 3, 31), - "dev": "https://github.com/pioarduino/platform-espressif32.git#develop", + "latest": cv.Version(55, 3, 31, "1"), + "dev": cv.Version(55, 3, 31, "1"), } From 81bf2688b4e8d2f76300305688aa9fb11ab5eded Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 11:36:31 -1000 Subject: [PATCH 013/526] [esp32] Update migration warning for Arduino-as-IDF-component transition (#11142) --- esphome/components/esp32/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 0bb0f46bb3..d9b8d067a4 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -657,6 +657,7 @@ def _show_framework_migration_message(name: str, variant: str) -> None: + "Why change? ESP-IDF offers:\n" + color(AnsiFore.GREEN, " ✨ Up to 40% smaller binaries\n") + color(AnsiFore.GREEN, " 🚀 Better performance and optimization\n") + + color(AnsiFore.GREEN, " ⚡ 2-3x faster compile times\n") + color(AnsiFore.GREEN, " 📦 Custom-built firmware for your exact needs\n") + color( AnsiFore.GREEN, @@ -664,7 +665,6 @@ def _show_framework_migration_message(name: str, variant: str) -> None: ) + "\n" + "Trade-offs:\n" - + color(AnsiFore.YELLOW, " ⏱️ Compile times are ~25% longer\n") + color(AnsiFore.YELLOW, " 🔄 Some components need migration\n") + "\n" + "What should I do?\n" From c8b898f9c547342fc15bb6d83500b56f145db230 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 11:40:47 -1000 Subject: [PATCH 014/526] [ci][mdns][tests] Remove redundant ESP32 Arduino test files (#11143) --- tests/components/mdns/test-enabled.esp32-ard.yaml | 1 - tests/components/mdns/test-enabled.esp32-c3-ard.yaml | 1 - 2 files changed, 2 deletions(-) delete mode 100644 tests/components/mdns/test-enabled.esp32-ard.yaml delete mode 100644 tests/components/mdns/test-enabled.esp32-c3-ard.yaml diff --git a/tests/components/mdns/test-enabled.esp32-ard.yaml b/tests/components/mdns/test-enabled.esp32-ard.yaml deleted file mode 100644 index 97fd63d70e..0000000000 --- a/tests/components/mdns/test-enabled.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-enabled.yaml diff --git a/tests/components/mdns/test-enabled.esp32-c3-ard.yaml b/tests/components/mdns/test-enabled.esp32-c3-ard.yaml deleted file mode 100644 index 97fd63d70e..0000000000 --- a/tests/components/mdns/test-enabled.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-enabled.yaml From a74fcbc8b63b496dc0d7fe5b11d95b5a52980d25 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 11:42:25 -1000 Subject: [PATCH 015/526] [esp32_ble_beacon, esp32_ble_tracker] Remove unused Arduino includes and redundant tests (#11140) --- esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp | 4 ---- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp | 4 ---- tests/components/esp32_ble_beacon/test.esp32-ard.yaml | 1 - tests/components/esp32_ble_beacon/test.esp32-c3-ard.yaml | 1 - tests/components/esp32_ble_tracker/test.esp32-ard.yaml | 5 ----- tests/components/esp32_ble_tracker/test.esp32-c3-ard.yaml | 5 ----- 6 files changed, 20 deletions(-) delete mode 100644 tests/components/esp32_ble_beacon/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_ble_beacon/test.esp32-c3-ard.yaml delete mode 100644 tests/components/esp32_ble_tracker/test.esp32-ard.yaml delete mode 100644 tests/components/esp32_ble_tracker/test.esp32-c3-ard.yaml diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp index 259628e00f..af28804013 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp @@ -14,10 +14,6 @@ #include "esphome/core/hal.h" #include "esphome/core/helpers.h" -#ifdef USE_ARDUINO -#include -#endif - namespace esphome { namespace esp32_ble_beacon { diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index a7d73a9709..83f59d492e 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -25,10 +25,6 @@ #include #endif -#ifdef USE_ARDUINO -#include -#endif - #define MBEDTLS_AES_ALT #include diff --git a/tests/components/esp32_ble_beacon/test.esp32-ard.yaml b/tests/components/esp32_ble_beacon/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_ble_beacon/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_ble_beacon/test.esp32-c3-ard.yaml b/tests/components/esp32_ble_beacon/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_ble_beacon/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/esp32_ble_tracker/test.esp32-ard.yaml b/tests/components/esp32_ble_tracker/test.esp32-ard.yaml deleted file mode 100644 index 3bfdb8773f..0000000000 --- a/tests/components/esp32_ble_tracker/test.esp32-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -<<: !include common.yaml - -esp32_ble_tracker: - software_coexistence: true - max_connections: 3 diff --git a/tests/components/esp32_ble_tracker/test.esp32-c3-ard.yaml b/tests/components/esp32_ble_tracker/test.esp32-c3-ard.yaml deleted file mode 100644 index 2e3c48117a..0000000000 --- a/tests/components/esp32_ble_tracker/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -<<: !include common.yaml - -esp32_ble_tracker: - max_connections: 3 - software_coexistence: false From 9f9c95dd09054c346acaa91ce5c3eaa2d0d1b3bd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 11:56:53 -1000 Subject: [PATCH 016/526] [network][ci][tests] Remove redundant ESP32 Arduino test files (#11148) --- tests/components/network/test-ipv6.esp32-ard.yaml | 4 ---- tests/components/network/test-ipv6.esp32-c3-ard.yaml | 4 ---- tests/components/network/test.esp32-ard.yaml | 1 - tests/components/network/test.esp32-c3-ard.yaml | 1 - 4 files changed, 10 deletions(-) delete mode 100644 tests/components/network/test-ipv6.esp32-ard.yaml delete mode 100644 tests/components/network/test-ipv6.esp32-c3-ard.yaml delete mode 100644 tests/components/network/test.esp32-ard.yaml delete mode 100644 tests/components/network/test.esp32-c3-ard.yaml diff --git a/tests/components/network/test-ipv6.esp32-ard.yaml b/tests/components/network/test-ipv6.esp32-ard.yaml deleted file mode 100644 index da1324b17e..0000000000 --- a/tests/components/network/test-ipv6.esp32-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - network_enable_ipv6: "true" - -<<: !include common.yaml diff --git a/tests/components/network/test-ipv6.esp32-c3-ard.yaml b/tests/components/network/test-ipv6.esp32-c3-ard.yaml deleted file mode 100644 index da1324b17e..0000000000 --- a/tests/components/network/test-ipv6.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - network_enable_ipv6: "true" - -<<: !include common.yaml diff --git a/tests/components/network/test.esp32-ard.yaml b/tests/components/network/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/network/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/network/test.esp32-c3-ard.yaml b/tests/components/network/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/network/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml From 48474c0f8c21413de961cae4365039c2fcf06b60 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 11:57:11 -1000 Subject: [PATCH 017/526] [ci][time][tests] Remove redundant ESP32 Arduino test files (#11147) --- tests/components/time/test.esp32-ard.yaml | 1 - tests/components/time/test.esp32-c3-ard.yaml | 1 - 2 files changed, 2 deletions(-) delete mode 100644 tests/components/time/test.esp32-ard.yaml delete mode 100644 tests/components/time/test.esp32-c3-ard.yaml diff --git a/tests/components/time/test.esp32-ard.yaml b/tests/components/time/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/time/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/time/test.esp32-c3-ard.yaml b/tests/components/time/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/time/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml From fcc8a809e6755e158f46b50bae73c25bdfedc765 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 11:57:40 -1000 Subject: [PATCH 018/526] [ci][debug][tests] Remove redundant ESP32 variant Arduino test files (#11146) --- tests/components/debug/test.esp32-c3-ard.yaml | 4 ---- tests/components/debug/test.esp32-s2-ard.yaml | 1 - tests/components/debug/test.esp32-s3-ard.yaml | 1 - 3 files changed, 6 deletions(-) delete mode 100644 tests/components/debug/test.esp32-c3-ard.yaml delete mode 100644 tests/components/debug/test.esp32-s2-ard.yaml delete mode 100644 tests/components/debug/test.esp32-s3-ard.yaml diff --git a/tests/components/debug/test.esp32-c3-ard.yaml b/tests/components/debug/test.esp32-c3-ard.yaml deleted file mode 100644 index 7d43491862..0000000000 --- a/tests/components/debug/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -<<: !include common.yaml - -esp32: - cpu_frequency: 80MHz diff --git a/tests/components/debug/test.esp32-s2-ard.yaml b/tests/components/debug/test.esp32-s2-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/debug/test.esp32-s2-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/debug/test.esp32-s3-ard.yaml b/tests/components/debug/test.esp32-s3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/debug/test.esp32-s3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml From a1b0ae78e098aa09cb802335894fd4efbd4b055b Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:10:09 -0400 Subject: [PATCH 019/526] [stale] Increase operations-per-run (#11135) CI passed, stuck on status --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 63a8ade37f..5843b3a5e0 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -23,7 +23,7 @@ jobs: with: debug-only: ${{ github.ref != 'refs/heads/dev' }} # Dry-run when not run on dev branch remove-stale-when-updated: true - operations-per-run: 150 + operations-per-run: 400 # The 90 day stale policy for PRs # - PRs From b5cc668a45d19fc4e98d096eedf4003de89d08f0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 13:30:05 -1000 Subject: [PATCH 020/526] [ci][logger][tests] Remove redundant ESP32 Arduino test files (#11144) --- ...-usb_cdc.esp32-c3-ard.yaml => test-usb_cdc.esp32-c3-idf.yaml} | 0 tests/components/logger/test-usb_cdc.esp32-s3-ard.yaml | 1 - ...-usb_cdc.esp32-s2-ard.yaml => test-usb_cdc.esp32-s3-idf.yaml} | 0 tests/components/logger/test.esp32-ard.yaml | 1 - tests/components/logger/test.esp32-c3-ard.yaml | 1 - 5 files changed, 3 deletions(-) rename tests/components/logger/{test-usb_cdc.esp32-c3-ard.yaml => test-usb_cdc.esp32-c3-idf.yaml} (100%) delete mode 100644 tests/components/logger/test-usb_cdc.esp32-s3-ard.yaml rename tests/components/logger/{test-usb_cdc.esp32-s2-ard.yaml => test-usb_cdc.esp32-s3-idf.yaml} (100%) delete mode 100644 tests/components/logger/test.esp32-ard.yaml delete mode 100644 tests/components/logger/test.esp32-c3-ard.yaml diff --git a/tests/components/logger/test-usb_cdc.esp32-c3-ard.yaml b/tests/components/logger/test-usb_cdc.esp32-c3-idf.yaml similarity index 100% rename from tests/components/logger/test-usb_cdc.esp32-c3-ard.yaml rename to tests/components/logger/test-usb_cdc.esp32-c3-idf.yaml diff --git a/tests/components/logger/test-usb_cdc.esp32-s3-ard.yaml b/tests/components/logger/test-usb_cdc.esp32-s3-ard.yaml deleted file mode 100644 index cfdaec9771..0000000000 --- a/tests/components/logger/test-usb_cdc.esp32-s3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-usb_cdc.yaml diff --git a/tests/components/logger/test-usb_cdc.esp32-s2-ard.yaml b/tests/components/logger/test-usb_cdc.esp32-s3-idf.yaml similarity index 100% rename from tests/components/logger/test-usb_cdc.esp32-s2-ard.yaml rename to tests/components/logger/test-usb_cdc.esp32-s3-idf.yaml diff --git a/tests/components/logger/test.esp32-ard.yaml b/tests/components/logger/test.esp32-ard.yaml deleted file mode 100644 index 3fe04e18a3..0000000000 --- a/tests/components/logger/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-default_uart.yaml diff --git a/tests/components/logger/test.esp32-c3-ard.yaml b/tests/components/logger/test.esp32-c3-ard.yaml deleted file mode 100644 index 3fe04e18a3..0000000000 --- a/tests/components/logger/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-default_uart.yaml From e15429b0f5e737f3b35a7d279d83303eea14b823 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 13:38:34 -1000 Subject: [PATCH 021/526] [opentherm][ci][tests] Remove redundant ESP32 Arduino tests and simplify conditionals (#11149) --- esphome/components/opentherm/opentherm.cpp | 10 +++++----- esphome/components/opentherm/opentherm.h | 6 +++--- tests/components/opentherm/test.esp32-ard.yaml | 1 - tests/components/opentherm/test.esp32-c3-ard.yaml | 1 - 4 files changed, 8 insertions(+), 10 deletions(-) delete mode 100644 tests/components/opentherm/test.esp32-ard.yaml delete mode 100644 tests/components/opentherm/test.esp32-c3-ard.yaml diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index b2751470b2..d59b9584d1 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -7,7 +7,7 @@ #include "opentherm.h" #include "esphome/core/helpers.h" -#if defined(ESP32) || defined(USE_ESP_IDF) +#ifdef USE_ESP32 #include "driver/timer.h" #include "esp_err.h" #endif @@ -31,7 +31,7 @@ OpenTherm *OpenTherm::instance = nullptr; OpenTherm::OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout) : in_pin_(in_pin), out_pin_(out_pin), -#if defined(ESP32) || defined(USE_ESP_IDF) +#ifdef USE_ESP32 timer_group_(TIMER_GROUP_0), timer_idx_(TIMER_0), #endif @@ -57,7 +57,7 @@ bool OpenTherm::initialize() { this->out_pin_->setup(); this->out_pin_->digital_write(true); -#if defined(ESP32) || defined(USE_ESP_IDF) +#ifdef USE_ESP32 return this->init_esp32_timer_(); #else return true; @@ -238,7 +238,7 @@ void IRAM_ATTR OpenTherm::write_bit_(uint8_t high, uint8_t clock) { } } -#if defined(ESP32) || defined(USE_ESP_IDF) +#ifdef USE_ESP32 bool OpenTherm::init_esp32_timer_() { // Search for a free timer. Maybe unstable, we'll see. @@ -365,7 +365,7 @@ void IRAM_ATTR OpenTherm::stop_timer_() { } } -#endif // END ESP32 +#endif // USE_ESP32 #ifdef ESP8266 // 5 kHz timer_ diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index a5822cdfe1..3996481760 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -12,7 +12,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#if defined(ESP32) || defined(USE_ESP_IDF) +#ifdef USE_ESP32 #include "driver/timer.h" #endif @@ -356,7 +356,7 @@ class OpenTherm { ISRInternalGPIOPin isr_in_pin_; ISRInternalGPIOPin isr_out_pin_; -#if defined(ESP32) || defined(USE_ESP_IDF) +#ifdef USE_ESP32 timer_group_t timer_group_; timer_idx_t timer_idx_; #endif @@ -370,7 +370,7 @@ class OpenTherm { int32_t timeout_counter_; // <0 no timeout int32_t device_timeout_; -#if defined(ESP32) || defined(USE_ESP_IDF) +#ifdef USE_ESP32 esp_err_t timer_error_ = ESP_OK; TimerErrorType timer_error_type_ = TimerErrorType::NO_TIMER_ERROR; diff --git a/tests/components/opentherm/test.esp32-ard.yaml b/tests/components/opentherm/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/opentherm/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/opentherm/test.esp32-c3-ard.yaml b/tests/components/opentherm/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/opentherm/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml From 590cae13c0905294251a847213c6d5534b91941e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 13:41:50 -1000 Subject: [PATCH 022/526] [ci][tests] Remove redundant ESP32-C3 Arduino tests for non-variant-specific components (#11152) --- tests/components/http_request/test.esp32-c3-ard.yaml | 4 ---- tests/components/nextion/test.esp32-c3-ard.yaml | 10 ---------- tests/components/ota/test.esp32-c3-ard.yaml | 1 - tests/components/web_server/test.esp32-c3-ard.yaml | 1 - 4 files changed, 16 deletions(-) delete mode 100644 tests/components/http_request/test.esp32-c3-ard.yaml delete mode 100644 tests/components/nextion/test.esp32-c3-ard.yaml delete mode 100644 tests/components/ota/test.esp32-c3-ard.yaml delete mode 100644 tests/components/web_server/test.esp32-c3-ard.yaml diff --git a/tests/components/http_request/test.esp32-c3-ard.yaml b/tests/components/http_request/test.esp32-c3-ard.yaml deleted file mode 100644 index c1937b5a10..0000000000 --- a/tests/components/http_request/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - verify_ssl: "false" - -<<: !include common.yaml diff --git a/tests/components/nextion/test.esp32-c3-ard.yaml b/tests/components/nextion/test.esp32-c3-ard.yaml deleted file mode 100644 index 5135c7e4f4..0000000000 --- a/tests/components/nextion/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,10 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -packages: - base: !include common.yaml - -display: - - id: !extend main_lcd - tft_url: http://esphome.io/default35.tft diff --git a/tests/components/ota/test.esp32-c3-ard.yaml b/tests/components/ota/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ota/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/web_server/test.esp32-c3-ard.yaml b/tests/components/web_server/test.esp32-c3-ard.yaml deleted file mode 100644 index 7e6658e20e..0000000000 --- a/tests/components/web_server/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common_v2.yaml From 52219c4dccb03a51d97172b73c92ff3255baf9aa Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 13:45:59 -1000 Subject: [PATCH 023/526] [datetime][ci][tests] Replace test.all.yaml with minimal platform cover (#11151) --- .../components/datetime/{test.all.yaml => test.bk72xx-ard.yaml} | 0 tests/components/datetime/test.esp32-idf.yaml | 1 + tests/components/datetime/test.esp8266-ard.yaml | 1 + tests/components/datetime/test.ln882x-ard.yaml | 1 + tests/components/datetime/test.rp2040-ard.yaml | 1 + 5 files changed, 4 insertions(+) rename tests/components/datetime/{test.all.yaml => test.bk72xx-ard.yaml} (100%) create mode 100644 tests/components/datetime/test.esp32-idf.yaml create mode 100644 tests/components/datetime/test.esp8266-ard.yaml create mode 100644 tests/components/datetime/test.ln882x-ard.yaml create mode 100644 tests/components/datetime/test.rp2040-ard.yaml diff --git a/tests/components/datetime/test.all.yaml b/tests/components/datetime/test.bk72xx-ard.yaml similarity index 100% rename from tests/components/datetime/test.all.yaml rename to tests/components/datetime/test.bk72xx-ard.yaml diff --git a/tests/components/datetime/test.esp32-idf.yaml b/tests/components/datetime/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/datetime/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/datetime/test.esp8266-ard.yaml b/tests/components/datetime/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/datetime/test.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/datetime/test.ln882x-ard.yaml b/tests/components/datetime/test.ln882x-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/datetime/test.ln882x-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/datetime/test.rp2040-ard.yaml b/tests/components/datetime/test.rp2040-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/datetime/test.rp2040-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From be51093a7e1fae9376aab7c5ce398ba419b07d73 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 9 Oct 2025 17:02:18 -1000 Subject: [PATCH 024/526] [ci][tests] Remove all redundant ESP32-C3 Arduino tests (#11154) --- .../ac_dimmer/test.esp32-c3-ard.yaml | 5 ----- .../captive_portal/test.esp32-c3-ard.yaml | 1 - tests/components/dsmr/test.esp32-c3-ard.yaml | 6 ------ .../esp32_ble/test.esp32-c3-ard.yaml | 1 - .../heatpumpir/test.esp32-c3-ard.yaml | 4 ---- tests/components/i2c/test.esp32-c3-ard.yaml | 5 ----- tests/components/midea/test.esp32-c3-ard.yaml | 6 ------ .../neopixelbus/test.esp32-c3-ard.yaml | 19 ------------------- .../speaker/audio_dac.esp32-c3-ard.yaml | 9 --------- .../components/speaker/test.esp32-c3-ard.yaml | 9 --------- tests/components/spi/test.esp32-c3-ard.yaml | 6 ------ tests/components/wled/test.esp32-c3-ard.yaml | 16 ---------------- 12 files changed, 87 deletions(-) delete mode 100644 tests/components/ac_dimmer/test.esp32-c3-ard.yaml delete mode 100644 tests/components/captive_portal/test.esp32-c3-ard.yaml delete mode 100644 tests/components/dsmr/test.esp32-c3-ard.yaml delete mode 100644 tests/components/esp32_ble/test.esp32-c3-ard.yaml delete mode 100644 tests/components/heatpumpir/test.esp32-c3-ard.yaml delete mode 100644 tests/components/i2c/test.esp32-c3-ard.yaml delete mode 100644 tests/components/midea/test.esp32-c3-ard.yaml delete mode 100644 tests/components/neopixelbus/test.esp32-c3-ard.yaml delete mode 100644 tests/components/speaker/audio_dac.esp32-c3-ard.yaml delete mode 100644 tests/components/speaker/test.esp32-c3-ard.yaml delete mode 100644 tests/components/spi/test.esp32-c3-ard.yaml delete mode 100644 tests/components/wled/test.esp32-c3-ard.yaml diff --git a/tests/components/ac_dimmer/test.esp32-c3-ard.yaml b/tests/components/ac_dimmer/test.esp32-c3-ard.yaml deleted file mode 100644 index 5d2d42b713..0000000000 --- a/tests/components/ac_dimmer/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - gate_pin: GPIO5 - zero_cross_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/captive_portal/test.esp32-c3-ard.yaml b/tests/components/captive_portal/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/captive_portal/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/dsmr/test.esp32-c3-ard.yaml b/tests/components/dsmr/test.esp32-c3-ard.yaml deleted file mode 100644 index 72998506ac..0000000000 --- a/tests/components/dsmr/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - request_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/esp32_ble/test.esp32-c3-ard.yaml b/tests/components/esp32_ble/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esp32_ble/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/heatpumpir/test.esp32-c3-ard.yaml b/tests/components/heatpumpir/test.esp32-c3-ard.yaml deleted file mode 100644 index 7b012aa64c..0000000000 --- a/tests/components/heatpumpir/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/i2c/test.esp32-c3-ard.yaml b/tests/components/i2c/test.esp32-c3-ard.yaml deleted file mode 100644 index ee2c29ca4e..0000000000 --- a/tests/components/i2c/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/midea/test.esp32-c3-ard.yaml b/tests/components/midea/test.esp32-c3-ard.yaml deleted file mode 100644 index a879df3ca4..0000000000 --- a/tests/components/midea/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/neopixelbus/test.esp32-c3-ard.yaml b/tests/components/neopixelbus/test.esp32-c3-ard.yaml deleted file mode 100644 index f2b53ab1e9..0000000000 --- a/tests/components/neopixelbus/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,19 +0,0 @@ -light: - - platform: neopixelbus - id: addr3 - name: Neopixelbus Light - gamma_correct: 2.8 - color_correct: [0.0, 0.0, 0.0, 0.0] - default_transition_length: 10s - effects: - - addressable_flicker: - name: Flicker Effect With Custom Values - update_interval: 16ms - intensity: 5% - type: GRBW - variant: SK6812 - method: - type: esp32_rmt - channel: 0 - num_leds: 5 - pin: 4 diff --git a/tests/components/speaker/audio_dac.esp32-c3-ard.yaml b/tests/components/speaker/audio_dac.esp32-c3-ard.yaml deleted file mode 100644 index 1004d2143e..0000000000 --- a/tests/components/speaker/audio_dac.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - i2s_bclk_pin: GPIO7 - i2s_lrclk_pin: GPIO6 - i2s_mclk_pin: GPIO9 - i2s_dout_pin: GPIO8 - -<<: !include common-audio_dac.yaml diff --git a/tests/components/speaker/test.esp32-c3-ard.yaml b/tests/components/speaker/test.esp32-c3-ard.yaml deleted file mode 100644 index ddcf051fab..0000000000 --- a/tests/components/speaker/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - i2s_bclk_pin: GPIO7 - i2s_lrclk_pin: GPIO6 - i2s_mclk_pin: GPIO9 - i2s_dout_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/spi/test.esp32-c3-ard.yaml b/tests/components/spi/test.esp32-c3-ard.yaml deleted file mode 100644 index bfa12b1755..0000000000 --- a/tests/components/spi/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/wled/test.esp32-c3-ard.yaml b/tests/components/wled/test.esp32-c3-ard.yaml deleted file mode 100644 index 156b31181e..0000000000 --- a/tests/components/wled/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,16 +0,0 @@ -wifi: - ssid: MySSID - password: password1 - -wled: - -light: - - platform: esp32_rmt_led_strip - id: led_matrix_32x8 - default_transition_length: 500ms - chipset: ws2812 - rgb_order: GRB - num_leds: 256 - pin: 2 - effects: - - wled: From 6abc2efd961dda4af0332eabd5335453d7cd71d1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 10 Oct 2025 11:18:57 -1000 Subject: [PATCH 025/526] [json] Fix PSRAM allocator dangling pointer crash (#11165) --- esphome/components/json/json_util.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index dbdf6e3486..869d29f92e 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -8,6 +8,13 @@ namespace json { static const char *const TAG = "json"; +#ifdef USE_PSRAM +// Global allocator that outlives all JsonDocuments returned by parse_json() +// This prevents dangling pointer issues when JsonDocuments are returned from functions +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) - Must be mutable for ArduinoJson::Allocator +static SpiRamAllocator global_json_allocator; +#endif + std::string build_json(const json_build_t &f) { // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonBuilder builder; @@ -33,8 +40,7 @@ JsonDocument parse_json(const uint8_t *data, size_t len) { return JsonObject(); // return unbound object } #ifdef USE_PSRAM - auto doc_allocator = SpiRamAllocator(); - JsonDocument json_document(&doc_allocator); + JsonDocument json_document(&global_json_allocator); #else JsonDocument json_document; #endif From b54beb357a76eb655f919bf2b2f2cc798335012b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:46:27 -1000 Subject: [PATCH 026/526] Bump github/codeql-action from 4.30.7 to 4.30.8 (#11163) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 59f58b7236..9aa7856116 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 + uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -86,6 +86,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 + uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 with: category: "/language:${{matrix.language}}" From cb602c9b1a33f04877d40e976f1d7310901cc3a4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 11 Oct 2025 05:47:23 -1000 Subject: [PATCH 027/526] [esp32_ble] Partial revert of #10862 - Fix GATT client notifications (#11171) --- esphome/components/esp32_ble/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 15afb22ab8..05ef936baf 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -332,12 +332,16 @@ def final_validation(config): # 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 ) + + # ESP-IDF BLE stack requires GATT Server to be enabled when GATT Client is enabled + # This is an internal dependency in the Bluedroid stack (tested ESP-IDF 5.4.2-5.5.1) + # See: https://github.com/espressif/esp-idf/issues/17724 + add_idf_sdkconfig_option("CONFIG_BT_GATTS_ENABLE", has_ble_server or has_ble_client) add_idf_sdkconfig_option("CONFIG_BT_GATTC_ENABLE", has_ble_client) # Handle max_connections: check for deprecated location in esp32_ble_tracker From 9bd9b043c83407cf3dcd12b4eda7bdebf6c4da12 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 11 Oct 2025 05:47:42 -1000 Subject: [PATCH 028/526] [esp32_ble_tracker] Replace std::vector with StaticVector for listeners and clients (#11173) --- .../components/esp32_ble_tracker/__init__.py | 30 +++++++++++++++++++ .../esp32_ble_tracker/esp32_ble_tracker.cpp | 30 ++++++++++++++++++- .../esp32_ble_tracker/esp32_ble_tracker.h | 10 +++++-- esphome/core/defines.h | 2 ++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 247496ccd9..8c7f3e3930 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations +from dataclasses import dataclass import logging from esphome import automation @@ -52,9 +53,19 @@ class BLEFeatures(StrEnum): ESP_BT_DEVICE = "ESP_BT_DEVICE" +# Dataclass for registration counts +@dataclass +class RegistrationCounts: + listeners: int = 0 + clients: int = 0 + + # Set to track which features are needed by components _required_features: set[BLEFeatures] = set() +# Track registration counts for StaticVector sizing +_registration_counts = RegistrationCounts() + def register_ble_features(features: set[BLEFeatures]) -> None: """Register BLE features that a component needs. @@ -257,12 +268,14 @@ async def to_code(config): register_ble_features({BLEFeatures.ESP_BT_DEVICE}) for conf in config.get(CONF_ON_BLE_ADVERTISE, []): + _registration_counts.listeners += 1 trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) if CONF_MAC_ADDRESS in conf: addr_list = [it.as_hex for it in conf[CONF_MAC_ADDRESS]] cg.add(trigger.set_addresses(addr_list)) await automation.build_automation(trigger, [(ESPBTDeviceConstRef, "x")], conf) for conf in config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, []): + _registration_counts.listeners += 1 trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) if len(conf[CONF_SERVICE_UUID]) == len(bt_uuid16_format): cg.add(trigger.set_service_uuid16(as_hex(conf[CONF_SERVICE_UUID]))) @@ -275,6 +288,7 @@ async def to_code(config): cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) for conf in config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, []): + _registration_counts.listeners += 1 trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) if len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid16_format): cg.add(trigger.set_manufacturer_uuid16(as_hex(conf[CONF_MANUFACTURER_ID]))) @@ -287,6 +301,7 @@ async def to_code(config): cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) for conf in config.get(CONF_ON_SCAN_END, []): + _registration_counts.listeners += 1 trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) @@ -320,6 +335,17 @@ async def _add_ble_features(): cg.add_define("USE_ESP32_BLE_DEVICE") cg.add_define("USE_ESP32_BLE_UUID") + # Add defines for StaticVector sizing based on registration counts + # Only define if count > 0 to avoid allocating unnecessary memory + if _registration_counts.listeners > 0: + cg.add_define( + "ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT", _registration_counts.listeners + ) + if _registration_counts.clients > 0: + cg.add_define( + "ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT", _registration_counts.clients + ) + ESP32_BLE_START_SCAN_ACTION_SCHEMA = cv.Schema( { @@ -369,6 +395,7 @@ async def register_ble_device( var: cg.SafeExpType, config: ConfigType ) -> cg.SafeExpType: register_ble_features({BLEFeatures.ESP_BT_DEVICE}) + _registration_counts.listeners += 1 paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) cg.add(paren.register_listener(var)) return var @@ -376,6 +403,7 @@ async def register_ble_device( async def register_client(var: cg.SafeExpType, config: ConfigType) -> cg.SafeExpType: register_ble_features({BLEFeatures.ESP_BT_DEVICE}) + _registration_counts.clients += 1 paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) cg.add(paren.register_client(var)) return var @@ -389,6 +417,7 @@ async def register_raw_ble_device( This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice will not be compiled in if this is the only registration method used. """ + _registration_counts.listeners += 1 paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) cg.add(paren.register_listener(var)) return var @@ -402,6 +431,7 @@ async def register_raw_client( This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice will not be compiled in if this is the only registration method used. """ + _registration_counts.clients += 1 paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) cg.add(paren.register_client(var)) return var diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 83f59d492e..d07e67825b 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -74,9 +74,11 @@ void ESP32BLETracker::setup() { [this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) { if (state == ota::OTA_STARTED) { this->stop_scan(); +#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT for (auto *client : this->clients_) { client->disconnect(); } +#endif } }); #endif @@ -206,8 +208,10 @@ void ESP32BLETracker::start_scan_(bool first) { this->set_scanner_state_(ScannerState::STARTING); ESP_LOGD(TAG, "Starting scan, set scanner state to STARTING."); if (!first) { +#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT for (auto *listener : this->listeners_) listener->on_scan_end(); +#endif } #ifdef USE_ESP32_BLE_DEVICE this->already_discovered_.clear(); @@ -236,20 +240,25 @@ void ESP32BLETracker::start_scan_(bool first) { } void ESP32BLETracker::register_client(ESPBTClient *client) { +#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT client->app_id = ++this->app_id_; this->clients_.push_back(client); this->recalculate_advertisement_parser_types(); +#endif } void ESP32BLETracker::register_listener(ESPBTDeviceListener *listener) { +#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT listener->set_parent(this); this->listeners_.push_back(listener); this->recalculate_advertisement_parser_types(); +#endif } void ESP32BLETracker::recalculate_advertisement_parser_types() { this->raw_advertisements_ = false; this->parse_advertisements_ = false; +#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT for (auto *listener : this->listeners_) { if (listener->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) { this->parse_advertisements_ = true; @@ -257,6 +266,8 @@ void ESP32BLETracker::recalculate_advertisement_parser_types() { this->raw_advertisements_ = true; } } +#endif +#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT for (auto *client : this->clients_) { if (client->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) { this->parse_advertisements_ = true; @@ -264,6 +275,7 @@ void ESP32BLETracker::recalculate_advertisement_parser_types() { this->raw_advertisements_ = true; } } +#endif } void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { @@ -282,10 +294,12 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga default: break; } - // Forward all events to clients (scan results are handled separately via gap_scan_event_handler) + // Forward all events to clients (scan results are handled separately via gap_scan_event_handler) +#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT for (auto *client : this->clients_) { client->gap_event_handler(event, param); } +#endif } void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) { @@ -348,9 +362,11 @@ void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_ void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { +#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT for (auto *client : this->clients_) { client->gattc_event_handler(event, gattc_if, param); } +#endif } void ESP32BLETracker::set_scanner_state_(ScannerState state) { @@ -704,12 +720,16 @@ bool ESPBTDevice::resolve_irk(const uint8_t *irk) const { void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) { // Process raw advertisements if (this->raw_advertisements_) { +#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT for (auto *listener : this->listeners_) { listener->parse_devices(&scan_result, 1); } +#endif +#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT for (auto *client : this->clients_) { client->parse_devices(&scan_result, 1); } +#endif } // Process parsed advertisements @@ -719,16 +739,20 @@ void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) { device.parse_scan_rst(scan_result); bool found = false; +#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT for (auto *listener : this->listeners_) { if (listener->parse_device(device)) found = true; } +#endif +#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT for (auto *client : this->clients_) { if (client->parse_device(device)) { found = true; } } +#endif if (!found && !this->scan_continuous_) { this->print_bt_device_info(device); @@ -745,8 +769,10 @@ void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) { // Reset timeout state machine instead of cancelling scheduler timeout this->scan_timeout_state_ = ScanTimeoutState::INACTIVE; +#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT for (auto *listener : this->listeners_) listener->on_scan_end(); +#endif this->set_scanner_state_(ScannerState::IDLE); } @@ -770,6 +796,7 @@ void ESP32BLETracker::handle_scanner_failure_() { void ESP32BLETracker::try_promote_discovered_clients_() { // Only promote the first discovered client to avoid multiple simultaneous connections +#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT for (auto *client : this->clients_) { if (client->state() != ClientState::DISCOVERED) { continue; @@ -791,6 +818,7 @@ void ESP32BLETracker::try_promote_discovered_clients_() { client->connect(); break; } +#endif } const char *ESP32BLETracker::scanner_state_to_string_(ScannerState state) const { diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index e53c2ac097..f80f3e2670 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -302,6 +302,7 @@ class ESP32BLETracker : public Component, /// Count clients in each state ClientStateCounts count_client_states_() const { ClientStateCounts counts; +#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT for (auto *client : this->clients_) { switch (client->state()) { case ClientState::DISCONNECTING: @@ -317,12 +318,17 @@ class ESP32BLETracker : public Component, break; } } +#endif return counts; } // Group 1: Large objects (12+ bytes) - vectors and callback manager - std::vector listeners_; - std::vector clients_; +#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT + StaticVector listeners_; +#endif +#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT + StaticVector clients_; +#endif CallbackManager scanner_state_callbacks_; #ifdef USE_ESP32_BLE_DEVICE /// Vector of addresses that have already been printed in print_bt_device_info diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 0f1d1bcf28..955d0f987c 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -175,6 +175,8 @@ #define USE_ESP32_BLE_SERVER_DESCRIPTOR_ON_WRITE #define USE_ESP32_BLE_SERVER_ON_CONNECT #define USE_ESP32_BLE_SERVER_ON_DISCONNECT +#define ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT 1 +#define ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT 1 #define USE_ESP32_CAMERA_JPEG_ENCODER #define USE_I2C #define USE_IMPROV From 6a11700a6b0d7c00d4fe0ab48c30ff93738b99e4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 11 Oct 2025 08:21:37 -1000 Subject: [PATCH 029/526] [mdns] Restore mdns_txt_record() public API for external components (#11158) --- esphome/components/mdns/__init__.py | 85 +++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 6e148092fe..14e0420ef5 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -11,7 +11,7 @@ from esphome.const import ( CONF_SERVICES, PlatformFramework, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, Lambda, coroutine_with_priority from esphome.coroutine import CoroPriority CODEOWNERS = ["@esphome/core"] @@ -58,9 +58,64 @@ CONFIG_SCHEMA = cv.All( ) +def mdns_txt_record(key: str, value: str) -> cg.RawExpression: + """Create a mDNS TXT record. + + Public API for external components. Do not remove. + + Args: + key: The TXT record key + value: The TXT record value (static string only) + + Returns: + A RawExpression representing a MDNSTXTRecord struct + """ + return cg.RawExpression( + f"{{MDNS_STR({cg.safe_exp(key)}), MDNS_STR({cg.safe_exp(value)})}}" + ) + + +async def _mdns_txt_record_templated( + mdns_comp: cg.Pvariable, key: str, value: Lambda | str +) -> cg.RawExpression: + """Create a mDNS TXT record with support for templated values. + + Internal helper function. + + Args: + mdns_comp: The MDNSComponent instance (from cg.get_variable()) + key: The TXT record key + value: The TXT record value (can be a static string or a lambda template) + + Returns: + A RawExpression representing a MDNSTXTRecord struct + """ + if not cg.is_template(value): + # It's a static string - use directly in flash, no need to store in vector + return mdns_txt_record(key, value) + # It's a lambda - evaluate and store using helper + templated_value = await cg.templatable(value, [], cg.std_string) + safe_key = cg.safe_exp(key) + dynamic_call = f"{mdns_comp}->add_dynamic_txt_value(({templated_value})())" + return cg.RawExpression(f"{{MDNS_STR({safe_key}), MDNS_STR({dynamic_call})}}") + + def mdns_service( - service: str, proto: str, port: int, txt_records: list[dict[str, str]] -): + service: str, proto: str, port: int, txt_records: list[cg.RawExpression] +) -> cg.StructInitializer: + """Create a mDNS service. + + Public API for external components. Do not remove. + + Args: + service: Service name (e.g., "_http") + proto: Protocol (e.g., "_tcp" or "_udp") + port: Port number + txt_records: List of MDNSTXTRecord expressions + + Returns: + A StructInitializer representing a MDNSService struct + """ return cg.StructInitializer( MDNSService, ("service_type", cg.RawExpression(f"MDNS_STR({cg.safe_exp(service)})")), @@ -120,26 +175,10 @@ async def to_code(config): await cg.register_component(var, config) for service in config[CONF_SERVICES]: - # Build the txt records list for the service - txt_records = [] - for txt_key, txt_value in service[CONF_TXT].items(): - if cg.is_template(txt_value): - # It's a lambda - evaluate and store using helper - templated_value = await cg.templatable(txt_value, [], cg.std_string) - safe_key = cg.safe_exp(txt_key) - dynamic_call = f"{var}->add_dynamic_txt_value(({templated_value})())" - txt_records.append( - cg.RawExpression( - f"{{MDNS_STR({safe_key}), MDNS_STR({dynamic_call})}}" - ) - ) - else: - # It's a static string - use directly in flash, no need to store in vector - txt_records.append( - cg.RawExpression( - f"{{MDNS_STR({cg.safe_exp(txt_key)}), MDNS_STR({cg.safe_exp(txt_value)})}}" - ) - ) + txt_records = [ + await _mdns_txt_record_templated(var, txt_key, txt_value) + for txt_key, txt_value in service[CONF_TXT].items() + ] exp = mdns_service( service[CONF_SERVICE], From dcf2697a2a1413ca159568aec1c2b213f00cd313 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 11 Oct 2025 08:21:45 -1000 Subject: [PATCH 030/526] Group component tests to reduce CI time (#11134) --- .ai/instructions.md | 5 + .github/workflows/ci.yml | 96 +- esphome/__main__.py | 7 + esphome/components/esp32_ble/__init__.py | 4 + esphome/components/uart/__init__.py | 2 +- esphome/core/__init__.py | 2 + esphome/core/entity_helpers.py | 15 +- esphome/pins.py | 4 +- esphome/platformio_api.py | 31 + script/analyze_component_buses.py | 523 ++++++++++ script/merge_component_configs.py | 379 +++++++ script/split_components_for_ci.py | 268 +++++ script/test_build_components | 107 +- script/test_build_components.py | 931 ++++++++++++++++++ script/test_component_grouping.py | 227 +++++ tests/components/a01nyub/common.yaml | 7 - .../components/a01nyub/test.esp32-c3-idf.yaml | 3 + tests/components/a01nyub/test.esp32-idf.yaml | 7 +- .../components/a01nyub/test.esp8266-ard.yaml | 5 +- tests/components/a01nyub/test.rp2040-ard.yaml | 5 +- tests/components/a02yyuw/common.yaml | 7 - .../components/a02yyuw/test.esp32-c3-idf.yaml | 3 + tests/components/a02yyuw/test.esp32-idf.yaml | 7 +- .../components/a02yyuw/test.esp8266-ard.yaml | 5 +- tests/components/a02yyuw/test.rp2040-ard.yaml | 5 +- tests/components/a4988/test.esp32-idf.yaml | 2 +- tests/components/a4988/test.esp8266-ard.yaml | 2 +- .../components/ac_dimmer/test.esp32-ard.yaml | 4 +- .../ac_dimmer/test.esp8266-ard.yaml | 4 +- tests/components/adc/common.yaml | 11 - tests/components/adc/test.bk72xx-ard.yaml | 14 +- tests/components/adc/test.esp32-c3-idf.yaml | 16 +- tests/components/adc/test.esp32-idf.yaml | 14 +- tests/components/adc/test.esp32-p4-idf.yaml | 16 +- tests/components/adc/test.esp32-s2-idf.yaml | 16 +- tests/components/adc/test.esp32-s3-idf.yaml | 16 +- tests/components/adc/test.esp8266-ard.yaml | 14 +- tests/components/adc/test.ln882x-ard.yaml | 16 +- tests/components/adc/test.rp2040-ard.yaml | 14 +- tests/components/adc128s102/common.yaml | 6 - .../adc128s102/test.esp32-c3-idf.yaml | 6 +- .../components/adc128s102/test.esp32-idf.yaml | 6 +- .../adc128s102/test.esp8266-ard.yaml | 9 +- .../adc128s102/test.rp2040-ard.yaml | 3 + tests/components/ade7880/common.yaml | 7 +- .../components/ade7880/test.esp32-c3-idf.yaml | 7 +- tests/components/ade7880/test.esp32-idf.yaml | 7 +- .../components/ade7880/test.esp8266-ard.yaml | 5 +- tests/components/ade7880/test.rp2040-ard.yaml | 5 +- tests/components/ade7953_i2c/common.yaml | 9 +- .../ade7953_i2c/test.esp32-c3-idf.yaml | 5 +- .../ade7953_i2c/test.esp32-idf.yaml | 5 +- .../ade7953_i2c/test.esp8266-ard.yaml | 5 +- .../ade7953_i2c/test.rp2040-ard.yaml | 5 +- tests/components/ade7953_spi/common.yaml | 6 - .../ade7953_spi/test.esp32-c3-idf.yaml | 5 +- .../ade7953_spi/test.esp32-idf.yaml | 6 +- .../ade7953_spi/test.esp8266-ard.yaml | 9 +- .../ade7953_spi/test.rp2040-ard.yaml | 3 + tests/components/ads1115/common.yaml | 6 +- .../components/ads1115/test.esp32-c3-idf.yaml | 5 +- tests/components/ads1115/test.esp32-idf.yaml | 5 +- .../components/ads1115/test.esp8266-ard.yaml | 5 +- tests/components/ads1115/test.rp2040-ard.yaml | 5 +- tests/components/ags10/common.yaml | 6 - tests/components/ags10/test.esp32-c3-idf.yaml | 5 +- tests/components/ags10/test.esp32-idf.yaml | 5 +- tests/components/ags10/test.esp8266-ard.yaml | 5 +- tests/components/aht10/common.yaml | 6 +- tests/components/aht10/test.esp32-c3-idf.yaml | 5 +- tests/components/aht10/test.esp32-idf.yaml | 5 +- tests/components/aht10/test.esp8266-ard.yaml | 5 +- tests/components/aht10/test.rp2040-ard.yaml | 5 +- tests/components/aic3204/common.yaml | 6 +- .../components/aic3204/test.esp32-c3-idf.yaml | 5 +- tests/components/aic3204/test.esp32-idf.yaml | 5 +- .../components/aic3204/test.esp8266-ard.yaml | 5 +- .../test.esp32-c3-idf.yaml | 3 + .../airthings_wave_mini/test.esp32-idf.yaml | 3 + .../test.esp32-c3-idf.yaml | 3 + .../airthings_wave_plus/test.esp32-idf.yaml | 3 + .../components/alpha3/test.esp32-c3-idf.yaml | 3 + tests/components/alpha3/test.esp32-idf.yaml | 3 + tests/components/am2315c/common.yaml | 6 +- .../components/am2315c/test.esp32-c3-idf.yaml | 5 +- tests/components/am2315c/test.esp32-idf.yaml | 5 +- .../components/am2315c/test.esp8266-ard.yaml | 5 +- tests/components/am2315c/test.rp2040-ard.yaml | 5 +- tests/components/am2320/common.yaml | 6 +- .../components/am2320/test.esp32-c3-idf.yaml | 5 +- tests/components/am2320/test.esp32-idf.yaml | 5 +- tests/components/am2320/test.esp8266-ard.yaml | 5 +- tests/components/am2320/test.rp2040-ard.yaml | 5 +- tests/components/am43/test.esp32-c3-idf.yaml | 3 + tests/components/am43/test.esp32-idf.yaml | 3 + .../animation/test.esp32-c3-idf.yaml | 12 +- .../components/animation/test.esp32-idf.yaml | 12 +- .../animation/test.esp8266-ard.yaml | 12 +- .../components/animation/test.rp2040-ard.yaml | 12 +- tests/components/anova/test.esp32-c3-idf.yaml | 3 + tests/components/anova/test.esp32-idf.yaml | 3 + tests/components/apds9306/common.yaml | 6 +- .../apds9306/test.esp32-c3-idf.yaml | 5 +- tests/components/apds9306/test.esp32-idf.yaml | 5 +- .../components/apds9306/test.esp8266-ard.yaml | 5 +- .../components/apds9306/test.rp2040-ard.yaml | 5 +- tests/components/apds9960/common.yaml | 6 +- .../apds9960/test.esp32-c3-idf.yaml | 5 +- tests/components/apds9960/test.esp32-idf.yaml | 5 +- .../components/apds9960/test.esp8266-ard.yaml | 5 +- .../components/apds9960/test.rp2040-ard.yaml | 5 +- tests/components/api/common-base.yaml | 89 ++ tests/components/api/common.yaml | 88 +- .../test-dynamic-encryption.esp32-idf.yaml | 7 +- tests/components/as3935_i2c/common.yaml | 6 +- .../as3935_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/as3935_i2c/test.esp32-idf.yaml | 5 +- .../as3935_i2c/test.esp8266-ard.yaml | 5 +- .../as3935_i2c/test.rp2040-ard.yaml | 5 +- tests/components/as3935_spi/common.yaml | 6 - .../as3935_spi/test.esp32-c3-idf.yaml | 5 +- .../components/as3935_spi/test.esp32-idf.yaml | 6 +- .../as3935_spi/test.esp8266-ard.yaml | 9 +- .../as3935_spi/test.rp2040-ard.yaml | 3 + tests/components/as5600/common.yaml | 6 +- .../components/as5600/test.esp32-c3-idf.yaml | 5 +- tests/components/as5600/test.esp32-idf.yaml | 5 +- tests/components/as5600/test.esp8266-ard.yaml | 5 +- tests/components/as5600/test.rp2040-ard.yaml | 5 +- tests/components/as7341/common.yaml | 6 +- .../components/as7341/test.esp32-c3-idf.yaml | 5 +- tests/components/as7341/test.esp32-idf.yaml | 5 +- tests/components/as7341/test.esp8266-ard.yaml | 5 +- tests/components/as7341/test.rp2040-ard.yaml | 5 +- tests/components/at581x/common.yaml | 6 +- .../components/at581x/test.esp32-c3-idf.yaml | 5 +- tests/components/at581x/test.esp32-idf.yaml | 5 +- tests/components/at581x/test.esp8266-ard.yaml | 5 +- tests/components/at581x/test.rp2040-ard.yaml | 5 +- .../atc_mithermometer/test.esp32-c3-idf.yaml | 3 + .../atc_mithermometer/test.esp32-idf.yaml | 3 + tests/components/atm90e26/common.yaml | 6 - .../atm90e26/test.esp32-c3-idf.yaml | 5 +- tests/components/atm90e26/test.esp32-idf.yaml | 6 +- .../components/atm90e26/test.esp8266-ard.yaml | 9 +- .../components/atm90e26/test.rp2040-ard.yaml | 3 + tests/components/atm90e32/common.yaml | 6 - .../atm90e32/test.esp32-c3-idf.yaml | 5 +- tests/components/atm90e32/test.esp32-idf.yaml | 6 +- .../components/atm90e32/test.esp8266-ard.yaml | 9 +- .../components/atm90e32/test.rp2040-ard.yaml | 3 + tests/components/axs15231/common.yaml | 10 +- .../axs15231/test.esp32-c3-idf.yaml | 3 + tests/components/axs15231/test.esp32-idf.yaml | 3 + .../components/axs15231/test.esp8266-ard.yaml | 8 +- .../components/axs15231/test.rp2040-ard.yaml | 3 + .../b_parasite/test.esp32-c3-idf.yaml | 3 + .../components/b_parasite/test.esp32-idf.yaml | 3 + .../components/bedjet/test.esp32-c3-idf.yaml | 3 + tests/components/bedjet/test.esp32-idf.yaml | 3 + tests/components/bh1750/common.yaml | 6 +- .../components/bh1750/test.esp32-c3-idf.yaml | 5 +- tests/components/bh1750/test.esp32-idf.yaml | 5 +- tests/components/bh1750/test.esp8266-ard.yaml | 5 +- tests/components/bh1750/test.rp2040-ard.yaml | 5 +- tests/components/bl0906/common.yaml | 6 - .../components/bl0906/test.esp32-c3-idf.yaml | 5 +- tests/components/bl0906/test.esp32-idf.yaml | 5 +- tests/components/bl0906/test.esp8266-ard.yaml | 3 + tests/components/bl0906/test.rp2040-ard.yaml | 3 + tests/components/bl0939/common.yaml | 6 - .../components/bl0939/test.esp32-c3-idf.yaml | 4 +- tests/components/bl0939/test.esp32-idf.yaml | 3 + tests/components/bl0939/test.esp8266-ard.yaml | 3 + tests/components/bl0939/test.rp2040-ard.yaml | 3 + tests/components/bl0940/common.yaml | 6 - .../components/bl0940/test.esp32-c3-idf.yaml | 4 +- tests/components/bl0940/test.esp32-idf.yaml | 3 + tests/components/bl0940/test.esp8266-ard.yaml | 3 + tests/components/bl0940/test.rp2040-ard.yaml | 3 + tests/components/bl0942/common.yaml | 6 - tests/components/bl0942/test.bk72xx-ard.yaml | 5 +- .../components/bl0942/test.esp32-c3-idf.yaml | 4 +- tests/components/bl0942/test.esp32-idf.yaml | 3 + tests/components/bl0942/test.esp8266-ard.yaml | 3 + tests/components/bl0942/test.rp2040-ard.yaml | 3 + .../ble_client/test.esp32-c3-idf.yaml | 3 + .../components/ble_client/test.esp32-idf.yaml | 3 + .../ble_presence/test.esp32-c3-idf.yaml | 3 + .../ble_presence/test.esp32-idf.yaml | 3 + .../ble_rssi/test.esp32-c3-idf.yaml | 3 + tests/components/ble_rssi/test.esp32-idf.yaml | 3 + .../ble_scanner/test.esp32-c3-idf.yaml | 3 + .../ble_scanner/test.esp32-idf.yaml | 3 + tests/components/bme280_i2c/common.yaml | 7 +- .../bme280_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/bme280_i2c/test.esp32-idf.yaml | 5 +- .../bme280_i2c/test.esp8266-ard.yaml | 5 +- .../bme280_i2c/test.rp2040-ard.yaml | 5 +- tests/components/bme280_spi/common.yaml | 7 - .../bme280_spi/test.esp32-c3-idf.yaml | 5 +- .../components/bme280_spi/test.esp32-idf.yaml | 6 +- .../bme280_spi/test.esp8266-ard.yaml | 9 +- .../bme280_spi/test.rp2040-ard.yaml | 3 + tests/components/bme680/common.yaml | 6 +- .../components/bme680/test.esp32-c3-idf.yaml | 5 +- tests/components/bme680/test.esp32-idf.yaml | 5 +- tests/components/bme680/test.esp8266-ard.yaml | 5 +- tests/components/bme680/test.rp2040-ard.yaml | 5 +- tests/components/bme680_bsec/common.yaml | 6 +- .../bme680_bsec/test.esp32-ard.yaml | 5 +- .../bme680_bsec/test.esp8266-ard.yaml | 5 +- tests/components/bme68x_bsec2_i2c/common.yaml | 6 +- .../bme68x_bsec2_i2c/test.esp32-c3-idf.yaml | 5 +- .../bme68x_bsec2_i2c/test.esp32-idf.yaml | 5 +- .../bme68x_bsec2_i2c/test.esp32-s2-idf.yaml | 5 +- .../bme68x_bsec2_i2c/test.esp32-s3-idf.yaml | 5 +- .../bme68x_bsec2_i2c/test.esp8266-ard.yaml | 5 +- .../bme68x_bsec2_i2c/test.rp2040-ard.yaml | 5 +- tests/components/bmi160/common.yaml | 6 +- .../components/bmi160/test.esp32-c3-idf.yaml | 5 +- tests/components/bmi160/test.esp32-idf.yaml | 5 +- tests/components/bmi160/test.esp8266-ard.yaml | 5 +- tests/components/bmi160/test.rp2040-ard.yaml | 5 +- tests/components/bmp085/common.yaml | 6 +- .../components/bmp085/test.esp32-c3-idf.yaml | 5 +- tests/components/bmp085/test.esp32-idf.yaml | 5 +- tests/components/bmp085/test.esp8266-ard.yaml | 5 +- tests/components/bmp085/test.rp2040-ard.yaml | 5 +- tests/components/bmp280_i2c/common.yaml | 7 +- .../bmp280_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/bmp280_i2c/test.esp32-idf.yaml | 5 +- .../bmp280_i2c/test.esp8266-ard.yaml | 5 +- .../bmp280_i2c/test.rp2040-ard.yaml | 5 +- tests/components/bmp280_spi/common.yaml | 7 - .../bmp280_spi/test.esp32-c3-idf.yaml | 5 +- .../components/bmp280_spi/test.esp32-idf.yaml | 6 +- .../bmp280_spi/test.esp8266-ard.yaml | 9 +- .../bmp280_spi/test.rp2040-ard.yaml | 3 + tests/components/bmp3xx_i2c/common.yaml | 7 +- .../bmp3xx_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/bmp3xx_i2c/test.esp32-idf.yaml | 5 +- .../bmp3xx_i2c/test.esp8266-ard.yaml | 5 +- .../bmp3xx_i2c/test.rp2040-ard.yaml | 5 +- tests/components/bmp3xx_spi/common.yaml | 7 - .../bmp3xx_spi/test.esp32-c3-idf.yaml | 5 +- .../components/bmp3xx_spi/test.esp32-idf.yaml | 6 +- .../bmp3xx_spi/test.esp8266-ard.yaml | 9 +- .../bmp3xx_spi/test.rp2040-ard.yaml | 3 + tests/components/bmp581/common.yaml | 6 +- .../components/bmp581/test.esp32-c3-idf.yaml | 5 +- tests/components/bmp581/test.esp32-idf.yaml | 5 +- tests/components/bmp581/test.esp8266-ard.yaml | 5 +- tests/components/bmp581/test.rp2040-ard.yaml | 5 +- tests/components/bp1658cj/test.esp32-idf.yaml | 4 +- .../components/bp1658cj/test.esp8266-ard.yaml | 4 +- tests/components/bp5758d/test.esp32-idf.yaml | 4 +- .../components/bp5758d/test.esp8266-ard.yaml | 4 +- tests/components/camera/common.yaml | 20 +- tests/components/camera/test.esp32-idf.yaml | 3 + .../camera_encoder/test.esp32-idf.yaml | 3 + tests/components/canbus/test.esp32-idf.yaml | 3 + tests/components/cap1188/common.yaml | 6 +- .../components/cap1188/test.esp32-c3-idf.yaml | 5 +- tests/components/cap1188/test.esp32-idf.yaml | 5 +- .../components/cap1188/test.esp8266-ard.yaml | 5 +- tests/components/cap1188/test.rp2040-ard.yaml | 5 +- tests/components/ccs811/common.yaml | 6 +- .../components/ccs811/test.esp32-c3-idf.yaml | 5 +- tests/components/ccs811/test.esp32-idf.yaml | 5 +- tests/components/ccs811/test.esp8266-ard.yaml | 5 +- tests/components/ccs811/test.rp2040-ard.yaml | 5 +- tests/components/ch422g/common.yaml | 1 + .../components/ch422g/test.esp32-c3-idf.yaml | 6 +- tests/components/ch422g/test.esp32-idf.yaml | 6 +- tests/components/ch422g/test.esp8266-ard.yaml | 6 +- tests/components/ch422g/test.rp2040-ard.yaml | 6 +- .../components/chsc6x/test.esp32-c3-idf.yaml | 15 +- tests/components/chsc6x/test.esp32-idf.yaml | 15 +- tests/components/chsc6x/test.rp2040-ard.yaml | 17 +- tests/components/cm1106/common.yaml | 6 - .../components/cm1106/test.esp32-c3-idf.yaml | 4 +- tests/components/cm1106/test.esp32-idf.yaml | 7 +- tests/components/cm1106/test.esp8266-ard.yaml | 7 +- tests/components/cm1106/test.rp2040-ard.yaml | 3 + tests/components/const/common.yaml | 6 - tests/components/const/test.esp32-s3-idf.yaml | 3 + tests/components/cs5460a/common.yaml | 6 - .../components/cs5460a/test.esp32-c3-idf.yaml | 5 +- tests/components/cs5460a/test.esp32-idf.yaml | 6 +- .../components/cs5460a/test.esp8266-ard.yaml | 9 +- tests/components/cs5460a/test.rp2040-ard.yaml | 3 + tests/components/cse7761/common.yaml | 6 - .../components/cse7761/test.esp32-c3-idf.yaml | 4 +- tests/components/cse7761/test.esp32-idf.yaml | 3 + .../components/cse7761/test.esp8266-ard.yaml | 3 + tests/components/cse7761/test.rp2040-ard.yaml | 3 + tests/components/cse7766/common.yaml | 7 - .../components/cse7766/test.esp32-c3-idf.yaml | 4 +- tests/components/cse7766/test.esp32-idf.yaml | 5 +- .../components/cse7766/test.esp8266-ard.yaml | 3 + tests/components/cse7766/test.rp2040-ard.yaml | 3 + tests/components/cst226/common.yaml | 14 +- .../components/cst226/test.esp32-c3-idf.yaml | 11 +- tests/components/cst226/test.esp32-idf.yaml | 10 +- tests/components/cst816/common.yaml | 15 +- .../components/cst816/test.esp32-c3-idf.yaml | 11 +- tests/components/cst816/test.esp32-idf.yaml | 10 +- tests/components/current_based/common.yaml | 6 +- .../current_based/test.esp32-c3-idf.yaml | 5 +- .../current_based/test.esp32-idf.yaml | 5 +- .../current_based/test.esp8266-ard.yaml | 5 +- .../current_based/test.rp2040-ard.yaml | 5 +- tests/components/cwww/common.yaml | 8 - tests/components/cwww/test.esp32-c3-idf.yaml | 13 +- tests/components/cwww/test.esp32-idf.yaml | 13 +- tests/components/cwww/test.esp8266-ard.yaml | 8 + tests/components/cwww/test.rp2040-ard.yaml | 8 + tests/components/dac7678/common.yaml | 6 +- .../components/dac7678/test.esp32-c3-idf.yaml | 5 +- tests/components/dac7678/test.esp32-idf.yaml | 5 +- .../components/dac7678/test.esp8266-ard.yaml | 5 +- tests/components/dac7678/test.rp2040-ard.yaml | 5 +- .../daikin_arc/test.esp8266-ard.yaml | 4 +- tests/components/dallas_temp/common.yaml | 2 +- .../dallas_temp/test.esp32-c3-idf.yaml | 6 + .../dallas_temp/test.esp32-idf.yaml | 6 + .../dallas_temp/test.esp8266-ard.yaml | 6 + .../dallas_temp/test.rp2040-ard.yaml | 6 + tests/components/daly_bms/common.yaml | 6 - .../daly_bms/test.esp32-c3-idf.yaml | 4 +- tests/components/daly_bms/test.esp32-idf.yaml | 3 + .../components/daly_bms/test.esp8266-ard.yaml | 3 + .../components/daly_bms/test.rp2040-ard.yaml | 3 + tests/components/dfplayer/common.yaml | 6 - .../dfplayer/test.esp32-c3-idf.yaml | 4 +- tests/components/dfplayer/test.esp32-idf.yaml | 3 + .../components/dfplayer/test.esp8266-ard.yaml | 3 + .../components/dfplayer/test.rp2040-ard.yaml | 3 + tests/components/dfrobot_sen0395/common.yaml | 6 - .../dfrobot_sen0395/test.esp32-c3-idf.yaml | 4 +- .../dfrobot_sen0395/test.esp32-idf.yaml | 3 + .../dfrobot_sen0395/test.esp8266-ard.yaml | 3 + .../dfrobot_sen0395/test.rp2040-ard.yaml | 3 + tests/components/dht12/common.yaml | 6 +- tests/components/dht12/test.esp32-c3-idf.yaml | 5 +- tests/components/dht12/test.esp32-idf.yaml | 5 +- tests/components/dht12/test.esp8266-ard.yaml | 5 +- tests/components/dht12/test.rp2040-ard.yaml | 5 +- tests/components/display/common.yaml | 6 - tests/components/dps310/common.yaml | 6 +- .../components/dps310/test.esp32-c3-idf.yaml | 5 +- tests/components/dps310/test.esp32-idf.yaml | 5 +- tests/components/dps310/test.esp8266-ard.yaml | 5 +- tests/components/dps310/test.rp2040-ard.yaml | 5 +- tests/components/ds1307/common.yaml | 6 +- .../components/ds1307/test.esp32-c3-idf.yaml | 5 +- tests/components/ds1307/test.esp32-idf.yaml | 5 +- tests/components/ds1307/test.esp8266-ard.yaml | 5 +- tests/components/ds1307/test.rp2040-ard.yaml | 5 +- tests/components/ds2484/common.yaml | 7 +- .../components/ds2484/test.esp32-c3-idf.yaml | 5 +- tests/components/ds2484/test.esp32-idf.yaml | 5 +- tests/components/ds2484/test.esp8266-ard.yaml | 5 +- tests/components/ds2484/test.rp2040-ard.yaml | 5 +- tests/components/dsmr/common.yaml | 6 - tests/components/dsmr/test.esp32-ard.yaml | 5 +- tests/components/dsmr/test.esp8266-ard.yaml | 5 +- tests/components/dsmr/test.rp2040-ard.yaml | 5 +- tests/components/ee895/common.yaml | 6 +- tests/components/ee895/test.esp32-c3-idf.yaml | 5 +- tests/components/ee895/test.esp32-idf.yaml | 5 +- tests/components/ee895/test.esp8266-ard.yaml | 5 +- tests/components/ee895/test.rp2040-ard.yaml | 5 +- tests/components/ektf2232/common.yaml | 10 +- .../ektf2232/test.esp32-c3-idf.yaml | 5 +- tests/components/ektf2232/test.esp32-idf.yaml | 5 +- .../components/ektf2232/test.esp8266-ard.yaml | 9 +- .../components/ektf2232/test.rp2040-ard.yaml | 5 +- tests/components/emc2101/common.yaml | 6 +- .../components/emc2101/test.esp32-c3-idf.yaml | 5 +- tests/components/emc2101/test.esp32-idf.yaml | 5 +- .../components/emc2101/test.esp8266-ard.yaml | 5 +- tests/components/emc2101/test.rp2040-ard.yaml | 5 +- tests/components/emmeti/test.esp8266-ard.yaml | 4 +- tests/components/ens160_i2c/common.yaml | 7 +- .../ens160_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/ens160_i2c/test.esp32-idf.yaml | 5 +- .../ens160_i2c/test.esp8266-ard.yaml | 5 +- .../ens160_i2c/test.rp2040-ard.yaml | 5 +- tests/components/ens160_spi/common.yaml | 7 - .../ens160_spi/test.esp32-c3-idf.yaml | 5 +- .../components/ens160_spi/test.esp32-idf.yaml | 6 +- .../ens160_spi/test.esp8266-ard.yaml | 9 +- .../ens160_spi/test.rp2040-ard.yaml | 3 + tests/components/ens210/common.yaml | 6 +- .../components/ens210/test.esp32-c3-idf.yaml | 5 +- tests/components/ens210/test.esp32-idf.yaml | 5 +- tests/components/ens210/test.esp8266-ard.yaml | 5 +- tests/components/ens210/test.rp2040-ard.yaml | 5 +- .../epaper_spi/test.esp32-s3-idf.yaml | 6 +- tests/components/es7210/common.yaml | 6 +- .../components/es7210/test.esp32-c3-idf.yaml | 5 +- tests/components/es7210/test.esp32-idf.yaml | 5 +- tests/components/es7243e/common.yaml | 6 +- .../components/es7243e/test.esp32-c3-idf.yaml | 5 +- tests/components/es7243e/test.esp32-idf.yaml | 5 +- tests/components/es8156/common.yaml | 6 +- .../components/es8156/test.esp32-c3-idf.yaml | 5 +- tests/components/es8156/test.esp32-idf.yaml | 5 +- tests/components/es8156/test.esp8266-ard.yaml | 5 +- tests/components/es8311/common.yaml | 6 +- .../components/es8311/test.esp32-c3-idf.yaml | 5 +- tests/components/es8311/test.esp32-idf.yaml | 5 +- tests/components/es8311/test.esp8266-ard.yaml | 5 +- tests/components/es8388/common.yaml | 6 +- .../components/es8388/test.esp32-c3-idf.yaml | 5 +- tests/components/es8388/test.esp32-idf.yaml | 5 +- tests/components/es8388/test.esp8266-ard.yaml | 5 +- .../esp32_ble_client/test.esp32-c3-idf.yaml | 3 + .../esp32_ble_client/test.esp32-idf.yaml | 3 + .../esp32_ble_tracker/test.esp32-c3-idf.yaml | 3 + .../esp32_ble_tracker/test.esp32-idf.yaml | 3 + tests/components/esp32_camera/common.yaml | 30 +- .../esp32_camera/test.esp32-idf.yaml | 3 + .../esp32_camera_web_server/common.yaml | 29 - .../test.esp32-idf.yaml | 3 + .../esp32_can/test.esp32-c3-idf.yaml | 2 +- .../test.esp32-s3-idf.yaml | 21 +- tests/components/espnow/test.esp32-idf.yaml | 3 + .../ethernet_info/test.esp32-idf.yaml | 3 + .../test.esp32-c3-idf.yaml | 3 + .../test.esp32-idf.yaml | 3 + tests/components/ezo/common.yaml | 6 +- tests/components/ezo/test.esp32-c3-idf.yaml | 5 +- tests/components/ezo/test.esp32-idf.yaml | 5 +- tests/components/ezo/test.esp8266-ard.yaml | 5 +- tests/components/ezo/test.rp2040-ard.yaml | 5 +- tests/components/ezo_pmp/common.yaml | 6 +- .../components/ezo_pmp/test.esp32-c3-idf.yaml | 5 +- tests/components/ezo_pmp/test.esp32-idf.yaml | 5 +- .../components/ezo_pmp/test.esp8266-ard.yaml | 5 +- tests/components/ezo_pmp/test.rp2040-ard.yaml | 5 +- tests/components/fingerprint_grow/common.yaml | 6 - .../fingerprint_grow/test.esp32-c3-idf.yaml | 4 +- .../fingerprint_grow/test.esp32-idf.yaml | 3 + .../fingerprint_grow/test.esp8266-ard.yaml | 7 +- .../fingerprint_grow/test.rp2040-ard.yaml | 3 + tests/components/font/common.yaml | 4 - tests/components/font/test.esp32-c3-idf.yaml | 6 +- tests/components/font/test.esp32-idf.yaml | 6 +- tests/components/font/test.esp8266-ard.yaml | 6 +- tests/components/font/test.rp2040-ard.yaml | 6 +- tests/components/fs3000/common.yaml | 6 +- .../components/fs3000/test.esp32-c3-idf.yaml | 5 +- tests/components/fs3000/test.esp32-idf.yaml | 5 +- tests/components/fs3000/test.esp8266-ard.yaml | 5 +- tests/components/fs3000/test.rp2040-ard.yaml | 5 +- tests/components/ft5x06/common.yaml | 13 +- .../components/ft5x06/test.esp32-c3-idf.yaml | 5 +- tests/components/ft5x06/test.esp32-idf.yaml | 5 +- tests/components/ft5x06/test.esp8266-ard.yaml | 5 +- tests/components/ft5x06/test.rp2040-ard.yaml | 5 +- tests/components/ft63x6/common.yaml | 19 +- .../components/ft63x6/test.esp32-c3-idf.yaml | 7 +- tests/components/ft63x6/test.esp32-idf.yaml | 9 +- tests/components/ft63x6/test.esp8266-ard.yaml | 7 +- tests/components/ft63x6/test.rp2040-ard.yaml | 7 +- tests/components/gcja5/common.yaml | 8 +- tests/components/gcja5/test.esp32-c3-idf.yaml | 5 +- tests/components/gcja5/test.esp32-idf.yaml | 5 +- tests/components/gcja5/test.esp8266-ard.yaml | 5 +- tests/components/gcja5/test.rp2040-ard.yaml | 5 +- tests/components/gdk101/common.yaml | 6 +- tests/components/gdk101/test.esp32-idf.yaml | 5 +- tests/components/gdk101/test.esp8266-ard.yaml | 5 +- tests/components/gdk101/test.rp2040-ard.yaml | 5 +- tests/components/gl_r01_i2c/common.yaml | 7 +- .../gl_r01_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/gl_r01_i2c/test.esp32-idf.yaml | 5 +- .../gl_r01_i2c/test.esp8266-ard.yaml | 5 +- .../gl_r01_i2c/test.rp2040-ard.yaml | 5 +- .../gp2y1010au0f/test.esp8266-ard.yaml | 2 +- tests/components/gp8403/common.yaml | 7 +- .../components/gp8403/test.esp32-c3-idf.yaml | 5 +- tests/components/gp8403/test.esp32-idf.yaml | 5 +- tests/components/gp8403/test.esp8266-ard.yaml | 5 +- tests/components/gp8403/test.rp2040-ard.yaml | 5 +- tests/components/gpio/test.esp8266-ard.yaml | 6 +- tests/components/gps/common.yaml | 7 - tests/components/gps/test.esp32-c3-idf.yaml | 4 +- tests/components/gps/test.esp32-idf.yaml | 3 + tests/components/gps/test.esp8266-ard.yaml | 7 +- tests/components/gps/test.rp2040-ard.yaml | 3 + tests/components/graph/common.yaml | 7 +- tests/components/graph/test.esp32-c3-idf.yaml | 5 +- tests/components/graph/test.esp32-idf.yaml | 5 +- tests/components/graph/test.esp8266-ard.yaml | 5 +- tests/components/graph/test.rp2040-ard.yaml | 5 +- .../graphical_display_menu/common.yaml | 7 +- .../test.esp32-c3-idf.yaml | 5 +- .../test.esp32-idf.yaml | 5 +- .../test.esp8266-ard.yaml | 5 +- .../test.rp2040-ard.yaml | 5 +- tests/components/grove_gas_mc_v2/common.yaml | 6 +- .../grove_gas_mc_v2/test.esp32-c3-idf.yaml | 5 +- .../grove_gas_mc_v2/test.esp32-idf.yaml | 5 +- .../grove_gas_mc_v2/test.esp8266-ard.yaml | 5 +- .../grove_gas_mc_v2/test.rp2040-ard.yaml | 5 +- tests/components/grove_tb6612fng/common.yaml | 6 +- .../grove_tb6612fng/test.esp32-c3-idf.yaml | 5 +- .../grove_tb6612fng/test.esp32-idf.yaml | 5 +- .../grove_tb6612fng/test.esp8266-ard.yaml | 5 +- .../grove_tb6612fng/test.rp2040-ard.yaml | 5 +- tests/components/growatt_solar/common.yaml | 10 +- .../growatt_solar/test.esp32-c3-idf.yaml | 4 +- .../growatt_solar/test.esp32-idf.yaml | 3 + .../growatt_solar/test.esp8266-ard.yaml | 9 +- .../growatt_solar/test.rp2040-ard.yaml | 3 + tests/components/gt911/common.yaml | 16 +- tests/components/gt911/test.esp32-c3-idf.yaml | 8 + tests/components/gt911/test.esp32-idf.yaml | 8 + tests/components/gt911/test.esp8266-ard.yaml | 29 +- tests/components/gt911/test.rp2040-ard.yaml | 8 + tests/components/haier/common.yaml | 7 - tests/components/haier/test.esp32-c3-idf.yaml | 4 +- tests/components/haier/test.esp32-idf.yaml | 7 +- tests/components/haier/test.esp8266-ard.yaml | 7 +- tests/components/haier/test.rp2040-ard.yaml | 3 + tests/components/havells_solar/common.yaml | 10 +- .../havells_solar/test.esp32-c3-idf.yaml | 4 +- .../havells_solar/test.esp32-idf.yaml | 3 + .../havells_solar/test.esp8266-ard.yaml | 9 +- .../havells_solar/test.rp2040-ard.yaml | 3 + tests/components/hbridge/common.yaml | 6 - .../components/hbridge/test.esp32-c3-idf.yaml | 8 +- tests/components/hbridge/test.esp32-idf.yaml | 8 +- .../components/hbridge/test.esp8266-ard.yaml | 8 +- tests/components/hbridge/test.rp2040-ard.yaml | 8 +- tests/components/hdc1080/common.yaml | 6 +- .../components/hdc1080/test.esp32-c3-idf.yaml | 5 +- tests/components/hdc1080/test.esp32-idf.yaml | 5 +- .../components/hdc1080/test.esp8266-ard.yaml | 5 +- tests/components/hdc1080/test.rp2040-ard.yaml | 5 +- tests/components/he60r/common.yaml | 7 - tests/components/he60r/test.esp32-c3-idf.yaml | 5 +- tests/components/he60r/test.esp32-idf.yaml | 5 +- tests/components/he60r/test.esp8266-ard.yaml | 5 +- tests/components/he60r/test.rp2040-ard.yaml | 5 +- .../components/hlw8012/test.esp8266-ard.yaml | 6 +- tests/components/hm3301/common.yaml | 6 +- .../components/hm3301/test.esp32-c3-idf.yaml | 5 +- tests/components/hm3301/test.esp32-idf.yaml | 5 +- tests/components/hm3301/test.esp8266-ard.yaml | 5 +- tests/components/hm3301/test.rp2040-ard.yaml | 5 +- tests/components/hmc5883l/common.yaml | 6 +- .../hmc5883l/test.esp32-c3-idf.yaml | 5 +- tests/components/hmc5883l/test.esp32-idf.yaml | 5 +- .../components/hmc5883l/test.esp8266-ard.yaml | 5 +- .../components/hmc5883l/test.rp2040-ard.yaml | 5 +- .../components/honeywell_hih_i2c/common.yaml | 6 +- .../honeywell_hih_i2c/test.esp32-c3-idf.yaml | 5 +- .../honeywell_hih_i2c/test.esp32-idf.yaml | 5 +- .../honeywell_hih_i2c/test.esp8266-ard.yaml | 5 +- .../honeywell_hih_i2c/test.rp2040-ard.yaml | 5 +- tests/components/honeywellabp/common.yaml | 6 - .../honeywellabp/test.esp32-c3-idf.yaml | 5 +- .../honeywellabp/test.esp32-idf.yaml | 6 +- .../honeywellabp/test.esp8266-ard.yaml | 9 +- .../honeywellabp/test.rp2040-ard.yaml | 3 + .../components/honeywellabp2_i2c/common.yaml | 6 +- .../honeywellabp2_i2c/test.esp32-c3-idf.yaml | 5 +- .../honeywellabp2_i2c/test.esp32-idf.yaml | 5 +- .../honeywellabp2_i2c/test.esp8266-ard.yaml | 5 +- .../honeywellabp2_i2c/test.rp2040-ard.yaml | 5 +- tests/components/hrxl_maxsonar_wr/common.yaml | 6 - .../hrxl_maxsonar_wr/test.esp32-c3-idf.yaml | 4 +- .../hrxl_maxsonar_wr/test.esp32-idf.yaml | 3 + .../hrxl_maxsonar_wr/test.esp8266-ard.yaml | 7 +- .../hrxl_maxsonar_wr/test.rp2040-ard.yaml | 3 + tests/components/hte501/common.yaml | 6 +- .../components/hte501/test.esp32-c3-idf.yaml | 5 +- tests/components/hte501/test.esp32-idf.yaml | 5 +- tests/components/hte501/test.esp8266-ard.yaml | 5 +- tests/components/hte501/test.rp2040-ard.yaml | 5 +- tests/components/http_request/common.yaml | 4 + tests/components/htu21d/common.yaml | 6 +- .../components/htu21d/test.esp32-c3-idf.yaml | 5 +- tests/components/htu21d/test.esp32-idf.yaml | 5 +- tests/components/htu21d/test.esp8266-ard.yaml | 5 +- tests/components/htu21d/test.rp2040-ard.yaml | 5 +- tests/components/htu31d/common.yaml | 6 +- .../components/htu31d/test.esp32-c3-idf.yaml | 5 +- tests/components/htu31d/test.esp32-idf.yaml | 5 +- tests/components/htu31d/test.esp8266-ard.yaml | 5 +- tests/components/htu31d/test.rp2040-ard.yaml | 5 +- tests/components/hx711/test.esp32-c3-idf.yaml | 4 +- tests/components/hx711/test.esp32-idf.yaml | 4 +- tests/components/hx711/test.esp8266-ard.yaml | 4 +- tests/components/hx711/test.rp2040-ard.yaml | 4 +- tests/components/hydreon_rgxx/common.yaml | 6 - .../hydreon_rgxx/test.esp32-c3-idf.yaml | 4 +- .../hydreon_rgxx/test.esp32-idf.yaml | 7 +- .../hydreon_rgxx/test.esp8266-ard.yaml | 7 +- .../hydreon_rgxx/test.rp2040-ard.yaml | 3 + tests/components/hyt271/common.yaml | 6 +- .../components/hyt271/test.esp32-c3-idf.yaml | 5 +- tests/components/hyt271/test.esp32-idf.yaml | 5 +- tests/components/hyt271/test.esp8266-ard.yaml | 5 +- tests/components/hyt271/test.rp2040-ard.yaml | 5 +- tests/components/i2c/common.yaml | 4 - tests/components/i2c/test.esp32-ard.yaml | 5 +- tests/components/i2c/test.esp32-c3-idf.yaml | 5 +- tests/components/i2c/test.esp32-idf.yaml | 5 +- tests/components/i2c/test.esp8266-ard.yaml | 5 +- tests/components/i2c/test.rp2040-ard.yaml | 5 +- tests/components/i2c_device/common.yaml | 6 +- .../i2c_device/test.esp32-c3-idf.yaml | 5 +- .../components/i2c_device/test.esp32-idf.yaml | 5 +- .../i2c_device/test.esp8266-ard.yaml | 5 +- .../i2c_device/test.rp2040-ard.yaml | 5 +- .../components/i2s_audio/test.esp32-idf.yaml | 7 +- tests/components/iaqcore/common.yaml | 6 +- .../components/iaqcore/test.esp32-c3-idf.yaml | 5 +- tests/components/iaqcore/test.esp32-idf.yaml | 5 +- .../components/iaqcore/test.esp8266-ard.yaml | 5 +- tests/components/iaqcore/test.rp2040-ard.yaml | 5 +- tests/components/ili9xxx/common.yaml | 5 - .../components/ili9xxx/test.esp32-c3-idf.yaml | 7 +- tests/components/ili9xxx/test.esp32-idf.yaml | 5 +- .../components/ili9xxx/test.esp8266-ard.yaml | 3 + tests/components/ili9xxx/test.rp2040-ard.yaml | 3 + tests/components/image/test.esp32-idf.yaml | 10 +- tests/components/image/test.esp8266-ard.yaml | 8 +- tests/components/image/test.rp2040-ard.yaml | 8 +- tests/components/ina219/common.yaml | 6 +- .../components/ina219/test.esp32-c3-idf.yaml | 5 +- tests/components/ina219/test.esp32-idf.yaml | 5 +- tests/components/ina219/test.esp8266-ard.yaml | 5 +- tests/components/ina219/test.rp2040-ard.yaml | 5 +- tests/components/ina226/common.yaml | 6 +- .../components/ina226/test.esp32-c3-idf.yaml | 5 +- tests/components/ina226/test.esp32-idf.yaml | 5 +- tests/components/ina226/test.esp8266-ard.yaml | 5 +- tests/components/ina226/test.rp2040-ard.yaml | 5 +- tests/components/ina260/common.yaml | 6 +- .../components/ina260/test.esp32-c3-idf.yaml | 5 +- tests/components/ina260/test.esp32-idf.yaml | 5 +- tests/components/ina260/test.esp8266-ard.yaml | 5 +- tests/components/ina260/test.rp2040-ard.yaml | 5 +- tests/components/ina2xx_i2c/common.yaml | 7 +- .../ina2xx_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/ina2xx_i2c/test.esp32-idf.yaml | 5 +- .../ina2xx_i2c/test.esp8266-ard.yaml | 5 +- .../ina2xx_i2c/test.rp2040-ard.yaml | 5 +- tests/components/ina2xx_spi/common.yaml | 7 - .../ina2xx_spi/test.esp32-c3-idf.yaml | 5 +- .../components/ina2xx_spi/test.esp32-idf.yaml | 6 +- .../ina2xx_spi/test.esp8266-ard.yaml | 9 +- .../ina2xx_spi/test.rp2040-ard.yaml | 3 + tests/components/ina3221/common.yaml | 6 +- .../components/ina3221/test.esp32-c3-idf.yaml | 5 +- tests/components/ina3221/test.esp32-idf.yaml | 5 +- .../components/ina3221/test.esp8266-ard.yaml | 5 +- tests/components/ina3221/test.rp2040-ard.yaml | 5 +- .../test.esp32-c3-idf.yaml | 3 + .../inkbird_ibsth1_mini/test.esp32-idf.yaml | 3 + tests/components/inkplate/common.yaml | 6 +- tests/components/inkplate/test.esp32-idf.yaml | 3 + tests/components/jsn_sr04t/common.yaml | 6 - .../jsn_sr04t/test.esp32-c3-idf.yaml | 4 +- .../components/jsn_sr04t/test.esp32-idf.yaml | 7 +- .../jsn_sr04t/test.esp8266-ard.yaml | 7 +- .../components/jsn_sr04t/test.rp2040-ard.yaml | 3 + tests/components/kamstrup_kmp/common.yaml | 6 - .../kamstrup_kmp/test.esp32-idf.yaml | 5 +- .../kamstrup_kmp/test.esp8266-ard.yaml | 3 + tests/components/kmeteriso/common.yaml | 6 +- .../kmeteriso/test.esp32-c3-idf.yaml | 5 +- .../components/kmeteriso/test.esp32-idf.yaml | 5 +- .../kmeteriso/test.esp8266-ard.yaml | 5 +- .../components/kmeteriso/test.rp2040-ard.yaml | 5 +- tests/components/kuntze/common.yaml | 10 +- .../components/kuntze/test.esp32-c3-idf.yaml | 4 +- tests/components/kuntze/test.esp32-idf.yaml | 3 + tests/components/kuntze/test.esp8266-ard.yaml | 9 +- tests/components/kuntze/test.rp2040-ard.yaml | 3 + tests/components/lc709203f/common.yaml | 6 +- .../lc709203f/test.esp32-c3-idf.yaml | 5 +- .../components/lc709203f/test.esp32-idf.yaml | 5 +- .../lc709203f/test.esp8266-ard.yaml | 5 +- .../components/lc709203f/test.rp2040-ard.yaml | 5 +- tests/components/lcd_gpio/test.esp32-idf.yaml | 2 +- .../components/lcd_gpio/test.esp8266-ard.yaml | 4 +- tests/components/lcd_menu/test.esp32-idf.yaml | 2 +- .../components/lcd_menu/test.esp8266-ard.yaml | 4 +- tests/components/lcd_pcf8574/common.yaml | 6 +- .../lcd_pcf8574/test.esp32-c3-idf.yaml | 5 +- .../lcd_pcf8574/test.esp32-idf.yaml | 5 +- .../lcd_pcf8574/test.esp8266-ard.yaml | 5 +- .../lcd_pcf8574/test.rp2040-ard.yaml | 5 +- tests/components/ld2410/common.yaml | 6 - .../components/ld2410/test.esp32-c3-idf.yaml | 3 + tests/components/ld2410/test.esp32-idf.yaml | 7 +- tests/components/ld2410/test.esp8266-ard.yaml | 7 +- tests/components/ld2410/test.rp2040-ard.yaml | 3 + tests/components/ld2412/common.yaml | 16 +- .../components/ld2412/test.esp32-c3-idf.yaml | 3 + tests/components/ld2412/test.esp32-idf.yaml | 5 +- tests/components/ld2412/test.esp8266-ard.yaml | 7 +- tests/components/ld2412/test.rp2040-ard.yaml | 3 + tests/components/ld2420/common.yaml | 6 - .../components/ld2420/test.esp32-c3-idf.yaml | 4 +- tests/components/ld2420/test.esp32-idf.yaml | 7 +- tests/components/ld2420/test.esp8266-ard.yaml | 7 +- tests/components/ld2420/test.rp2040-ard.yaml | 3 + tests/components/ld2450/common.yaml | 9 - .../components/ld2450/test.esp32-c3-idf.yaml | 3 + tests/components/ld2450/test.esp32-idf.yaml | 7 +- tests/components/ld2450/test.esp8266-ard.yaml | 7 +- tests/components/ld2450/test.rp2040-ard.yaml | 3 + tests/components/lilygo_t5_47/common.yaml | 9 +- .../lilygo_t5_47/test.esp32-c3-idf.yaml | 5 +- .../lilygo_t5_47/test.esp32-idf.yaml | 7 +- .../lilygo_t5_47/test.esp8266-ard.yaml | 7 +- .../lilygo_t5_47/test.rp2040-ard.yaml | 5 +- tests/components/lm75b/common.yaml | 6 +- tests/components/lm75b/test.esp32-c3-idf.yaml | 5 +- tests/components/lm75b/test.esp32-idf.yaml | 5 +- tests/components/lm75b/test.esp8266-ard.yaml | 5 +- tests/components/lm75b/test.rp2040-ard.yaml | 5 +- tests/components/lock/common.yaml | 4 +- tests/components/lps22/common.yaml | 1 + tests/components/lps22/test.esp32-c3-idf.yaml | 6 +- tests/components/lps22/test.esp32-idf.yaml | 6 +- tests/components/lps22/test.esp8266-ard.yaml | 6 +- tests/components/lps22/test.rp2040-ard.yaml | 6 +- tests/components/ltr390/common.yaml | 7 +- .../components/ltr390/test.esp32-c3-idf.yaml | 5 +- tests/components/ltr390/test.esp32-idf.yaml | 5 +- tests/components/ltr390/test.esp8266-ard.yaml | 5 +- tests/components/ltr390/test.rp2040-ard.yaml | 5 +- tests/components/ltr501/common.yaml | 1 - .../components/ltr501/test.esp32-c3-idf.yaml | 6 +- tests/components/ltr501/test.esp32-idf.yaml | 6 +- tests/components/ltr501/test.esp8266-ard.yaml | 6 +- tests/components/ltr501/test.rp2040-ard.yaml | 6 +- tests/components/ltr_als_ps/common.yaml | 1 - .../ltr_als_ps/test.esp32-c3-idf.yaml | 6 +- .../components/ltr_als_ps/test.esp32-idf.yaml | 6 +- .../ltr_als_ps/test.esp8266-ard.yaml | 6 +- .../ltr_als_ps/test.rp2040-ard.yaml | 6 +- tests/components/lvgl/common.yaml | 1 + tests/components/lvgl/test.esp32-idf.yaml | 16 +- tests/components/m5stack_8angle/common.yaml | 7 +- .../m5stack_8angle/test.esp32-c3-idf.yaml | 3 + .../m5stack_8angle/test.esp32-idf.yaml | 3 + .../m5stack_8angle/test.esp8266-ard.yaml | 3 + .../m5stack_8angle/test.rp2040-ard.yaml | 3 + .../components/mapping/test.esp32-c3-idf.yaml | 12 +- tests/components/mapping/test.esp32-idf.yaml | 12 +- .../components/mapping/test.esp8266-ard.yaml | 12 +- tests/components/mapping/test.rp2040-ard.yaml | 12 +- tests/components/max17043/common.yaml | 7 +- .../max17043/test.esp32-c3-idf.yaml | 5 +- tests/components/max17043/test.esp32-idf.yaml | 5 +- .../components/max17043/test.esp8266-ard.yaml | 5 +- .../components/max17043/test.rp2040-ard.yaml | 5 +- tests/components/max31855/common.yaml | 6 - .../max31855/test.esp32-c3-idf.yaml | 5 +- tests/components/max31855/test.esp32-idf.yaml | 6 +- .../components/max31855/test.esp8266-ard.yaml | 9 +- .../components/max31855/test.rp2040-ard.yaml | 3 + tests/components/max31856/common.yaml | 6 - .../max31856/test.esp32-c3-idf.yaml | 5 +- tests/components/max31856/test.esp32-idf.yaml | 6 +- .../components/max31856/test.esp8266-ard.yaml | 9 +- .../components/max31856/test.rp2040-ard.yaml | 3 + tests/components/max31865/common.yaml | 6 - .../max31865/test.esp32-c3-idf.yaml | 5 +- tests/components/max31865/test.esp32-idf.yaml | 6 +- .../components/max31865/test.esp8266-ard.yaml | 9 +- .../components/max31865/test.rp2040-ard.yaml | 3 + tests/components/max44009/common.yaml | 6 +- .../max44009/test.esp32-c3-idf.yaml | 5 +- tests/components/max44009/test.esp32-idf.yaml | 5 +- .../components/max44009/test.esp8266-ard.yaml | 5 +- .../components/max44009/test.rp2040-ard.yaml | 5 +- tests/components/max6675/common.yaml | 6 - .../components/max6675/test.esp32-c3-idf.yaml | 5 +- tests/components/max6675/test.esp32-idf.yaml | 6 +- .../components/max6675/test.esp8266-ard.yaml | 9 +- tests/components/max6675/test.rp2040-ard.yaml | 3 + tests/components/max6956/common.yaml | 6 +- .../components/max6956/test.esp32-c3-idf.yaml | 5 +- tests/components/max6956/test.esp32-idf.yaml | 5 +- .../components/max6956/test.esp8266-ard.yaml | 5 +- tests/components/max6956/test.rp2040-ard.yaml | 5 +- tests/components/max7219/common.yaml | 6 - .../components/max7219/test.esp32-c3-idf.yaml | 5 +- tests/components/max7219/test.esp32-idf.yaml | 6 +- .../components/max7219/test.esp8266-ard.yaml | 9 +- tests/components/max7219/test.rp2040-ard.yaml | 3 + tests/components/max7219digit/common.yaml | 6 - .../max7219digit/test.esp32-c3-idf.yaml | 5 +- .../max7219digit/test.esp32-idf.yaml | 6 +- .../max7219digit/test.esp8266-ard.yaml | 9 +- .../max7219digit/test.rp2040-ard.yaml | 3 + tests/components/max9611/common.yaml | 6 +- .../components/max9611/test.esp32-c3-idf.yaml | 5 +- tests/components/max9611/test.esp32-idf.yaml | 5 +- .../components/max9611/test.esp8266-ard.yaml | 5 +- tests/components/max9611/test.rp2040-ard.yaml | 5 +- tests/components/mcp23008/common.yaml | 6 +- .../mcp23008/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp23008/test.esp32-idf.yaml | 5 +- .../components/mcp23008/test.esp8266-ard.yaml | 5 +- .../components/mcp23008/test.rp2040-ard.yaml | 5 +- tests/components/mcp23016/common.yaml | 6 +- .../mcp23016/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp23016/test.esp32-idf.yaml | 5 +- .../components/mcp23016/test.esp8266-ard.yaml | 5 +- .../components/mcp23016/test.rp2040-ard.yaml | 5 +- tests/components/mcp23017/common.yaml | 6 +- .../mcp23017/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp23017/test.esp32-idf.yaml | 5 +- .../components/mcp23017/test.esp8266-ard.yaml | 5 +- .../components/mcp23017/test.rp2040-ard.yaml | 5 +- tests/components/mcp23s08/common.yaml | 6 - .../mcp23s08/test.esp32-c3-idf.yaml | 6 +- tests/components/mcp23s08/test.esp32-idf.yaml | 6 +- .../components/mcp23s08/test.esp8266-ard.yaml | 6 +- .../components/mcp23s08/test.rp2040-ard.yaml | 6 +- tests/components/mcp23s17/common.yaml | 6 - .../mcp23s17/test.esp32-c3-idf.yaml | 6 +- tests/components/mcp23s17/test.esp32-idf.yaml | 6 +- .../components/mcp23s17/test.esp8266-ard.yaml | 6 +- .../components/mcp23s17/test.rp2040-ard.yaml | 6 +- tests/components/mcp2515/common.yaml | 6 - .../components/mcp2515/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp2515/test.esp32-idf.yaml | 6 +- .../components/mcp2515/test.esp8266-ard.yaml | 9 +- tests/components/mcp2515/test.rp2040-ard.yaml | 3 + tests/components/mcp3008/common.yaml | 6 - .../components/mcp3008/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp3008/test.esp32-idf.yaml | 6 +- .../components/mcp3008/test.esp8266-ard.yaml | 9 +- tests/components/mcp3008/test.rp2040-ard.yaml | 3 + tests/components/mcp3204/common.yaml | 6 - .../components/mcp3204/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp3204/test.esp32-idf.yaml | 6 +- .../components/mcp3204/test.esp8266-ard.yaml | 9 +- tests/components/mcp3204/test.rp2040-ard.yaml | 3 + tests/components/mcp4461/common.yaml | 6 +- .../components/mcp4461/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp4461/test.esp32-idf.yaml | 5 +- .../components/mcp4461/test.esp8266-ard.yaml | 5 +- tests/components/mcp4725/common.yaml | 5 - .../components/mcp4725/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp4725/test.esp32-idf.yaml | 5 +- .../components/mcp4725/test.esp8266-ard.yaml | 5 +- tests/components/mcp4725/test.rp2040-ard.yaml | 5 +- tests/components/mcp4728/common.yaml | 6 +- .../components/mcp4728/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp4728/test.esp32-idf.yaml | 5 +- .../components/mcp4728/test.esp8266-ard.yaml | 5 +- tests/components/mcp4728/test.rp2040-ard.yaml | 5 +- tests/components/mcp47a1/common.yaml | 5 - .../components/mcp47a1/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp47a1/test.esp32-idf.yaml | 5 +- .../components/mcp47a1/test.esp8266-ard.yaml | 5 +- tests/components/mcp47a1/test.rp2040-ard.yaml | 5 +- tests/components/mcp9600/common.yaml | 6 +- .../components/mcp9600/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp9600/test.esp32-idf.yaml | 5 +- .../components/mcp9600/test.esp8266-ard.yaml | 5 +- tests/components/mcp9600/test.rp2040-ard.yaml | 5 +- tests/components/mcp9808/common.yaml | 6 +- .../components/mcp9808/test.esp32-c3-idf.yaml | 5 +- tests/components/mcp9808/test.esp32-idf.yaml | 5 +- .../components/mcp9808/test.esp8266-ard.yaml | 5 +- tests/components/mcp9808/test.rp2040-ard.yaml | 5 +- tests/components/mhz19/common.yaml | 6 - tests/components/mhz19/test.esp32-c3-idf.yaml | 4 +- tests/components/mhz19/test.esp32-idf.yaml | 7 +- tests/components/mhz19/test.esp8266-ard.yaml | 7 +- tests/components/mhz19/test.rp2040-ard.yaml | 3 + tests/components/micronova/common.yaml | 6 - .../micronova/test.esp32-c3-idf.yaml | 5 +- .../components/micronova/test.esp32-idf.yaml | 5 +- .../micronova/test.esp8266-ard.yaml | 7 +- .../components/micronova/test.rp2040-ard.yaml | 5 +- .../components/microphone/test.esp32-idf.yaml | 4 +- tests/components/mics_4514/common.yaml | 6 +- .../mics_4514/test.esp32-c3-idf.yaml | 5 +- .../components/mics_4514/test.esp32-idf.yaml | 5 +- .../mics_4514/test.esp8266-ard.yaml | 5 +- .../components/mics_4514/test.rp2040-ard.yaml | 5 +- tests/components/midea/common.yaml | 6 - tests/components/midea/test.esp32-ard.yaml | 5 +- tests/components/midea/test.esp8266-ard.yaml | 5 +- .../mipi_dsi/test.esp32-p4-idf.yaml | 9 +- .../mipi_rgb/test.esp32-s3-idf.yaml | 12 +- tests/components/mipi_spi/common.yaml | 13 +- .../mipi_spi/test-lvgl.esp32-s3-idf.yaml | 13 +- .../mipi_spi/test.esp32-c3-idf.yaml | 12 +- tests/components/mipi_spi/test.esp32-idf.yaml | 15 +- .../components/mipi_spi/test.rp2040-ard.yaml | 8 +- tests/components/mixer/test.esp32-idf.yaml | 7 +- tests/components/mlx90393/common.yaml | 6 +- .../mlx90393/test.esp32-c3-idf.yaml | 5 +- tests/components/mlx90393/test.esp32-idf.yaml | 5 +- .../mlx90393/test.esp32-s3-idf.yaml | 5 +- .../components/mlx90393/test.esp8266-ard.yaml | 5 +- .../components/mlx90393/test.rp2040-ard.yaml | 5 +- tests/components/mlx90614/common.yaml | 6 +- .../mlx90614/test.esp32-c3-idf.yaml | 5 +- tests/components/mlx90614/test.esp32-idf.yaml | 5 +- .../components/mlx90614/test.esp8266-ard.yaml | 5 +- .../components/mlx90614/test.rp2040-ard.yaml | 5 +- tests/components/mmc5603/common.yaml | 6 +- .../components/mmc5603/test.esp32-c3-idf.yaml | 5 +- tests/components/mmc5603/test.esp32-idf.yaml | 5 +- .../components/mmc5603/test.esp8266-ard.yaml | 5 +- tests/components/mmc5603/test.rp2040-ard.yaml | 5 +- tests/components/mmc5983/common.yaml | 6 +- .../components/mmc5983/test.esp32-c3-idf.yaml | 5 +- tests/components/mmc5983/test.esp32-idf.yaml | 5 +- .../components/mmc5983/test.esp8266-ard.yaml | 5 +- tests/components/mmc5983/test.rp2040-ard.yaml | 5 +- tests/components/modbus/common.yaml | 6 - .../components/modbus/test.esp32-c3-idf.yaml | 4 +- tests/components/modbus/test.esp32-idf.yaml | 3 + tests/components/modbus/test.esp8266-ard.yaml | 9 +- tests/components/modbus/test.rp2040-ard.yaml | 3 + .../components/modbus_controller/common.yaml | 17 +- .../modbus_controller/test.esp32-c3-idf.yaml | 8 +- .../modbus_controller/test.esp32-idf.yaml | 8 +- .../modbus_controller/test.esp8266-ard.yaml | 8 +- .../modbus_controller/test.rp2040-ard.yaml | 8 +- .../mopeka_ble/test.esp32-c3-idf.yaml | 3 + .../components/mopeka_ble/test.esp32-idf.yaml | 3 + .../mopeka_pro_check/test.esp32-c3-idf.yaml | 3 + .../mopeka_pro_check/test.esp32-idf.yaml | 3 + .../mopeka_std_check/test.esp32-c3-idf.yaml | 3 + .../mopeka_std_check/test.esp32-idf.yaml | 3 + tests/components/mpl3115a2/common.yaml | 6 +- .../mpl3115a2/test.esp32-c3-idf.yaml | 5 +- .../components/mpl3115a2/test.esp32-idf.yaml | 5 +- .../mpl3115a2/test.esp8266-ard.yaml | 5 +- .../components/mpl3115a2/test.rp2040-ard.yaml | 5 +- tests/components/mpr121/common.yaml | 6 +- .../components/mpr121/test.esp32-c3-idf.yaml | 3 + tests/components/mpr121/test.esp32-idf.yaml | 3 + tests/components/mpr121/test.esp8266-ard.yaml | 3 + tests/components/mpr121/test.rp2040-ard.yaml | 3 + tests/components/mpu6050/common.yaml | 6 +- .../components/mpu6050/test.esp32-c3-idf.yaml | 5 +- tests/components/mpu6050/test.esp32-idf.yaml | 5 +- .../components/mpu6050/test.esp8266-ard.yaml | 5 +- tests/components/mpu6050/test.rp2040-ard.yaml | 5 +- tests/components/mpu6886/common.yaml | 6 +- .../components/mpu6886/test.esp32-c3-idf.yaml | 5 +- tests/components/mpu6886/test.esp32-idf.yaml | 5 +- .../components/mpu6886/test.esp8266-ard.yaml | 5 +- tests/components/mpu6886/test.rp2040-ard.yaml | 5 +- tests/components/mqtt/common-update.yaml | 2 + tests/components/ms5611/common.yaml | 6 +- .../components/ms5611/test.esp32-c3-idf.yaml | 3 + tests/components/ms5611/test.esp32-idf.yaml | 3 + tests/components/ms5611/test.esp8266-ard.yaml | 3 + tests/components/ms5611/test.rp2040-ard.yaml | 3 + tests/components/msa3xx/common.yaml | 2 +- .../components/msa3xx/test.esp32-c3-idf.yaml | 6 +- tests/components/msa3xx/test.esp32-idf.yaml | 6 +- tests/components/msa3xx/test.esp8266-ard.yaml | 6 +- tests/components/msa3xx/test.rp2040-ard.yaml | 6 +- tests/components/nau7802/common.yaml | 2 +- .../components/nau7802/test.esp32-c3-idf.yaml | 6 +- tests/components/nau7802/test.esp32-idf.yaml | 6 +- .../components/nau7802/test.esp8266-ard.yaml | 6 +- tests/components/nau7802/test.rp2040-ard.yaml | 6 +- tests/components/nextion/common.yaml | 6 - tests/components/nextion/test.esp32-ard.yaml | 5 +- .../components/nextion/test.esp32-c3-idf.yaml | 5 +- tests/components/nextion/test.esp32-idf.yaml | 5 +- .../components/nextion/test.esp8266-ard.yaml | 5 +- tests/components/nextion/test.rp2040-ard.yaml | 5 +- tests/components/npi19/common.yaml | 8 +- tests/components/npi19/test.esp32-idf.yaml | 5 +- tests/components/npi19/test.esp32-s3-idf.yaml | 5 +- tests/components/npi19/test.esp8266-ard.yaml | 5 +- .../components/online_image/common-esp32.yaml | 10 +- .../online_image/common-esp8266.yaml | 10 +- .../online_image/common-rp2040.yaml | 10 +- tests/components/opt3001/common.yaml | 6 +- .../components/opt3001/test.esp32-c3-idf.yaml | 5 +- tests/components/opt3001/test.esp32-idf.yaml | 5 +- .../components/opt3001/test.esp8266-ard.yaml | 5 +- tests/components/opt3001/test.rp2040-ard.yaml | 5 +- .../packet_transport/test.esp32-c3-idf.yaml | 3 + .../packet_transport/test.esp32-idf.yaml | 3 + .../packet_transport/test.esp8266-ard.yaml | 3 + .../packet_transport/test.host.yaml | 42 +- .../packet_transport/test.rp2040-ard.yaml | 3 + tests/components/pca6416a/common.yaml | 6 +- .../pca6416a/test.esp32-c3-idf.yaml | 5 +- tests/components/pca6416a/test.esp32-idf.yaml | 5 +- .../components/pca6416a/test.esp8266-ard.yaml | 5 +- .../components/pca6416a/test.rp2040-ard.yaml | 5 +- tests/components/pca9554/common.yaml | 6 +- .../components/pca9554/test.esp32-c3-idf.yaml | 5 +- tests/components/pca9554/test.esp32-idf.yaml | 5 +- .../components/pca9554/test.esp8266-ard.yaml | 5 +- tests/components/pca9554/test.rp2040-ard.yaml | 5 +- tests/components/pca9685/common.yaml | 6 +- .../components/pca9685/test.esp32-c3-idf.yaml | 5 +- tests/components/pca9685/test.esp32-idf.yaml | 5 +- .../components/pca9685/test.esp8266-ard.yaml | 5 +- tests/components/pca9685/test.rp2040-ard.yaml | 5 +- tests/components/pcd8544/common.yaml | 6 - .../components/pcd8544/test.esp32-c3-idf.yaml | 5 +- tests/components/pcd8544/test.esp32-idf.yaml | 6 +- .../components/pcd8544/test.esp8266-ard.yaml | 7 +- tests/components/pcd8544/test.rp2040-ard.yaml | 3 + tests/components/pcf85063/common.yaml | 6 +- .../pcf85063/test.esp32-c3-idf.yaml | 5 +- tests/components/pcf85063/test.esp32-idf.yaml | 5 +- .../components/pcf85063/test.esp8266-ard.yaml | 5 +- .../components/pcf85063/test.rp2040-ard.yaml | 5 +- tests/components/pcf8563/common.yaml | 6 +- .../components/pcf8563/test.esp32-c3-idf.yaml | 5 +- tests/components/pcf8563/test.esp32-idf.yaml | 5 +- .../components/pcf8563/test.esp8266-ard.yaml | 5 +- tests/components/pcf8563/test.rp2040-ard.yaml | 5 +- tests/components/pcf8574/common.yaml | 6 +- .../components/pcf8574/test.esp32-c3-idf.yaml | 5 +- tests/components/pcf8574/test.esp32-idf.yaml | 5 +- .../components/pcf8574/test.esp8266-ard.yaml | 5 +- tests/components/pcf8574/test.rp2040-ard.yaml | 5 +- tests/components/pi4ioe5v6408/common.yaml | 6 +- .../pi4ioe5v6408/test.esp32-idf.yaml | 3 + .../pi4ioe5v6408/test.rp2040-ard.yaml | 3 + tests/components/pipsolar/common.yaml | 6 - .../pipsolar/test.esp32-c3-idf.yaml | 4 +- tests/components/pipsolar/test.esp32-idf.yaml | 7 +- .../pipsolar/test.esp32-s2-idf.yaml | 5 +- .../components/pipsolar/test.esp8266-ard.yaml | 7 +- .../components/pipsolar/test.rp2040-ard.yaml | 3 + tests/components/pm1006/common.yaml | 6 - .../components/pm1006/test.esp32-c3-idf.yaml | 4 +- tests/components/pm1006/test.esp32-idf.yaml | 7 +- tests/components/pm1006/test.esp8266-ard.yaml | 7 +- tests/components/pm1006/test.rp2040-ard.yaml | 3 + tests/components/pm2005/common.yaml | 6 +- .../components/pm2005/test.esp32-c3-idf.yaml | 5 +- tests/components/pm2005/test.esp32-idf.yaml | 5 +- tests/components/pm2005/test.esp8266-ard.yaml | 5 +- tests/components/pm2005/test.rp2040-ard.yaml | 5 +- tests/components/pmsa003i/common.yaml | 6 +- .../pmsa003i/test.esp32-c3-idf.yaml | 5 +- tests/components/pmsa003i/test.esp32-idf.yaml | 5 +- .../components/pmsa003i/test.esp8266-ard.yaml | 5 +- .../components/pmsa003i/test.rp2040-ard.yaml | 5 +- tests/components/pmsx003/common.yaml | 6 - .../components/pmsx003/test.esp32-c3-idf.yaml | 4 +- tests/components/pmsx003/test.esp32-idf.yaml | 7 +- .../components/pmsx003/test.esp8266-ard.yaml | 7 +- tests/components/pmsx003/test.rp2040-ard.yaml | 3 + tests/components/pmwcs3/common.yaml | 6 +- .../components/pmwcs3/test.esp32-c3-idf.yaml | 5 +- tests/components/pmwcs3/test.esp32-idf.yaml | 5 +- tests/components/pmwcs3/test.esp8266-ard.yaml | 5 +- tests/components/pmwcs3/test.rp2040-ard.yaml | 5 +- tests/components/pn532_i2c/common.yaml | 6 +- .../pn532_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/pn532_i2c/test.esp32-idf.yaml | 5 +- .../pn532_i2c/test.esp8266-ard.yaml | 5 +- .../components/pn532_i2c/test.rp2040-ard.yaml | 5 +- tests/components/pn532_spi/common.yaml | 6 - .../pn532_spi/test.esp32-c3-idf.yaml | 5 +- .../components/pn532_spi/test.esp32-idf.yaml | 6 +- .../pn532_spi/test.esp8266-ard.yaml | 11 +- .../components/pn532_spi/test.rp2040-ard.yaml | 3 + tests/components/pn7150_i2c/common.yaml | 6 +- .../pn7150_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/pn7150_i2c/test.esp32-idf.yaml | 5 +- .../pn7150_i2c/test.esp8266-ard.yaml | 9 +- .../pn7150_i2c/test.rp2040-ard.yaml | 5 +- tests/components/pn7160_i2c/common.yaml | 6 +- .../pn7160_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/pn7160_i2c/test.esp32-idf.yaml | 5 +- .../pn7160_i2c/test.esp8266-ard.yaml | 9 +- .../pn7160_i2c/test.rp2040-ard.yaml | 5 +- tests/components/pn7160_spi/common.yaml | 6 - .../pn7160_spi/test.esp32-c3-idf.yaml | 5 +- .../components/pn7160_spi/test.esp32-idf.yaml | 6 +- .../pn7160_spi/test.esp8266-ard.yaml | 7 +- .../pn7160_spi/test.rp2040-ard.yaml | 3 + tests/components/prometheus/common.yaml | 2 + .../components/prometheus/test.esp32-idf.yaml | 3 + .../pvvx_mithermometer/test.esp32-c3-idf.yaml | 3 + .../pvvx_mithermometer/test.esp32-idf.yaml | 3 + tests/components/pylontech/common.yaml | 6 - .../pylontech/test.esp32-c3-idf.yaml | 4 +- .../components/pylontech/test.esp32-idf.yaml | 7 +- .../pylontech/test.esp8266-ard.yaml | 7 +- .../components/pylontech/test.rp2040-ard.yaml | 3 + tests/components/pzem004t/common.yaml | 6 - .../pzem004t/test.esp32-c3-idf.yaml | 4 +- tests/components/pzem004t/test.esp32-idf.yaml | 7 +- .../components/pzem004t/test.esp8266-ard.yaml | 7 +- .../components/pzem004t/test.rp2040-ard.yaml | 3 + tests/components/pzemac/common.yaml | 9 +- .../components/pzemac/test.esp32-c3-idf.yaml | 4 +- tests/components/pzemac/test.esp32-idf.yaml | 7 +- tests/components/pzemac/test.esp8266-ard.yaml | 7 +- tests/components/pzemac/test.rp2040-ard.yaml | 3 + tests/components/pzemdc/common.yaml | 8 +- .../components/pzemdc/test.esp32-c3-idf.yaml | 4 +- tests/components/pzemdc/test.esp32-idf.yaml | 7 +- tests/components/pzemdc/test.esp8266-ard.yaml | 7 +- tests/components/pzemdc/test.rp2040-ard.yaml | 3 + tests/components/qmc5883l/common.yaml | 6 +- .../qmc5883l/test.esp32-c3-idf.yaml | 5 +- tests/components/qmc5883l/test.esp32-idf.yaml | 7 +- .../components/qmc5883l/test.esp8266-ard.yaml | 5 +- .../components/qmc5883l/test.rp2040-ard.yaml | 5 +- tests/components/qmp6988/common.yaml | 6 +- .../components/qmp6988/test.esp32-c3-idf.yaml | 5 +- tests/components/qmp6988/test.esp32-idf.yaml | 5 +- .../components/qmp6988/test.esp8266-ard.yaml | 5 +- tests/components/qmp6988/test.rp2040-ard.yaml | 5 +- tests/components/qr_code/common.yaml | 13 +- .../components/qr_code/test.esp32-c3-idf.yaml | 5 +- tests/components/qr_code/test.esp32-idf.yaml | 5 +- .../components/qr_code/test.esp8266-ard.yaml | 7 +- tests/components/qr_code/test.rp2040-ard.yaml | 3 + tests/components/qspi_dbi/common.yaml | 6 - .../qspi_dbi/test.esp32-s3-idf.yaml | 3 + tests/components/qwiic_pir/common.yaml | 6 +- .../qwiic_pir/test.esp32-c3-idf.yaml | 5 +- .../components/qwiic_pir/test.esp32-idf.yaml | 5 +- .../qwiic_pir/test.esp8266-ard.yaml | 5 +- .../components/qwiic_pir/test.rp2040-ard.yaml | 5 +- .../radon_eye_ble/test.esp32-c3-idf.yaml | 3 + .../radon_eye_ble/test.esp32-idf.yaml | 3 + .../radon_eye_rd200/test.esp32-c3-idf.yaml | 3 + .../radon_eye_rd200/test.esp32-idf.yaml | 3 + tests/components/rc522_i2c/common.yaml | 6 +- .../rc522_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/rc522_i2c/test.esp32-idf.yaml | 5 +- .../rc522_i2c/test.esp8266-ard.yaml | 5 +- .../components/rc522_i2c/test.rp2040-ard.yaml | 5 +- tests/components/rc522_spi/common.yaml | 6 - .../rc522_spi/test.esp32-c3-idf.yaml | 5 +- .../components/rc522_spi/test.esp32-idf.yaml | 6 +- .../rc522_spi/test.esp8266-ard.yaml | 9 +- .../components/rc522_spi/test.rp2040-ard.yaml | 3 + tests/components/rdm6300/common.yaml | 6 - .../components/rdm6300/test.esp32-c3-idf.yaml | 4 +- tests/components/rdm6300/test.esp32-idf.yaml | 7 +- .../components/rdm6300/test.esp8266-ard.yaml | 7 +- tests/components/rdm6300/test.rp2040-ard.yaml | 3 + .../remote_receiver/test.esp32-s3-idf.yaml | 21 +- .../remote_transmitter/test.esp32-s3-idf.yaml | 13 +- .../components/resampler/test.esp32-idf.yaml | 7 +- tests/components/rf_bridge/common.yaml | 6 - .../rf_bridge/test.esp32-c3-idf.yaml | 5 +- .../components/rf_bridge/test.esp32-idf.yaml | 5 +- .../rf_bridge/test.esp8266-ard.yaml | 5 +- .../components/rf_bridge/test.rp2040-ard.yaml | 5 +- .../ruuvi_ble/test.esp32-c3-idf.yaml | 3 + .../components/ruuvi_ble/test.esp32-idf.yaml | 3 + .../ruuvitag/test.esp32-c3-idf.yaml | 3 + tests/components/ruuvitag/test.esp32-idf.yaml | 3 + tests/components/scd30/common.yaml | 6 +- tests/components/scd30/test.esp32-c3-idf.yaml | 5 +- tests/components/scd30/test.esp32-idf.yaml | 5 +- tests/components/scd30/test.esp8266-ard.yaml | 5 +- tests/components/scd30/test.rp2040-ard.yaml | 5 +- tests/components/scd4x/common.yaml | 6 +- tests/components/scd4x/test.esp32-c3-idf.yaml | 5 +- tests/components/scd4x/test.esp32-idf.yaml | 5 +- tests/components/scd4x/test.esp8266-ard.yaml | 5 +- tests/components/scd4x/test.rp2040-ard.yaml | 5 +- tests/components/sdm_meter/common.yaml | 7 +- .../sdm_meter/test.esp32-c3-idf.yaml | 4 +- .../components/sdm_meter/test.esp32-idf.yaml | 7 +- .../sdm_meter/test.esp8266-ard.yaml | 7 +- .../components/sdm_meter/test.rp2040-ard.yaml | 3 + tests/components/sdp3x/common.yaml | 6 +- tests/components/sdp3x/test.esp32-c3-idf.yaml | 5 +- tests/components/sdp3x/test.esp32-idf.yaml | 5 +- tests/components/sdp3x/test.esp8266-ard.yaml | 5 +- tests/components/sdp3x/test.rp2040-ard.yaml | 5 +- tests/components/sds011/common.yaml | 6 - .../components/sds011/test.esp32-c3-idf.yaml | 4 +- tests/components/sds011/test.esp32-idf.yaml | 7 +- tests/components/sds011/test.esp8266-ard.yaml | 7 +- tests/components/sds011/test.rp2040-ard.yaml | 3 + tests/components/seeed_mr24hpc1/common.yaml | 9 - .../seeed_mr24hpc1/test.esp32-c3-idf.yaml | 5 +- tests/components/seeed_mr60bha2/common.yaml | 8 - .../seeed_mr60bha2/test.esp32-c3-idf.yaml | 5 +- tests/components/seeed_mr60fda2/common.yaml | 9 - .../seeed_mr60fda2/test.esp32-c3-idf.yaml | 5 +- tests/components/selec_meter/common.yaml | 7 +- .../selec_meter/test.esp32-c3-idf.yaml | 6 +- .../selec_meter/test.esp32-idf.yaml | 8 +- .../selec_meter/test.esp8266-ard.yaml | 8 +- .../selec_meter/test.rp2040-ard.yaml | 4 + tests/components/sen0321/common.yaml | 6 +- .../components/sen0321/test.esp32-c3-idf.yaml | 5 +- tests/components/sen0321/test.esp32-idf.yaml | 5 +- .../components/sen0321/test.esp8266-ard.yaml | 5 +- tests/components/sen0321/test.rp2040-ard.yaml | 5 +- tests/components/sen21231/common.yaml | 6 +- .../sen21231/test.esp32-c3-idf.yaml | 5 +- tests/components/sen21231/test.esp32-idf.yaml | 5 +- .../components/sen21231/test.esp8266-ard.yaml | 5 +- .../components/sen21231/test.rp2040-ard.yaml | 5 +- tests/components/sen5x/common.yaml | 6 +- tests/components/sen5x/test.esp32-c3-idf.yaml | 5 +- tests/components/sen5x/test.esp32-idf.yaml | 5 +- tests/components/sen5x/test.esp8266-ard.yaml | 5 +- tests/components/sen5x/test.rp2040-ard.yaml | 5 +- tests/components/senseair/common.yaml | 6 - .../senseair/test.esp32-c3-idf.yaml | 4 +- tests/components/senseair/test.esp32-idf.yaml | 7 +- .../components/senseair/test.esp8266-ard.yaml | 7 +- .../components/senseair/test.rp2040-ard.yaml | 3 + tests/components/sfa30/common.yaml | 6 +- tests/components/sfa30/test.esp32-c3-idf.yaml | 5 +- tests/components/sfa30/test.esp32-idf.yaml | 5 +- tests/components/sfa30/test.esp8266-ard.yaml | 5 +- tests/components/sfa30/test.rp2040-ard.yaml | 5 +- tests/components/sgp30/common.yaml | 6 +- tests/components/sgp30/test.esp32-c3-idf.yaml | 5 +- tests/components/sgp30/test.esp32-idf.yaml | 5 +- tests/components/sgp30/test.esp8266-ard.yaml | 5 +- tests/components/sgp30/test.rp2040-ard.yaml | 5 +- tests/components/sgp4x/common.yaml | 6 +- tests/components/sgp4x/test.esp32-c3-idf.yaml | 5 +- tests/components/sgp4x/test.esp32-idf.yaml | 5 +- tests/components/sgp4x/test.esp8266-ard.yaml | 5 +- tests/components/sgp4x/test.rp2040-ard.yaml | 5 +- tests/components/shelly_dimmer/common.yaml | 6 - .../shelly_dimmer/test.esp8266-ard.yaml | 3 + tests/components/sht3xd/common.yaml | 6 +- .../components/sht3xd/test.esp32-c3-idf.yaml | 5 +- tests/components/sht3xd/test.esp32-idf.yaml | 5 +- tests/components/sht3xd/test.esp8266-ard.yaml | 5 +- tests/components/sht3xd/test.rp2040-ard.yaml | 5 +- tests/components/sht4x/common.yaml | 6 +- tests/components/sht4x/test.esp32-c3-idf.yaml | 5 +- tests/components/sht4x/test.esp32-idf.yaml | 5 +- tests/components/sht4x/test.esp8266-ard.yaml | 5 +- tests/components/sht4x/test.rp2040-ard.yaml | 5 +- tests/components/shtcx/common.yaml | 6 +- tests/components/shtcx/test.esp32-c3-idf.yaml | 5 +- tests/components/shtcx/test.esp32-idf.yaml | 5 +- tests/components/shtcx/test.esp8266-ard.yaml | 5 +- tests/components/shtcx/test.rp2040-ard.yaml | 5 +- tests/components/sim800l/common.yaml | 6 - .../components/sim800l/test.esp32-c3-idf.yaml | 4 +- tests/components/sim800l/test.esp32-idf.yaml | 7 +- .../components/sim800l/test.esp8266-ard.yaml | 7 +- tests/components/sim800l/test.rp2040-ard.yaml | 3 + tests/components/sm16716/test.esp32-idf.yaml | 4 +- .../components/sm16716/test.esp8266-ard.yaml | 4 +- tests/components/sm2135/test.esp32-idf.yaml | 4 +- tests/components/sm2135/test.esp8266-ard.yaml | 4 +- tests/components/sm2235/test.esp32-idf.yaml | 4 +- tests/components/sm2235/test.esp8266-ard.yaml | 4 +- tests/components/sm2335/test.esp32-idf.yaml | 4 +- tests/components/sm2335/test.esp8266-ard.yaml | 4 +- tests/components/sm300d2/common.yaml | 6 - .../components/sm300d2/test.esp32-c3-idf.yaml | 4 +- tests/components/sm300d2/test.esp32-idf.yaml | 7 +- .../components/sm300d2/test.esp8266-ard.yaml | 7 +- tests/components/sm300d2/test.rp2040-ard.yaml | 3 + tests/components/sml/common.yaml | 6 - tests/components/sml/test.esp32-c3-idf.yaml | 4 +- tests/components/sml/test.esp32-idf.yaml | 7 +- tests/components/sml/test.esp8266-ard.yaml | 7 +- tests/components/sml/test.rp2040-ard.yaml | 3 + tests/components/smt100/common.yaml | 6 - .../components/smt100/test.esp32-c3-idf.yaml | 4 +- tests/components/smt100/test.esp32-idf.yaml | 7 +- tests/components/smt100/test.esp8266-ard.yaml | 7 +- tests/components/smt100/test.rp2040-ard.yaml | 3 + .../components/sn74hc165/test.esp32-idf.yaml | 2 +- .../sn74hc165/test.esp8266-ard.yaml | 4 +- tests/components/sn74hc595/common.yaml | 6 - .../sn74hc595/test.esp32-c3-idf.yaml | 10 +- .../components/sn74hc595/test.esp32-idf.yaml | 10 +- .../sn74hc595/test.esp8266-ard.yaml | 6 +- .../components/sn74hc595/test.rp2040-ard.yaml | 12 +- tests/components/sonoff_d1/common.yaml | 6 - .../sonoff_d1/test.esp32-c3-idf.yaml | 5 +- .../components/sonoff_d1/test.esp32-idf.yaml | 5 +- .../sonoff_d1/test.esp8266-ard.yaml | 5 +- .../components/sonoff_d1/test.rp2040-ard.yaml | 5 +- .../sound_level/test.esp32-idf.yaml | 3 + .../speaker/audio_dac.esp32-ard.yaml | 5 +- .../speaker/audio_dac.esp32-c3-idf.yaml | 5 +- .../speaker/audio_dac.esp32-idf.yaml | 5 +- .../components/speaker/common-audio_dac.yaml | 6 +- tests/components/speaker/test.esp32-ard.yaml | 7 +- .../components/speaker/test.esp32-c3-idf.yaml | 3 + tests/components/speaker/test.esp32-idf.yaml | 7 +- tests/components/spi/common.yaml | 5 - tests/components/spi/test.esp32-c3-idf.yaml | 5 +- tests/components/spi/test.esp32-idf.yaml | 6 +- tests/components/spi/test.esp8266-ard.yaml | 6 +- tests/components/spi_device/common.yaml | 6 - .../spi_device/test.esp32-c3-idf.yaml | 6 +- .../components/spi_device/test.esp32-idf.yaml | 6 +- .../spi_device/test.esp8266-ard.yaml | 6 +- .../spi_device/test.rp2040-ard.yaml | 6 +- tests/components/spi_led_strip/common.yaml | 5 - .../spi_led_strip/test.esp32-c3-idf.yaml | 5 +- .../spi_led_strip/test.esp32-idf.yaml | 5 +- .../spi_led_strip/test.esp8266-ard.yaml | 5 +- .../spi_led_strip/test.rp2040-ard.yaml | 5 +- tests/components/sps30/common.yaml | 6 +- tests/components/sps30/test.esp32-c3-idf.yaml | 5 +- tests/components/sps30/test.esp32-idf.yaml | 5 +- tests/components/sps30/test.esp8266-ard.yaml | 5 +- tests/components/sps30/test.rp2040-ard.yaml | 5 +- tests/components/ssd1306_i2c/common.yaml | 14 +- .../ssd1306_i2c/test.esp32-c3-idf.yaml | 5 +- .../ssd1306_i2c/test.esp32-idf.yaml | 5 +- .../ssd1306_i2c/test.esp8266-ard.yaml | 5 +- .../ssd1306_i2c/test.rp2040-ard.yaml | 5 +- tests/components/ssd1306_spi/common.yaml | 13 +- .../ssd1306_spi/test.esp32-c3-idf.yaml | 5 +- .../ssd1306_spi/test.esp32-idf.yaml | 5 +- .../ssd1306_spi/test.esp8266-ard.yaml | 7 +- .../ssd1306_spi/test.rp2040-ard.yaml | 3 + tests/components/ssd1322_spi/common.yaml | 13 +- .../ssd1322_spi/test.esp32-c3-idf.yaml | 5 +- .../ssd1322_spi/test.esp32-idf.yaml | 5 +- .../ssd1322_spi/test.esp8266-ard.yaml | 7 +- .../ssd1322_spi/test.rp2040-ard.yaml | 3 + tests/components/ssd1325_spi/common.yaml | 13 +- .../ssd1325_spi/test.esp32-c3-idf.yaml | 5 +- .../ssd1325_spi/test.esp32-idf.yaml | 5 +- .../ssd1325_spi/test.esp8266-ard.yaml | 7 +- .../ssd1325_spi/test.rp2040-ard.yaml | 3 + tests/components/ssd1327_i2c/common.yaml | 14 +- .../ssd1327_i2c/test.esp32-c3-idf.yaml | 5 +- .../ssd1327_i2c/test.esp32-idf.yaml | 5 +- .../ssd1327_i2c/test.esp8266-ard.yaml | 5 +- .../ssd1327_i2c/test.rp2040-ard.yaml | 5 +- tests/components/ssd1327_spi/common.yaml | 13 +- .../ssd1327_spi/test.esp32-c3-idf.yaml | 5 +- .../ssd1327_spi/test.esp32-idf.yaml | 5 +- .../ssd1327_spi/test.esp8266-ard.yaml | 7 +- .../ssd1327_spi/test.rp2040-ard.yaml | 3 + tests/components/ssd1331_spi/common.yaml | 13 +- .../ssd1331_spi/test.esp32-c3-idf.yaml | 5 +- .../ssd1331_spi/test.esp32-idf.yaml | 5 +- .../ssd1331_spi/test.esp8266-ard.yaml | 7 +- .../ssd1331_spi/test.rp2040-ard.yaml | 3 + tests/components/ssd1351_spi/common.yaml | 13 +- .../ssd1351_spi/test.esp32-c3-idf.yaml | 5 +- .../ssd1351_spi/test.esp32-idf.yaml | 5 +- .../ssd1351_spi/test.esp8266-ard.yaml | 7 +- .../ssd1351_spi/test.rp2040-ard.yaml | 3 + tests/components/st7567_i2c/common.yaml | 14 +- .../st7567_i2c/test.esp32-c3-idf.yaml | 5 +- .../components/st7567_i2c/test.esp32-idf.yaml | 5 +- .../st7567_i2c/test.esp8266-ard.yaml | 5 +- .../st7567_i2c/test.rp2040-ard.yaml | 5 +- tests/components/st7567_spi/common.yaml | 13 +- .../st7567_spi/test.esp32-c3-idf.yaml | 5 +- .../components/st7567_spi/test.esp32-idf.yaml | 5 +- .../st7567_spi/test.esp8266-ard.yaml | 7 +- .../st7567_spi/test.rp2040-ard.yaml | 3 + tests/components/st7701s/common.yaml | 5 - .../components/st7701s/test.esp32-s3-idf.yaml | 4 +- tests/components/st7735/common.yaml | 13 +- .../components/st7735/test.esp32-c3-idf.yaml | 5 +- tests/components/st7735/test.esp32-idf.yaml | 5 +- tests/components/st7735/test.esp8266-ard.yaml | 7 +- tests/components/st7735/test.rp2040-ard.yaml | 3 + tests/components/st7789v/common.yaml | 14 +- .../components/st7789v/test.esp32-c3-idf.yaml | 7 +- tests/components/st7789v/test.esp32-idf.yaml | 6 +- .../components/st7789v/test.esp8266-ard.yaml | 8 +- tests/components/st7789v/test.rp2040-ard.yaml | 4 + tests/components/st7920/common.yaml | 13 +- .../components/st7920/test.esp32-c3-idf.yaml | 5 +- tests/components/st7920/test.esp32-idf.yaml | 5 +- tests/components/st7920/test.esp8266-ard.yaml | 11 +- tests/components/st7920/test.rp2040-ard.yaml | 3 + tests/components/sts3x/common.yaml | 6 +- tests/components/sts3x/test.esp32-c3-idf.yaml | 5 +- tests/components/sts3x/test.esp32-idf.yaml | 5 +- tests/components/sts3x/test.esp8266-ard.yaml | 5 +- tests/components/sts3x/test.rp2040-ard.yaml | 5 +- tests/components/sun_gtil2/common.yaml | 5 - .../sun_gtil2/test.esp32-c3-idf.yaml | 3 +- .../components/sun_gtil2/test.esp32-idf.yaml | 5 +- .../sun_gtil2/test.esp8266-ard.yaml | 5 +- .../components/sun_gtil2/test.rp2040-ard.yaml | 3 + tests/components/sx126x/common.yaml | 5 - .../components/sx126x/test.esp32-c3-idf.yaml | 8 +- tests/components/sx126x/test.esp32-idf.yaml | 10 +- tests/components/sx126x/test.esp8266-ard.yaml | 9 +- tests/components/sx126x/test.rp2040-ard.yaml | 3 + tests/components/sx127x/common.yaml | 5 - .../components/sx127x/test.esp32-c3-idf.yaml | 5 +- tests/components/sx127x/test.esp32-idf.yaml | 10 +- tests/components/sx127x/test.esp8266-ard.yaml | 9 +- tests/components/sx127x/test.rp2040-ard.yaml | 3 + tests/components/sx1509/common.yaml | 6 +- .../components/sx1509/test.esp32-c3-idf.yaml | 5 +- tests/components/sx1509/test.esp32-idf.yaml | 5 +- tests/components/sx1509/test.esp8266-ard.yaml | 5 +- tests/components/sx1509/test.rp2040-ard.yaml | 5 +- tests/components/syslog/test.host.yaml | 13 +- tests/components/t6615/common.yaml | 6 - tests/components/t6615/test.esp32-c3-idf.yaml | 5 +- tests/components/t6615/test.esp32-idf.yaml | 7 +- tests/components/t6615/test.esp8266-ard.yaml | 7 +- tests/components/t6615/test.rp2040-ard.yaml | 3 + tests/components/tc74/common.yaml | 6 +- tests/components/tc74/test.esp32-c3-idf.yaml | 5 +- tests/components/tc74/test.esp32-idf.yaml | 5 +- tests/components/tc74/test.esp8266-ard.yaml | 5 +- tests/components/tc74/test.rp2040-ard.yaml | 5 +- tests/components/tca9548a/common.yaml | 9 +- .../tca9548a/test.esp32-c3-idf.yaml | 5 +- tests/components/tca9548a/test.esp32-idf.yaml | 5 +- .../components/tca9548a/test.esp8266-ard.yaml | 5 +- .../components/tca9548a/test.rp2040-ard.yaml | 5 +- tests/components/tca9555/common.yaml | 6 +- .../components/tca9555/test.esp32-c3-idf.yaml | 5 +- tests/components/tca9555/test.esp32-idf.yaml | 5 +- .../components/tca9555/test.esp8266-ard.yaml | 5 +- tests/components/tca9555/test.rp2040-ard.yaml | 5 +- tests/components/tcs34725/common.yaml | 6 +- .../tcs34725/test.esp32-c3-idf.yaml | 5 +- tests/components/tcs34725/test.esp32-idf.yaml | 5 +- .../components/tcs34725/test.esp8266-ard.yaml | 5 +- .../components/tcs34725/test.rp2040-ard.yaml | 5 +- tests/components/tee501/common.yaml | 6 +- .../components/tee501/test.esp32-c3-idf.yaml | 5 +- tests/components/tee501/test.esp32-idf.yaml | 5 +- tests/components/tee501/test.esp8266-ard.yaml | 5 +- tests/components/tee501/test.rp2040-ard.yaml | 5 +- tests/components/teleinfo/common.yaml | 7 - .../teleinfo/test.esp32-c3-idf.yaml | 4 +- tests/components/teleinfo/test.esp32-idf.yaml | 7 +- .../components/teleinfo/test.esp8266-ard.yaml | 7 +- .../components/teleinfo/test.rp2040-ard.yaml | 3 + tests/components/tem3200/common.yaml | 8 +- tests/components/tem3200/test.esp32-idf.yaml | 5 +- .../components/tem3200/test.esp32-s3-idf.yaml | 3 + .../components/tem3200/test.esp8266-ard.yaml | 5 +- tests/components/template/common-base.yaml | 342 +++++++ tests/components/template/common.yaml | 341 +------ .../template/test.nrf52-adafruit.yaml | 7 +- .../template/test.nrf52-mcumgr.yaml | 7 +- tests/components/tlc59208f/common.yaml | 8 +- .../tlc59208f/test.esp32-c3-idf.yaml | 5 +- .../components/tlc59208f/test.esp32-idf.yaml | 5 +- .../tlc59208f/test.esp8266-ard.yaml | 5 +- .../components/tlc59208f/test.rp2040-ard.yaml | 5 +- .../components/tlc5947/test.esp8266-ard.yaml | 6 +- .../components/tlc5971/test.esp8266-ard.yaml | 4 +- tests/components/tm1621/test.esp32-idf.yaml | 7 +- tests/components/tm1621/test.esp8266-ard.yaml | 9 +- tests/components/tm1621/test.rp2040-ard.yaml | 7 +- .../components/tm1637/test.esp32-c3-idf.yaml | 2 +- tests/components/tm1637/test.esp8266-ard.yaml | 4 +- tests/components/tmp102/common.yaml | 6 +- .../components/tmp102/test.esp32-c3-idf.yaml | 5 +- tests/components/tmp102/test.esp32-idf.yaml | 5 +- tests/components/tmp102/test.esp8266-ard.yaml | 5 +- tests/components/tmp102/test.rp2040-ard.yaml | 5 +- tests/components/tmp1075/common.yaml | 6 +- .../components/tmp1075/test.esp32-c3-idf.yaml | 5 +- tests/components/tmp1075/test.esp32-idf.yaml | 5 +- .../components/tmp1075/test.esp8266-ard.yaml | 5 +- tests/components/tmp1075/test.rp2040-ard.yaml | 5 +- tests/components/tmp117/common.yaml | 6 +- .../components/tmp117/test.esp32-c3-idf.yaml | 5 +- tests/components/tmp117/test.esp32-idf.yaml | 5 +- tests/components/tmp117/test.esp8266-ard.yaml | 5 +- tests/components/tmp117/test.rp2040-ard.yaml | 5 +- tests/components/tof10120/common.yaml | 6 +- .../tof10120/test.esp32-c3-idf.yaml | 5 +- tests/components/tof10120/test.esp32-idf.yaml | 5 +- .../components/tof10120/test.esp8266-ard.yaml | 5 +- .../components/tof10120/test.rp2040-ard.yaml | 5 +- tests/components/tormatic/common.yaml | 7 - .../tormatic/test.esp32-c3-idf.yaml | 4 +- tests/components/tormatic/test.esp32-idf.yaml | 7 +- .../components/tormatic/test.esp8266-ard.yaml | 7 +- .../components/tormatic/test.rp2040-ard.yaml | 3 + .../total_daily_energy/test.esp8266-ard.yaml | 6 +- tests/components/tsl2561/common.yaml | 6 +- .../components/tsl2561/test.esp32-c3-idf.yaml | 5 +- tests/components/tsl2561/test.esp32-idf.yaml | 5 +- .../components/tsl2561/test.esp8266-ard.yaml | 5 +- tests/components/tsl2561/test.rp2040-ard.yaml | 5 +- tests/components/tsl2591/common.yaml | 6 +- .../components/tsl2591/test.esp32-c3-idf.yaml | 5 +- tests/components/tsl2591/test.esp32-idf.yaml | 5 +- .../components/tsl2591/test.esp8266-ard.yaml | 5 +- tests/components/tsl2591/test.rp2040-ard.yaml | 5 +- tests/components/tt21100/common.yaml | 11 +- .../components/tt21100/test.esp32-c3-idf.yaml | 7 +- tests/components/tt21100/test.esp32-idf.yaml | 7 +- .../components/tt21100/test.esp8266-ard.yaml | 7 +- tests/components/tt21100/test.rp2040-ard.yaml | 5 +- tests/components/ttp229_bsf/common.yaml | 4 +- .../ttp229_bsf/test.esp32-c3-idf.yaml | 7 +- .../components/ttp229_bsf/test.esp32-idf.yaml | 7 +- .../ttp229_bsf/test.esp8266-ard.yaml | 7 +- .../ttp229_bsf/test.rp2040-ard.yaml | 7 +- tests/components/ttp229_lsf/common.yaml | 6 +- .../ttp229_lsf/test.esp32-c3-idf.yaml | 5 +- .../components/ttp229_lsf/test.esp32-idf.yaml | 5 +- .../ttp229_lsf/test.esp8266-ard.yaml | 5 +- .../ttp229_lsf/test.rp2040-ard.yaml | 5 +- tests/components/tuya/common.yaml | 6 - tests/components/tuya/test.esp32-c3-idf.yaml | 4 +- tests/components/tuya/test.esp32-idf.yaml | 7 +- tests/components/tuya/test.esp8266-ard.yaml | 9 +- tests/components/tuya/test.rp2040-ard.yaml | 3 + tests/components/udp/test.esp32-c3-idf.yaml | 3 + tests/components/udp/test.esp32-idf.yaml | 3 + tests/components/udp/test.esp8266-ard.yaml | 3 + tests/components/udp/test.host.yaml | 19 +- tests/components/udp/test.rp2040-ard.yaml | 3 + tests/components/ufire_ec/common.yaml | 6 +- .../ufire_ec/test.esp32-c3-idf.yaml | 5 +- tests/components/ufire_ec/test.esp32-idf.yaml | 5 +- .../components/ufire_ec/test.esp8266-ard.yaml | 5 +- .../components/ufire_ec/test.rp2040-ard.yaml | 5 +- tests/components/ufire_ise/common.yaml | 6 +- .../ufire_ise/test.esp32-c3-idf.yaml | 5 +- .../components/ufire_ise/test.esp32-idf.yaml | 5 +- .../ufire_ise/test.esp8266-ard.yaml | 5 +- .../components/ufire_ise/test.rp2040-ard.yaml | 5 +- tests/components/update/common.yaml | 2 + tests/components/uponor_smatrix/common.yaml | 7 - .../uponor_smatrix/test.esp32-c3-idf.yaml | 6 +- .../uponor_smatrix/test.esp32-idf.yaml | 5 +- .../uponor_smatrix/test.esp8266-ard.yaml | 6 +- .../uponor_smatrix/test.rp2040-ard.yaml | 6 +- tests/components/vbus/common.yaml | 6 - tests/components/vbus/test.esp32-c3-idf.yaml | 5 +- tests/components/vbus/test.esp32-idf.yaml | 5 +- tests/components/vbus/test.esp8266-ard.yaml | 5 +- tests/components/vbus/test.rp2040-ard.yaml | 5 +- tests/components/veml3235/common.yaml | 6 +- .../veml3235/test.esp32-c3-idf.yaml | 5 +- tests/components/veml3235/test.esp32-idf.yaml | 5 +- .../components/veml3235/test.esp8266-ard.yaml | 5 +- .../components/veml3235/test.rp2040-ard.yaml | 5 +- tests/components/veml7700/common.yaml | 7 +- .../veml7700/test.esp32-c3-idf.yaml | 5 +- tests/components/veml7700/test.esp32-idf.yaml | 5 +- .../components/veml7700/test.esp8266-ard.yaml | 5 +- .../components/veml7700/test.rp2040-ard.yaml | 5 +- tests/components/vl53l0x/common.yaml | 6 +- .../components/vl53l0x/test.esp32-c3-idf.yaml | 5 +- tests/components/vl53l0x/test.esp32-idf.yaml | 5 +- .../components/vl53l0x/test.esp8266-ard.yaml | 5 +- tests/components/vl53l0x/test.rp2040-ard.yaml | 5 +- .../voice_assistant/common-idf.yaml | 1 + .../voice_assistant/test.esp32-idf.yaml | 4 +- .../wake_on_lan/test.esp32-c3-idf.yaml | 3 + .../wake_on_lan/test.esp32-idf.yaml | 3 + .../wake_on_lan/test.esp8266-ard.yaml | 3 + .../wake_on_lan/test.rp2040-ard.yaml | 3 + tests/components/waveshare_epaper/common.yaml | 50 - .../waveshare_epaper/test.esp32-c3-idf.yaml | 6 +- .../waveshare_epaper/test.esp32-idf.yaml | 9 +- .../waveshare_epaper/test.esp8266-ard.yaml | 7 +- .../waveshare_epaper/test.rp2040-ard.yaml | 3 + .../web_server/test_ota.esp32-idf.yaml | 2 + .../wifi_info/test.esp32-c3-idf.yaml | 3 + .../components/wifi_info/test.esp32-idf.yaml | 3 + .../wifi_info/test.esp8266-ard.yaml | 3 + .../components/wifi_info/test.rp2040-ard.yaml | 3 + .../wireguard/test.esp32-c3-idf.yaml | 3 + .../components/wireguard/test.esp32-idf.yaml | 3 + .../wireguard/test.esp8266-ard.yaml | 3 + tests/components/wk2132_i2c/common.yaml | 9 +- .../components/wk2132_i2c/test.esp32-idf.yaml | 5 +- .../wk2132_i2c/test.esp32-s3-idf.yaml | 3 + tests/components/wk2132_spi/common.yaml | 7 - .../components/wk2132_spi/test.esp32-idf.yaml | 6 +- .../wk2132_spi/test.esp32-s3-idf.yaml | 3 + tests/components/wk2168_i2c/common.yaml | 27 +- .../components/wk2168_i2c/test.esp32-idf.yaml | 5 +- .../wk2168_i2c/test.esp32-s3-idf.yaml | 3 + tests/components/wk2168_spi/common.yaml | 27 +- .../components/wk2168_spi/test.esp32-idf.yaml | 6 +- .../wk2168_spi/test.esp32-s3-idf.yaml | 3 + tests/components/wk2204_i2c/common.yaml | 7 - .../components/wk2204_i2c/test.esp32-idf.yaml | 5 +- .../wk2204_i2c/test.esp32-s3-idf.yaml | 3 + tests/components/wk2204_spi/common.yaml | 7 - .../components/wk2204_spi/test.esp32-idf.yaml | 6 +- .../wk2204_spi/test.esp32-s3-idf.yaml | 3 + tests/components/wk2212_i2c/common.yaml | 17 +- .../components/wk2212_i2c/test.esp32-idf.yaml | 5 +- .../wk2212_i2c/test.esp32-s3-idf.yaml | 3 + tests/components/wk2212_spi/common.yaml | 23 +- .../components/wk2212_spi/test.esp32-idf.yaml | 6 +- .../wk2212_spi/test.esp32-s3-idf.yaml | 3 + tests/components/wl_134/common.yaml | 6 - .../components/wl_134/test.esp32-c3-idf.yaml | 4 +- tests/components/wl_134/test.esp32-idf.yaml | 7 +- tests/components/wl_134/test.esp8266-ard.yaml | 7 +- tests/components/wl_134/test.rp2040-ard.yaml | 3 + tests/components/wts01/common.yaml | 4 - tests/components/wts01/test.esp32-c3-idf.yaml | 4 +- tests/components/wts01/test.esp32-idf.yaml | 7 +- tests/components/wts01/test.esp8266-ard.yaml | 3 + tests/components/wts01/test.rp2040-ard.yaml | 3 + tests/components/x9c/test.esp32-idf.yaml | 3 + tests/components/x9c/test.esp8266-ard.yaml | 7 +- tests/components/x9c/test.rp2040-ard.yaml | 9 +- tests/components/xgzp68xx/common.yaml | 6 +- .../xgzp68xx/test.esp32-c3-idf.yaml | 5 +- tests/components/xgzp68xx/test.esp32-idf.yaml | 5 +- .../components/xgzp68xx/test.esp8266-ard.yaml | 5 +- .../components/xgzp68xx/test.rp2040-ard.yaml | 5 +- .../xiaomi_ble/test.esp32-c3-idf.yaml | 3 + .../components/xiaomi_ble/test.esp32-idf.yaml | 3 + .../xiaomi_cgd1/test.esp32-c3-idf.yaml | 3 + .../xiaomi_cgd1/test.esp32-idf.yaml | 3 + .../xiaomi_cgdk2/test.esp32-c3-idf.yaml | 3 + .../xiaomi_cgdk2/test.esp32-idf.yaml | 3 + .../xiaomi_cgg1/test.esp32-c3-idf.yaml | 3 + .../xiaomi_cgg1/test.esp32-idf.yaml | 3 + .../xiaomi_cgpr1/test.esp32-c3-idf.yaml | 3 + .../xiaomi_cgpr1/test.esp32-idf.yaml | 3 + .../xiaomi_gcls002/test.esp32-c3-idf.yaml | 3 + .../xiaomi_gcls002/test.esp32-idf.yaml | 3 + .../xiaomi_hhccjcy01/test.esp32-c3-idf.yaml | 3 + .../xiaomi_hhccjcy01/test.esp32-idf.yaml | 3 + .../xiaomi_hhccpot002/test.esp32-c3-idf.yaml | 3 + .../xiaomi_hhccpot002/test.esp32-idf.yaml | 3 + .../xiaomi_jqjcy01ym/test.esp32-c3-idf.yaml | 3 + .../xiaomi_jqjcy01ym/test.esp32-idf.yaml | 3 + .../xiaomi_lywsd02/test.esp32-c3-idf.yaml | 3 + .../xiaomi_lywsd02/test.esp32-idf.yaml | 3 + .../xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml | 3 + .../xiaomi_lywsd02mmc/test.esp32-idf.yaml | 3 + .../xiaomi_lywsd03mmc/test.esp32-c3-idf.yaml | 3 + .../xiaomi_lywsd03mmc/test.esp32-idf.yaml | 3 + .../xiaomi_lywsdcgq/test.esp32-c3-idf.yaml | 3 + .../xiaomi_lywsdcgq/test.esp32-idf.yaml | 3 + .../xiaomi_mhoc303/test.esp32-c3-idf.yaml | 3 + .../xiaomi_mhoc303/test.esp32-idf.yaml | 3 + .../xiaomi_mhoc401/test.esp32-c3-idf.yaml | 3 + .../xiaomi_mhoc401/test.esp32-idf.yaml | 3 + .../xiaomi_miscale copy/common.yaml | 9 - .../xiaomi_miscale copy/test.esp32-ard.yaml | 1 - .../test.esp32-c3-ard.yaml | 1 - .../test.esp32-c3-idf.yaml | 1 - .../xiaomi_miscale copy/test.esp32-idf.yaml | 1 - .../xiaomi_miscale/test.esp32-c3-idf.yaml | 3 + .../xiaomi_miscale/test.esp32-idf.yaml | 3 + .../xiaomi_mjyd02yla/test.esp32-c3-idf.yaml | 3 + .../xiaomi_mjyd02yla/test.esp32-idf.yaml | 3 + .../xiaomi_mue4094rt/test.esp32-c3-idf.yaml | 3 + .../xiaomi_mue4094rt/test.esp32-idf.yaml | 3 + .../xiaomi_rtcgq02lm/test.esp32-c3-idf.yaml | 3 + .../xiaomi_rtcgq02lm/test.esp32-idf.yaml | 3 + .../xiaomi_wx08zm/test.esp32-c3-idf.yaml | 3 + .../xiaomi_wx08zm/test.esp32-idf.yaml | 3 + .../xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml | 3 + .../xiaomi_xmwsdj04mmc/test.esp32-idf.yaml | 3 + tests/components/xl9535/common.yaml | 6 +- .../components/xl9535/test.esp32-c3-idf.yaml | 5 +- tests/components/xl9535/test.esp32-idf.yaml | 5 +- tests/components/xl9535/test.esp8266-ard.yaml | 5 +- tests/components/xl9535/test.rp2040-ard.yaml | 5 +- tests/components/xpt2046/common.yaml | 6 - .../components/xpt2046/test.esp32-c3-idf.yaml | 5 +- tests/components/xpt2046/test.esp32-idf.yaml | 6 +- .../components/xpt2046/test.esp8266-ard.yaml | 5 +- tests/components/xpt2046/test.rp2040-ard.yaml | 3 + tests/components/zio_ultrasonic/common.yaml | 6 +- .../zio_ultrasonic/test.esp32-c3-idf.yaml | 5 +- .../zio_ultrasonic/test.esp32-idf.yaml | 5 +- .../zio_ultrasonic/test.esp8266-ard.yaml | 5 +- .../zio_ultrasonic/test.rp2040-ard.yaml | 5 +- tests/components/zwave_proxy/common.yaml | 6 - .../zwave_proxy/test.esp32-c3-idf.yaml | 4 +- .../zwave_proxy/test.esp32-idf.yaml | 7 +- .../zwave_proxy/test.esp8266-ard.yaml | 7 +- .../zwave_proxy/test.rp2040-ard.yaml | 3 + tests/components/zyaura/test.esp32-idf.yaml | 4 +- tests/components/zyaura/test.esp8266-ard.yaml | 4 +- .../build_components_base.bk72xx-ard.yaml | 2 +- tests/test_build_components/common/README.md | 248 +++++ .../common/ble/esp32-ard.yaml | 9 + .../common/ble/esp32-c3-idf.yaml | 9 + .../common/ble/esp32-idf.yaml | 9 + .../common/camera/esp32-idf.yaml | 29 + .../common/i2c/bk72xx-ard.yaml | 11 + .../common/i2c/esp32-ard.yaml | 11 + .../common/i2c/esp32-c3-ard.yaml | 11 + .../common/i2c/esp32-c3-idf.yaml | 11 + .../common/i2c/esp32-idf.yaml | 13 + .../common/i2c/esp32-p4-idf.yaml | 13 + .../common/i2c/esp32-s2-ard.yaml | 11 + .../common/i2c/esp32-s2-idf.yaml | 11 + .../common/i2c/esp32-s3-ard.yaml | 11 + .../common/i2c/esp32-s3-idf.yaml | 11 + .../common/i2c/esp8266-ard.yaml | 11 + .../common/i2c/rp2040-ard.yaml | 11 + .../common/i2c_low_freq/esp32-ard.yaml | 12 + .../common/i2c_low_freq/esp32-c3-ard.yaml | 12 + .../common/i2c_low_freq/esp32-c3-idf.yaml | 12 + .../common/i2c_low_freq/esp32-idf.yaml | 13 + .../common/i2c_low_freq/esp8266-ard.yaml | 12 + .../common/i2c_low_freq/rp2040-ard.yaml | 12 + .../common/modbus/bk72xx-ard.yaml | 12 + .../common/modbus/esp32-ard.yaml | 12 + .../common/modbus/esp32-c3-ard.yaml | 12 + .../common/modbus/esp32-c3-idf.yaml | 12 + .../common/modbus/esp32-idf.yaml | 13 + .../common/modbus/esp32-s2-ard.yaml | 12 + .../common/modbus/esp32-s2-idf.yaml | 12 + .../common/modbus/esp32-s3-ard.yaml | 12 + .../common/modbus/esp32-s3-idf.yaml | 12 + .../common/modbus/esp8266-ard.yaml | 12 + .../common/modbus/rp2040-ard.yaml | 12 + .../common/qspi/esp32-s3-idf.yaml | 13 + .../common/spi/bk72xx-ard.yaml | 12 + .../common/spi/esp32-ard.yaml | 12 + .../common/spi/esp32-c3-ard.yaml | 12 + .../common/spi/esp32-c3-idf.yaml | 15 + .../common/spi/esp32-idf.yaml | 15 + .../common/spi/esp32-s2-ard.yaml | 12 + .../common/spi/esp32-s2-idf.yaml | 12 + .../common/spi/esp32-s3-ard.yaml | 12 + .../common/spi/esp32-s3-idf.yaml | 12 + .../common/spi/esp8266-ard.yaml | 12 + .../common/spi/rp2040-ard.yaml | 12 + .../common/uart/bk72xx-ard.yaml | 13 + .../common/uart/esp32-ard.yaml | 11 + .../common/uart/esp32-c3-ard.yaml | 11 + .../common/uart/esp32-c3-idf.yaml | 13 + .../common/uart/esp32-idf.yaml | 13 + .../common/uart/esp8266-ard.yaml | 11 + .../common/uart/rp2040-ard.yaml | 11 + .../common/uart_115200/esp32-ard.yaml | 11 + .../common/uart_115200/esp32-c3-ard.yaml | 11 + .../common/uart_115200/esp32-c3-idf.yaml | 12 + .../common/uart_115200/esp32-idf.yaml | 12 + .../common/uart_115200/esp8266-ard.yaml | 11 + .../common/uart_115200/rp2040-ard.yaml | 11 + .../common/uart_1200/esp32-ard.yaml | 11 + .../common/uart_1200/esp32-c3-ard.yaml | 11 + .../common/uart_1200/esp32-c3-idf.yaml | 11 + .../common/uart_1200/esp32-idf.yaml | 11 + .../common/uart_1200/esp8266-ard.yaml | 11 + .../common/uart_1200/rp2040-ard.yaml | 11 + .../common/uart_1200_even/esp32-ard.yaml | 12 + .../common/uart_1200_even/esp32-c3-ard.yaml | 12 + .../common/uart_1200_even/esp32-c3-idf.yaml | 12 + .../common/uart_1200_even/esp32-idf.yaml | 12 + .../common/uart_1200_even/esp8266-ard.yaml | 12 + .../common/uart_1200_even/rp2040-ard.yaml | 12 + .../common/uart_19200/esp32-ard.yaml | 11 + .../common/uart_19200/esp32-c3-ard.yaml | 11 + .../common/uart_19200/esp32-c3-idf.yaml | 12 + .../common/uart_19200/esp32-idf.yaml | 12 + .../common/uart_19200/esp8266-ard.yaml | 11 + .../common/uart_19200/rp2040-ard.yaml | 11 + .../common/uart_38400/esp32-ard.yaml | 11 + .../common/uart_38400/esp32-c3-ard.yaml | 11 + .../common/uart_38400/esp32-c3-idf.yaml | 11 + .../common/uart_38400/esp32-idf.yaml | 11 + .../common/uart_38400/esp8266-ard.yaml | 11 + .../common/uart_38400/rp2040-ard.yaml | 11 + .../common/uart_4800/esp32-ard.yaml | 11 + .../common/uart_4800/esp32-c3-ard.yaml | 11 + .../common/uart_4800/esp32-c3-idf.yaml | 11 + .../common/uart_4800/esp32-idf.yaml | 11 + .../common/uart_4800/esp8266-ard.yaml | 11 + .../common/uart_4800/rp2040-ard.yaml | 11 + .../common/uart_4800_even/esp32-ard.yaml | 12 + .../common/uart_4800_even/esp32-c3-ard.yaml | 12 + .../common/uart_4800_even/esp32-c3-idf.yaml | 12 + .../common/uart_4800_even/esp32-idf.yaml | 12 + .../common/uart_4800_even/esp8266-ard.yaml | 12 + .../common/uart_4800_even/rp2040-ard.yaml | 12 + .../common/uart_9600_even/esp32-ard.yaml | 12 + .../common/uart_9600_even/esp32-c3-ard.yaml | 12 + .../common/uart_9600_even/esp32-c3-idf.yaml | 12 + .../common/uart_9600_even/esp32-idf.yaml | 12 + .../common/uart_9600_even/esp8266-ard.yaml | 12 + .../common/uart_9600_even/rp2040-ard.yaml | 12 + 1808 files changed, 8564 insertions(+), 5758 deletions(-) create mode 100755 script/analyze_component_buses.py create mode 100755 script/merge_component_configs.py create mode 100755 script/split_components_for_ci.py mode change 100755 => 120000 script/test_build_components create mode 100755 script/test_build_components.py create mode 100755 script/test_component_grouping.py delete mode 100644 tests/components/adc/common.yaml create mode 100644 tests/components/api/common-base.yaml create mode 100644 tests/components/template/common-base.yaml delete mode 100644 tests/components/xiaomi_miscale copy/common.yaml delete mode 100644 tests/components/xiaomi_miscale copy/test.esp32-ard.yaml delete mode 100644 tests/components/xiaomi_miscale copy/test.esp32-c3-ard.yaml delete mode 100644 tests/components/xiaomi_miscale copy/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_miscale copy/test.esp32-idf.yaml create mode 100644 tests/test_build_components/common/README.md create mode 100644 tests/test_build_components/common/ble/esp32-ard.yaml create mode 100644 tests/test_build_components/common/ble/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/ble/esp32-idf.yaml create mode 100644 tests/test_build_components/common/camera/esp32-idf.yaml create mode 100644 tests/test_build_components/common/i2c/bk72xx-ard.yaml create mode 100644 tests/test_build_components/common/i2c/esp32-ard.yaml create mode 100644 tests/test_build_components/common/i2c/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/i2c/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/i2c/esp32-idf.yaml create mode 100644 tests/test_build_components/common/i2c/esp32-p4-idf.yaml create mode 100644 tests/test_build_components/common/i2c/esp32-s2-ard.yaml create mode 100644 tests/test_build_components/common/i2c/esp32-s2-idf.yaml create mode 100644 tests/test_build_components/common/i2c/esp32-s3-ard.yaml create mode 100644 tests/test_build_components/common/i2c/esp32-s3-idf.yaml create mode 100644 tests/test_build_components/common/i2c/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/i2c/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/i2c_low_freq/esp32-ard.yaml create mode 100644 tests/test_build_components/common/i2c_low_freq/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/i2c_low_freq/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/i2c_low_freq/esp32-idf.yaml create mode 100644 tests/test_build_components/common/i2c_low_freq/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/i2c_low_freq/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/modbus/bk72xx-ard.yaml create mode 100644 tests/test_build_components/common/modbus/esp32-ard.yaml create mode 100644 tests/test_build_components/common/modbus/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/modbus/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/modbus/esp32-idf.yaml create mode 100644 tests/test_build_components/common/modbus/esp32-s2-ard.yaml create mode 100644 tests/test_build_components/common/modbus/esp32-s2-idf.yaml create mode 100644 tests/test_build_components/common/modbus/esp32-s3-ard.yaml create mode 100644 tests/test_build_components/common/modbus/esp32-s3-idf.yaml create mode 100644 tests/test_build_components/common/modbus/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/modbus/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/qspi/esp32-s3-idf.yaml create mode 100644 tests/test_build_components/common/spi/bk72xx-ard.yaml create mode 100644 tests/test_build_components/common/spi/esp32-ard.yaml create mode 100644 tests/test_build_components/common/spi/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/spi/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/spi/esp32-idf.yaml create mode 100644 tests/test_build_components/common/spi/esp32-s2-ard.yaml create mode 100644 tests/test_build_components/common/spi/esp32-s2-idf.yaml create mode 100644 tests/test_build_components/common/spi/esp32-s3-ard.yaml create mode 100644 tests/test_build_components/common/spi/esp32-s3-idf.yaml create mode 100644 tests/test_build_components/common/spi/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/spi/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/uart/bk72xx-ard.yaml create mode 100644 tests/test_build_components/common/uart/esp32-ard.yaml create mode 100644 tests/test_build_components/common/uart/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/uart/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/uart/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/uart/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/uart_115200/esp32-ard.yaml create mode 100644 tests/test_build_components/common/uart_115200/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/uart_115200/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/uart_115200/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart_115200/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/uart_115200/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/uart_1200/esp32-ard.yaml create mode 100644 tests/test_build_components/common/uart_1200/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/uart_1200/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/uart_1200/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart_1200/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/uart_1200/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/uart_1200_even/esp32-ard.yaml create mode 100644 tests/test_build_components/common/uart_1200_even/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/uart_1200_even/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/uart_1200_even/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart_1200_even/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/uart_1200_even/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/uart_19200/esp32-ard.yaml create mode 100644 tests/test_build_components/common/uart_19200/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/uart_19200/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/uart_19200/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart_19200/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/uart_19200/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/uart_38400/esp32-ard.yaml create mode 100644 tests/test_build_components/common/uart_38400/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/uart_38400/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/uart_38400/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart_38400/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/uart_38400/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/uart_4800/esp32-ard.yaml create mode 100644 tests/test_build_components/common/uart_4800/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/uart_4800/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/uart_4800/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart_4800/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/uart_4800/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/uart_4800_even/esp32-ard.yaml create mode 100644 tests/test_build_components/common/uart_4800_even/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/uart_4800_even/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/uart_4800_even/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart_4800_even/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/uart_4800_even/rp2040-ard.yaml create mode 100644 tests/test_build_components/common/uart_9600_even/esp32-ard.yaml create mode 100644 tests/test_build_components/common/uart_9600_even/esp32-c3-ard.yaml create mode 100644 tests/test_build_components/common/uart_9600_even/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/uart_9600_even/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart_9600_even/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/uart_9600_even/rp2040-ard.yaml diff --git a/.ai/instructions.md b/.ai/instructions.md index 1cd77b9136..d2e173472a 100644 --- a/.ai/instructions.md +++ b/.ai/instructions.md @@ -186,6 +186,11 @@ This document provides essential context for AI models interacting with this pro └── components/[component]/ # Component-specific tests ``` Run them using `script/test_build_components`. Use `-c ` to test specific components and `-t ` for specific platforms. + * **Testing All Components Together:** To verify that all components can be tested together without ID conflicts or configuration issues, use: + ```bash + ./script/test_component_grouping.py -e config --all + ``` + This tests all components in a single build to catch conflicts that might not appear when testing components individually. Use `-e config` for fast configuration validation, or `-e compile` for full compilation testing. * **Debugging and Troubleshooting:** * **Debug Tools:** - `esphome config .yaml` to validate configuration. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d7043c888..4451007da0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -369,10 +369,11 @@ jobs: matrix: file: ${{ fromJson(needs.determine-jobs.outputs.changed-components) }} steps: - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install libsdl2-dev + - name: Cache apt packages + uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.5.3 + with: + packages: libsdl2-dev + version: 1.0 - name: Check out code from GitHub uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -381,17 +382,17 @@ jobs: with: python-version: ${{ env.DEFAULT_PYTHON }} cache-key: ${{ needs.common.outputs.cache-key }} - - name: test_build_components -e config -c ${{ matrix.file }} + - name: Validate config for ${{ matrix.file }} run: | . venv/bin/activate - ./script/test_build_components -e config -c ${{ matrix.file }} - - name: test_build_components -e compile -c ${{ matrix.file }} + python3 script/test_build_components.py -e config -c ${{ matrix.file }} + - name: Compile config for ${{ matrix.file }} run: | . venv/bin/activate - ./script/test_build_components -e compile -c ${{ matrix.file }} + python3 script/test_build_components.py -e compile -c ${{ matrix.file }} test-build-components-splitter: - name: Split components for testing into 10 components per group + name: Split components for intelligent grouping (40 weighted per batch) runs-on: ubuntu-24.04 needs: - common @@ -402,14 +403,26 @@ jobs: steps: - name: Check out code from GitHub uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Split components into groups of 10 + - name: Restore Python + uses: ./.github/actions/restore-python + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache-key: ${{ needs.common.outputs.cache-key }} + - name: Split components intelligently based on bus configurations id: split run: | - components=$(echo '${{ needs.determine-jobs.outputs.changed-components }}' | jq -c '.[]' | shuf | jq -s -c '[_nwise(10) | join(" ")]') - echo "components=$components" >> $GITHUB_OUTPUT + . venv/bin/activate + + # Use intelligent splitter that groups components with same bus configs + components='${{ needs.determine-jobs.outputs.changed-components }}' + + echo "Splitting components intelligently..." + output=$(python3 script/split_components_for_ci.py --components "$components" --batch-size 40 --output github) + + echo "$output" >> $GITHUB_OUTPUT test-build-components-split: - name: Test split components + name: Test components batch (${{ matrix.components }}) runs-on: ubuntu-24.04 needs: - common @@ -418,17 +431,23 @@ jobs: if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) >= 100 strategy: fail-fast: false - max-parallel: 4 + max-parallel: 5 matrix: components: ${{ fromJson(needs.test-build-components-splitter.outputs.matrix) }} steps: + - name: Show disk space + run: | + echo "Available disk space:" + df -h + - name: List components run: echo ${{ matrix.components }} - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install libsdl2-dev + - name: Cache apt packages + uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.5.3 + with: + packages: libsdl2-dev + version: 1.0 - name: Check out code from GitHub uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -437,20 +456,37 @@ jobs: with: python-version: ${{ env.DEFAULT_PYTHON }} cache-key: ${{ needs.common.outputs.cache-key }} - - name: Validate config + - name: Validate and compile components with intelligent grouping run: | . venv/bin/activate - for component in ${{ matrix.components }}; do - ./script/test_build_components -e config -c $component - done - - name: Compile config - run: | - . venv/bin/activate - mkdir build_cache - export PLATFORMIO_BUILD_CACHE_DIR=$PWD/build_cache - for component in ${{ matrix.components }}; do - ./script/test_build_components -e compile -c $component - done + # Use /mnt for build files (70GB available vs ~29GB on /) + # Bind mount PlatformIO directory to /mnt (tools, packages, build cache all go there) + sudo mkdir -p /mnt/platformio + sudo chown $USER:$USER /mnt/platformio + mkdir -p ~/.platformio + sudo mount --bind /mnt/platformio ~/.platformio + + # Bind mount test build directory to /mnt + sudo mkdir -p /mnt/test_build_components_build + sudo chown $USER:$USER /mnt/test_build_components_build + mkdir -p tests/test_build_components/build + sudo mount --bind /mnt/test_build_components_build tests/test_build_components/build + + # Convert space-separated components to comma-separated for Python script + components_csv=$(echo "${{ matrix.components }}" | tr ' ' ',') + + echo "Testing components: $components_csv" + echo "" + + # Run config validation with grouping + python3 script/test_build_components.py -e config -c "$components_csv" -f + + echo "" + echo "Config validation passed! Starting compilation..." + echo "" + + # Run compilation with grouping + python3 script/test_build_components.py -e compile -c "$components_csv" -f pre-commit-ci-lite: name: pre-commit.ci lite diff --git a/esphome/__main__.py b/esphome/__main__.py index b0f541f521..be4551f6b5 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -1002,6 +1002,12 @@ def parse_args(argv): action="append", default=[], ) + options_parser.add_argument( + "--testing-mode", + help="Enable testing mode (disables validation checks for grouped component testing)", + action="store_true", + default=False, + ) parser = argparse.ArgumentParser( description=f"ESPHome {const.__version__}", parents=[options_parser] @@ -1260,6 +1266,7 @@ def run_esphome(argv): args = parse_args(argv) CORE.dashboard = args.dashboard + CORE.testing_mode = args.testing_mode # Create address cache from command-line arguments CORE.address_cache = AddressCache.from_cli_args( diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 05ef936baf..1c142ca7bd 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -285,6 +285,10 @@ def consume_connection_slots( def validate_connection_slots(max_connections: int) -> None: """Validate that BLE connection slots don't exceed the configured maximum.""" + # Skip validation in testing mode to allow component grouping + if CORE.testing_mode: + return + ble_data = CORE.data.get(KEY_ESP32_BLE, {}) used_slots = ble_data.get(KEY_USED_CONNECTION_SLOTS, []) num_used = len(used_slots) diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index 764576744f..f8f927d469 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -347,7 +347,7 @@ def final_validate_device_schema( def validate_pin(opt, device): def validator(value): - if opt in device: + if opt in device and not CORE.testing_mode: raise cv.Invalid( f"The uart {opt} is used both by {name} and {device[opt]}, " f"but can only be used by one. Please create a new uart bus for {name}." diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 7ab8a3ba71..43febd28a2 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -529,6 +529,8 @@ class EsphomeCore: self.dashboard = False # True if command is run from vscode api self.vscode = False + # True if running in testing mode (disables validation checks for grouped testing) + self.testing_mode = False # The name of the node self.name: str | None = None # The friendly name of the node diff --git a/esphome/core/entity_helpers.py b/esphome/core/entity_helpers.py index e1b2a8264b..f0a04b4860 100644 --- a/esphome/core/entity_helpers.py +++ b/esphome/core/entity_helpers.py @@ -246,12 +246,15 @@ def entity_duplicate_validator(platform: str) -> Callable[[ConfigType], ConfigTy "\n to distinguish them" ) - raise cv.Invalid( - f"Duplicate {platform} entity with name '{entity_name}' found{device_prefix}. " - f"{conflict_msg}. " - "Each entity on a device must have a unique name within its platform." - f"{sanitized_msg}" - ) + # Skip duplicate entity name validation when testing_mode is enabled + # This flag is used for grouped component testing + if not CORE.testing_mode: + raise cv.Invalid( + f"Duplicate {platform} entity with name '{entity_name}' found{device_prefix}. " + f"{conflict_msg}. " + "Each entity on a device must have a unique name within its platform." + f"{sanitized_msg}" + ) # Store metadata about this entity entity_metadata: EntityMetadata = { diff --git a/esphome/pins.py b/esphome/pins.py index 4f9b4859a1..601c05880a 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -118,11 +118,11 @@ class PinRegistry(dict): parent_config = fconf.get_config_for_path(parent_path) final_val_fun(pin_config, parent_config) allow_others = pin_config.get(CONF_ALLOW_OTHER_USES, False) - if count != 1 and not allow_others: + if count != 1 and not allow_others and not CORE.testing_mode: raise cv.Invalid( f"Pin {pin_config[CONF_NUMBER]} is used in multiple places" ) - if count == 1 and allow_others: + if count == 1 and allow_others and not CORE.testing_mode: raise cv.Invalid( f"Pin {pin_config[CONF_NUMBER]} incorrectly sets {CONF_ALLOW_OTHER_USES}: true" ) diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 8b7b790829..9418c1c7d3 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -5,6 +5,7 @@ import os from pathlib import Path import re import subprocess +from typing import Any from esphome.const import CONF_COMPILE_PROCESS_LIMIT, CONF_ESPHOME, KEY_CORE from esphome.core import CORE, EsphomeError @@ -42,6 +43,35 @@ def patch_structhash(): cli.clean_build_dir = patched_clean_build_dir +def patch_file_downloader(): + """Patch PlatformIO's FileDownloader to retry on PackageException errors.""" + from platformio.package.download import FileDownloader + from platformio.package.exception import PackageException + + original_init = FileDownloader.__init__ + + def patched_init(self, *args: Any, **kwargs: Any) -> None: + max_retries = 3 + + for attempt in range(max_retries): + try: + return original_init(self, *args, **kwargs) + except PackageException as e: + if attempt < max_retries - 1: + _LOGGER.warning( + "Package download failed: %s. Retrying... (attempt %d/%d)", + str(e), + attempt + 1, + max_retries, + ) + else: + # Final attempt - re-raise + raise + return None + + FileDownloader.__init__ = patched_init + + IGNORE_LIB_WARNINGS = f"(?:{'|'.join(['Hash', 'Update'])})" FILTER_PLATFORMIO_LINES = [ r"Verbose mode can be enabled via `-v, --verbose` option.*", @@ -99,6 +129,7 @@ def run_platformio_cli(*args, **kwargs) -> str | int: import platformio.__main__ patch_structhash() + patch_file_downloader() return run_external_command(platformio.__main__.main, *cmd, **kwargs) diff --git a/script/analyze_component_buses.py b/script/analyze_component_buses.py new file mode 100755 index 0000000000..39e1e66c75 --- /dev/null +++ b/script/analyze_component_buses.py @@ -0,0 +1,523 @@ +#!/usr/bin/env python3 +"""Analyze component test files to detect which common bus configs they use. + +This script scans component test files and extracts which common bus configurations +(i2c, spi, uart, etc.) are included via the packages mechanism. This information +is used to group components that can be tested together. + +Components can only be grouped together if they use the EXACT SAME set of common +bus configurations, ensuring that merged configs are compatible. + +Example output: +{ + "component1": { + "esp32-ard": ["i2c", "uart_19200"], + "esp32-idf": ["i2c", "uart_19200"] + }, + "component2": { + "esp32-ard": ["spi"], + "esp32-idf": ["spi"] + } +} +""" + +from __future__ import annotations + +import argparse +from functools import lru_cache +import json +from pathlib import Path +import re +import sys +from typing import Any + +# Add esphome to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from esphome import yaml_util +from esphome.config_helpers import Extend, Remove + +# Path to common bus configs +COMMON_BUS_PATH = Path("tests/test_build_components/common") + +# Package dependencies - maps packages to the packages they include +# When a component uses a package on the left, it automatically gets +# the packages on the right as well +PACKAGE_DEPENDENCIES = { + "modbus": ["uart"], # modbus packages include uart packages + # Add more package dependencies here as needed +} + +# Bus types that can be defined directly in config files +# Components defining these directly cannot be grouped (they create unique bus IDs) +DIRECT_BUS_TYPES = ("i2c", "spi", "uart", "modbus") + +# Signature for components with no bus requirements +# These components can be merged with any other group +NO_BUSES_SIGNATURE = "no_buses" + +# Base bus components - these ARE the bus implementations and should not +# be flagged as needing migration since they are the platform/base components +BASE_BUS_COMPONENTS = { + "i2c", + "spi", + "uart", + "modbus", + "canbus", +} + +# Components that must be tested in isolation (not grouped or batched with others) +# These have known build issues that prevent grouping +# NOTE: This should be kept in sync with both test_build_components and split_components_for_ci.py +ISOLATED_COMPONENTS = { + "animation": "Has display lambda in common.yaml that requires existing display platform - breaks when merged without display", + "esphome": "Defines devices/areas in esphome: section that are referenced in other sections - breaks when merged", + "ethernet": "Defines ethernet: which conflicts with wifi: used by most components", + "ethernet_info": "Related to ethernet component which conflicts with wifi", + "lvgl": "Defines multiple SDL displays on host platform that conflict when merged with other display configs", + "matrix_keypad": "Needs isolation due to keypad", + "mcp4725": "no YAML config to specify i2c bus id", + "mcp47a1": "no YAML config to specify i2c bus id", + "modbus_controller": "Defines multiple modbus buses for testing client/server functionality - conflicts with package modbus bus", + "neopixelbus": "RMT type conflict with ESP32 Arduino/ESP-IDF headers (enum vs struct rmt_channel_t)", + "packages": "cannot merge packages", +} + + +@lru_cache(maxsize=1) +def get_common_bus_packages() -> frozenset[str]: + """Get the list of common bus package names. + + Reads from tests/test_build_components/common/ directory + and caches the result. All bus types support component grouping + for config validation since --testing-mode bypasses runtime conflicts. + + Returns: + Frozenset of common bus package names (i2c, spi, uart, etc.) + """ + if not COMMON_BUS_PATH.exists(): + return frozenset() + + # List all directories in common/ - these are the bus package names + return frozenset(d.name for d in COMMON_BUS_PATH.iterdir() if d.is_dir()) + + +def uses_local_file_references(component_dir: Path) -> bool: + """Check if a component uses local file references via $component_dir. + + Components that reference local files cannot be grouped because each needs + a unique component_dir path pointing to their specific directory. + + Args: + component_dir: Path to the component's test directory + + Returns: + True if the component uses $component_dir for local file references + """ + common_yaml = component_dir / "common.yaml" + if not common_yaml.exists(): + return False + + try: + content = common_yaml.read_text() + except Exception: # pylint: disable=broad-exception-caught + return False + + # Pattern to match $component_dir or ${component_dir} references + # These indicate local file usage that prevents grouping + return bool(re.search(r"\$\{?component_dir\}?", content)) + + +def is_platform_component(component_dir: Path) -> bool: + """Check if a component is a platform component (abstract base class). + + Platform components have IS_PLATFORM_COMPONENT = True and cannot be + instantiated without a platform-specific implementation. These components + define abstract methods and cause linker errors if compiled standalone. + + Examples: canbus, mcp23x08_base, mcp23x17_base + + Args: + component_dir: Path to the component's test directory + + Returns: + True if this is a platform component + """ + # Check in the actual component source, not tests + # tests/components/X -> tests/components -> tests -> repo root + repo_root = component_dir.parent.parent.parent + comp_init = ( + repo_root / "esphome" / "components" / component_dir.name / "__init__.py" + ) + + if not comp_init.exists(): + return False + + try: + content = comp_init.read_text() + return "IS_PLATFORM_COMPONENT = True" in content + except Exception: # pylint: disable=broad-exception-caught + return False + + +def _contains_extend_or_remove(data: Any) -> bool: + """Recursively check if data contains Extend or Remove objects. + + Args: + data: Parsed YAML data structure + + Returns: + True if any Extend or Remove objects are found + """ + if isinstance(data, (Extend, Remove)): + return True + + if isinstance(data, dict): + for value in data.values(): + if _contains_extend_or_remove(value): + return True + + if isinstance(data, list): + for item in data: + if _contains_extend_or_remove(item): + return True + + return False + + +def analyze_yaml_file(yaml_file: Path) -> dict[str, Any]: + """Load a YAML file once and extract all needed information. + + This loads the YAML file a single time and extracts all information needed + for component analysis, avoiding multiple file reads. + + Args: + yaml_file: Path to the YAML file to analyze + + Returns: + Dictionary with keys: + - buses: set of common bus package names + - has_extend_remove: bool indicating if Extend/Remove objects are present + - has_direct_bus_config: bool indicating if buses are defined directly (not via packages) + - loaded: bool indicating if file was successfully loaded + """ + result = { + "buses": set(), + "has_extend_remove": False, + "has_direct_bus_config": False, + "loaded": False, + } + + if not yaml_file.exists(): + return result + + try: + data = yaml_util.load_yaml(yaml_file) + result["loaded"] = True + except Exception: # pylint: disable=broad-exception-caught + return result + + # Check for Extend/Remove objects + result["has_extend_remove"] = _contains_extend_or_remove(data) + + # Check if buses are defined directly (not via packages) + # Components that define i2c, spi, uart, or modbus directly in test files + # cannot be grouped because they create unique bus IDs + if isinstance(data, dict): + for bus_type in DIRECT_BUS_TYPES: + if bus_type in data: + result["has_direct_bus_config"] = True + break + + # Extract common bus packages + if not isinstance(data, dict) or "packages" not in data: + return result + + packages = data["packages"] + if not isinstance(packages, dict): + return result + + valid_buses = get_common_bus_packages() + for pkg_name in packages: + if pkg_name not in valid_buses: + continue + result["buses"].add(pkg_name) + # Add any package dependencies (e.g., modbus includes uart) + if pkg_name not in PACKAGE_DEPENDENCIES: + continue + for dep in PACKAGE_DEPENDENCIES[pkg_name]: + if dep not in valid_buses: + continue + result["buses"].add(dep) + + return result + + +def analyze_component(component_dir: Path) -> tuple[dict[str, list[str]], bool, bool]: + """Analyze a component directory to find which buses each platform uses. + + Args: + component_dir: Path to the component's test directory + + Returns: + Tuple of: + - Dictionary mapping platform to list of bus configs + Example: {"esp32-ard": ["i2c", "spi"], "esp32-idf": ["i2c"]} + - Boolean indicating if component uses !extend or !remove + - Boolean indicating if component defines buses directly (not via packages) + """ + if not component_dir.is_dir(): + return {}, False, False + + platform_buses = {} + has_extend_remove = False + has_direct_bus_config = False + + # Analyze all YAML files in the component directory + for yaml_file in component_dir.glob("*.yaml"): + analysis = analyze_yaml_file(yaml_file) + + # Track if any file uses extend/remove + if analysis["has_extend_remove"]: + has_extend_remove = True + + # Track if any file defines buses directly + if analysis["has_direct_bus_config"]: + has_direct_bus_config = True + + # For test.*.yaml files, extract platform and buses + if yaml_file.name.startswith("test.") and yaml_file.suffix == ".yaml": + # Extract platform name (e.g., test.esp32-ard.yaml -> esp32-ard) + platform = yaml_file.stem.replace("test.", "") + # Always add platform, even if it has no buses (empty list) + # This allows grouping components that don't use any shared buses + platform_buses[platform] = ( + sorted(analysis["buses"]) if analysis["buses"] else [] + ) + + return platform_buses, has_extend_remove, has_direct_bus_config + + +def analyze_all_components( + tests_dir: Path = None, +) -> tuple[dict[str, dict[str, list[str]]], set[str], set[str]]: + """Analyze all component test directories. + + Args: + tests_dir: Path to tests/components directory (defaults to auto-detect) + + Returns: + Tuple of: + - Dictionary mapping component name to platform->buses mapping + - Set of component names that cannot be grouped + - Set of component names that define buses directly (need migration warning) + """ + if tests_dir is None: + tests_dir = Path("tests/components") + + if not tests_dir.exists(): + print(f"Error: {tests_dir} does not exist", file=sys.stderr) + return {}, set(), set() + + components = {} + non_groupable = set() + direct_bus_components = set() + + for component_dir in sorted(tests_dir.iterdir()): + if not component_dir.is_dir(): + continue + + component_name = component_dir.name + platform_buses, has_extend_remove, has_direct_bus_config = analyze_component( + component_dir + ) + + if platform_buses: + components[component_name] = platform_buses + + # Note: Components using $component_dir are now groupable because the merge + # script rewrites these to absolute paths with component-specific substitutions + + # Check if component is explicitly isolated + # These have known issues that prevent grouping with other components + if component_name in ISOLATED_COMPONENTS: + non_groupable.add(component_name) + + # Check if component is a base bus component + # These ARE the bus platform implementations and define buses directly for testing + # They cannot be grouped with components that use bus packages (causes ID conflicts) + if component_name in BASE_BUS_COMPONENTS: + non_groupable.add(component_name) + + # Check if component uses !extend or !remove directives + # These rely on specific config structure and cannot be merged with other components + # The directives work within a component's own package hierarchy but break when + # merging independent components together + if has_extend_remove: + non_groupable.add(component_name) + + # Check if component defines buses directly in test files + # These create unique bus IDs and cause conflicts when merged + # Exclude base bus components (i2c, spi, uart, etc.) since they ARE the platform + if has_direct_bus_config and component_name not in BASE_BUS_COMPONENTS: + non_groupable.add(component_name) + direct_bus_components.add(component_name) + + return components, non_groupable, direct_bus_components + + +def create_grouping_signature( + platform_buses: dict[str, list[str]], platform: str +) -> str: + """Create a signature string for grouping components. + + Components with the same signature can be grouped together for testing. + All valid bus types can be grouped since --testing-mode bypasses runtime + conflicts during config validation. + + Args: + platform_buses: Mapping of platform to list of buses + platform: The specific platform to create signature for + + Returns: + Signature string (e.g., "i2c" or "uart") or empty if no valid buses + """ + buses = platform_buses.get(platform, []) + if not buses: + return "" + + # Only include valid bus types in signature + common_buses = get_common_bus_packages() + valid_buses = [b for b in buses if b in common_buses] + if not valid_buses: + return "" + + return "+".join(sorted(valid_buses)) + + +def group_components_by_signature( + components: dict[str, dict[str, list[str]]], platform: str +) -> dict[str, list[str]]: + """Group components by their bus signature for a specific platform. + + Args: + components: Component analysis results from analyze_all_components() + platform: Platform to group for (e.g., "esp32-ard") + + Returns: + Dictionary mapping signature to list of component names + Example: {"i2c+uart_19200": ["comp1", "comp2"], "spi": ["comp3"]} + """ + signature_groups: dict[str, list[str]] = {} + + for component_name, platform_buses in components.items(): + if platform not in platform_buses: + continue + + signature = create_grouping_signature(platform_buses, platform) + if not signature: + continue + + if signature not in signature_groups: + signature_groups[signature] = [] + signature_groups[signature].append(component_name) + + return signature_groups + + +def main() -> None: + """Main entry point.""" + parser = argparse.ArgumentParser( + description="Analyze component test files to detect common bus usage" + ) + parser.add_argument( + "--components", + "-c", + nargs="+", + help="Specific components to analyze (default: all)", + ) + parser.add_argument( + "--platform", + "-p", + help="Show grouping for a specific platform", + ) + parser.add_argument( + "--json", + action="store_true", + help="Output as JSON", + ) + parser.add_argument( + "--group", + action="store_true", + help="Show component groupings by bus signature", + ) + + args = parser.parse_args() + + # Analyze components + tests_dir = Path("tests/components") + + if args.components: + # Analyze only specified components + components = {} + non_groupable = set() + direct_bus_components = set() + for comp in args.components: + comp_dir = tests_dir / comp + platform_buses, has_extend_remove, has_direct_bus_config = ( + analyze_component(comp_dir) + ) + if platform_buses: + components[comp] = platform_buses + # Note: Components using $component_dir are now groupable + if comp in ISOLATED_COMPONENTS: + non_groupable.add(comp) + if comp in BASE_BUS_COMPONENTS: + non_groupable.add(comp) + if has_direct_bus_config and comp not in BASE_BUS_COMPONENTS: + non_groupable.add(comp) + direct_bus_components.add(comp) + else: + # Analyze all components + components, non_groupable, direct_bus_components = analyze_all_components( + tests_dir + ) + + # Output results + if args.group and args.platform: + # Show groupings for a specific platform + groups = group_components_by_signature(components, args.platform) + + if args.json: + print(json.dumps(groups, indent=2)) + else: + print(f"Component groupings for {args.platform}:") + print() + for signature, comp_list in sorted(groups.items()): + print(f" {signature}:") + for comp in sorted(comp_list): + print(f" - {comp}") + print() + elif args.json: + # JSON output + print(json.dumps(components, indent=2)) + else: + # Human-readable output + for component, platform_buses in sorted(components.items()): + non_groupable_marker = ( + " [NON-GROUPABLE]" if component in non_groupable else "" + ) + print(f"{component}{non_groupable_marker}:") + for platform, buses in sorted(platform_buses.items()): + bus_str = ", ".join(buses) + print(f" {platform}: {bus_str}") + print() + print(f"Total components analyzed: {len(components)}") + if non_groupable: + print(f"Non-groupable components (use local files): {len(non_groupable)}") + for comp in sorted(non_groupable): + print(f" - {comp}") + + +if __name__ == "__main__": + main() diff --git a/script/merge_component_configs.py b/script/merge_component_configs.py new file mode 100755 index 0000000000..a19b65038c --- /dev/null +++ b/script/merge_component_configs.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 +"""Merge multiple component test configurations into a single test file. + +This script combines multiple component test files that use the same common bus +configurations into a single merged test file. This allows testing multiple +compatible components together, reducing CI build time. + +The merger handles: +- Component-specific substitutions (prefixing to avoid conflicts) +- Multiple instances of component configurations +- Shared common bus packages (included only once) +- Platform-specific configurations +- Uses ESPHome's built-in merge_config for proper YAML merging +""" + +from __future__ import annotations + +import argparse +from pathlib import Path +import re +import sys +from typing import Any + +# Add esphome to path so we can import from it +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from esphome import yaml_util +from esphome.config_helpers import merge_config +from script.analyze_component_buses import PACKAGE_DEPENDENCIES, get_common_bus_packages + + +def load_yaml_file(yaml_file: Path) -> dict: + """Load YAML file using ESPHome's YAML loader. + + Args: + yaml_file: Path to the YAML file + + Returns: + Parsed YAML as dictionary + """ + if not yaml_file.exists(): + raise FileNotFoundError(f"YAML file not found: {yaml_file}") + + return yaml_util.load_yaml(yaml_file) + + +def extract_packages_from_yaml(data: dict) -> dict[str, str]: + """Extract COMMON BUS package includes from parsed YAML. + + Only extracts packages that are from test_build_components/common/, + ignoring component-specific packages. + + Args: + data: Parsed YAML dictionary + + Returns: + Dictionary mapping package name to include path (as string representation) + Only includes common bus packages (i2c, spi, uart, etc.) + """ + if "packages" not in data: + return {} + + packages_value = data["packages"] + if not isinstance(packages_value, dict): + # List format doesn't include common bus packages (those use dict format) + return {} + + # Get common bus package names (cached) + common_bus_packages = get_common_bus_packages() + packages = {} + + # Dictionary format: packages: {name: value} + for name, value in packages_value.items(): + # Only include common bus packages, ignore component-specific ones + if name not in common_bus_packages: + continue + packages[name] = str(value) + # Also track package dependencies (e.g., modbus includes uart) + if name not in PACKAGE_DEPENDENCIES: + continue + for dep in PACKAGE_DEPENDENCIES[name]: + if dep not in common_bus_packages: + continue + # Mark as included via dependency + packages[f"_dep_{dep}"] = f"(included via {name})" + + return packages + + +def prefix_substitutions_in_dict( + data: Any, prefix: str, exclude: set[str] | None = None +) -> Any: + """Recursively prefix all substitution references in a data structure. + + Args: + data: YAML data structure (dict, list, or scalar) + prefix: Prefix to add to substitution names + exclude: Set of substitution names to exclude from prefixing + + Returns: + Data structure with prefixed substitution references + """ + if exclude is None: + exclude = set() + + def replace_sub(text: str) -> str: + """Replace substitution references in a string.""" + + def replace_match(match): + sub_name = match.group(1) + if sub_name in exclude: + return match.group(0) + # Always use braced format in output for consistency + return f"${{{prefix}_{sub_name}}}" + + # Match both ${substitution} and $substitution formats + return re.sub(r"\$\{?(\w+)\}?", replace_match, text) + + if isinstance(data, dict): + result = {} + for key, value in data.items(): + result[key] = prefix_substitutions_in_dict(value, prefix, exclude) + return result + if isinstance(data, list): + return [prefix_substitutions_in_dict(item, prefix, exclude) for item in data] + if isinstance(data, str): + return replace_sub(data) + return data + + +def deduplicate_by_id(data: dict) -> dict: + """Deduplicate list items with the same ID. + + Keeps only the first occurrence of each ID. If items with the same ID + are identical, this silently deduplicates. If they differ, the first + one is kept (ESPHome's validation will catch if this causes issues). + + Args: + data: Parsed config dictionary + + Returns: + Config with deduplicated lists + """ + if not isinstance(data, dict): + return data + + result = {} + for key, value in data.items(): + if isinstance(value, list): + # Check for items with 'id' field + seen_ids = set() + deduped_list = [] + + for item in value: + if isinstance(item, dict) and "id" in item: + item_id = item["id"] + if item_id not in seen_ids: + seen_ids.add(item_id) + deduped_list.append(item) + # else: skip duplicate ID (keep first occurrence) + else: + # No ID, just add it + deduped_list.append(item) + + result[key] = deduped_list + elif isinstance(value, dict): + # Recursively deduplicate nested dicts + result[key] = deduplicate_by_id(value) + else: + result[key] = value + + return result + + +def merge_component_configs( + component_names: list[str], + platform: str, + tests_dir: Path, + output_file: Path, +) -> None: + """Merge multiple component test configs into a single file. + + Args: + component_names: List of component names to merge + platform: Platform to merge for (e.g., "esp32-ard") + tests_dir: Path to tests/components directory + output_file: Path to output merged config file + """ + if not component_names: + raise ValueError("No components specified") + + # Track packages to ensure they're identical + all_packages = None + + # Start with empty config + merged_config_data = {} + + # Process each component + for comp_name in component_names: + comp_dir = tests_dir / comp_name + test_file = comp_dir / f"test.{platform}.yaml" + + if not test_file.exists(): + raise FileNotFoundError(f"Test file not found: {test_file}") + + # Load the component's test file + comp_data = load_yaml_file(test_file) + + # Validate packages are compatible + # Components with no packages (no_buses) can merge with any group + comp_packages = extract_packages_from_yaml(comp_data) + + if all_packages is None: + # First component - set the baseline + all_packages = comp_packages + elif not comp_packages: + # This component has no packages (no_buses) - it can merge with any group + pass + elif not all_packages: + # Previous components had no packages, but this one does - adopt these packages + all_packages = comp_packages + elif comp_packages != all_packages: + # Both have packages but they differ - this is an error + raise ValueError( + f"Component {comp_name} has different packages than previous components. " + f"Expected: {all_packages}, Got: {comp_packages}. " + f"All components must use the same common bus configs to be merged." + ) + + # Handle $component_dir by replacing with absolute path + # This allows components that use local file references to be grouped + comp_abs_dir = str(comp_dir.absolute()) + + # Save top-level substitutions BEFORE expanding packages + # In ESPHome, top-level substitutions override package substitutions + top_level_subs = ( + comp_data["substitutions"].copy() + if "substitutions" in comp_data and comp_data["substitutions"] is not None + else {} + ) + + # Expand packages - but we'll restore substitution priority after + if "packages" in comp_data: + packages_value = comp_data["packages"] + + if isinstance(packages_value, dict): + # Dict format - check each package + common_bus_packages = get_common_bus_packages() + for pkg_name, pkg_value in list(packages_value.items()): + if pkg_name in common_bus_packages: + continue + if not isinstance(pkg_value, dict): + continue + # Component-specific package - expand its content into top level + comp_data = merge_config(comp_data, pkg_value) + elif isinstance(packages_value, list): + # List format - expand all package includes + for pkg_value in packages_value: + if not isinstance(pkg_value, dict): + continue + comp_data = merge_config(comp_data, pkg_value) + + # Remove all packages (common will be re-added at the end) + del comp_data["packages"] + + # Restore top-level substitution priority + # Top-level substitutions override any from packages + if "substitutions" not in comp_data or comp_data["substitutions"] is None: + comp_data["substitutions"] = {} + + # Merge: package subs as base, top-level subs override + comp_data["substitutions"].update(top_level_subs) + + # Now prefix the final merged substitutions + comp_data["substitutions"] = { + f"{comp_name}_{sub_name}": sub_value + for sub_name, sub_value in comp_data["substitutions"].items() + } + + # Add component_dir substitution with absolute path for this component + comp_data["substitutions"][f"{comp_name}_component_dir"] = comp_abs_dir + + # Prefix substitution references throughout the config + comp_data = prefix_substitutions_in_dict(comp_data, comp_name) + + # Use ESPHome's merge_config to merge this component into the result + # merge_config handles list merging with ID-based deduplication automatically + merged_config_data = merge_config(merged_config_data, comp_data) + + # Add packages back (only once, since they're identical) + # IMPORTANT: Only re-add common bus packages (spi, i2c, uart, etc.) + # Do NOT re-add component-specific packages as they contain unprefixed $component_dir refs + if all_packages: + first_comp_data = load_yaml_file( + tests_dir / component_names[0] / f"test.{platform}.yaml" + ) + if "packages" in first_comp_data and isinstance( + first_comp_data["packages"], dict + ): + # Filter to only include common bus packages + # Only dict format can contain common bus packages + common_bus_packages = get_common_bus_packages() + filtered_packages = { + name: value + for name, value in first_comp_data["packages"].items() + if name in common_bus_packages + } + if filtered_packages: + merged_config_data["packages"] = filtered_packages + + # Deduplicate items with same ID (keeps first occurrence) + merged_config_data = deduplicate_by_id(merged_config_data) + + # Remove esphome section since it will be provided by the wrapper file + # The wrapper file includes this merged config via packages and provides + # the proper esphome: section with name, platform, etc. + if "esphome" in merged_config_data: + del merged_config_data["esphome"] + + # Write merged config + output_file.parent.mkdir(parents=True, exist_ok=True) + yaml_content = yaml_util.dump(merged_config_data) + output_file.write_text(yaml_content) + + print(f"Successfully merged {len(component_names)} components into {output_file}") + + +def main() -> None: + """Main entry point.""" + parser = argparse.ArgumentParser( + description="Merge multiple component test configs into a single file" + ) + parser.add_argument( + "--components", + "-c", + required=True, + help="Comma-separated list of component names to merge", + ) + parser.add_argument( + "--platform", + "-p", + required=True, + help="Platform to merge for (e.g., esp32-ard)", + ) + parser.add_argument( + "--output", + "-o", + required=True, + type=Path, + help="Output file path for merged config", + ) + parser.add_argument( + "--tests-dir", + type=Path, + default=Path("tests/components"), + help="Path to tests/components directory", + ) + + args = parser.parse_args() + + component_names = [c.strip() for c in args.components.split(",")] + + try: + merge_component_configs( + component_names=component_names, + platform=args.platform, + tests_dir=args.tests_dir, + output_file=args.output, + ) + except Exception as e: + print(f"Error merging configs: {e}", file=sys.stderr) + import traceback + + traceback.print_exc() + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/script/split_components_for_ci.py b/script/split_components_for_ci.py new file mode 100755 index 0000000000..ad2261499c --- /dev/null +++ b/script/split_components_for_ci.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +"""Split components into batches with intelligent grouping. + +This script analyzes components to identify which ones share common bus configurations +and intelligently groups them into batches to maximize the efficiency of the +component grouping system in CI. + +Components with the same bus signature are placed in the same batch whenever possible, +allowing the test_build_components.py script to merge them into single builds. +""" + +from __future__ import annotations + +import argparse +from collections import defaultdict +import json +from pathlib import Path +import sys + +# Add esphome to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from script.analyze_component_buses import ( + ISOLATED_COMPONENTS, + NO_BUSES_SIGNATURE, + analyze_all_components, + create_grouping_signature, +) + +# Weighting for batch creation +# Isolated components can't be grouped/merged, so they count as 10x +# Groupable components can be merged into single builds, so they count as 1x +ISOLATED_WEIGHT = 10 +GROUPABLE_WEIGHT = 1 + + +def has_test_files(component_name: str, tests_dir: Path) -> bool: + """Check if a component has test files. + + Args: + component_name: Name of the component + tests_dir: Path to tests/components directory + + Returns: + True if the component has test.*.yaml files + """ + component_dir = tests_dir / component_name + if not component_dir.exists() or not component_dir.is_dir(): + return False + + # Check for test.*.yaml files + return any(component_dir.glob("test.*.yaml")) + + +def create_intelligent_batches( + components: list[str], + tests_dir: Path, + batch_size: int = 40, +) -> list[list[str]]: + """Create batches optimized for component grouping. + + Args: + components: List of component names to batch + tests_dir: Path to tests/components directory + batch_size: Target size for each batch + + Returns: + List of component batches (lists of component names) + """ + # Filter out components without test files + # Platform components like 'climate' and 'climate_ir' don't have test files + components_with_tests = [ + comp for comp in components if has_test_files(comp, tests_dir) + ] + + # Log filtered components to stderr for debugging + if len(components_with_tests) < len(components): + filtered_out = set(components) - set(components_with_tests) + print( + f"Note: Filtered {len(filtered_out)} components without test files: " + f"{', '.join(sorted(filtered_out))}", + file=sys.stderr, + ) + + # Analyze all components to get their bus signatures + component_buses, non_groupable, _direct_bus_components = analyze_all_components( + tests_dir + ) + + # Group components by their bus signature ONLY (ignore platform) + # All platforms will be tested by test_build_components.py for each batch + # Key: signature, Value: list of components + signature_groups: dict[str, list[str]] = defaultdict(list) + + for component in components_with_tests: + # Components that can't be grouped get unique signatures + # This includes both manually curated ISOLATED_COMPONENTS and + # automatically detected non_groupable components + # These can share a batch/runner but won't be grouped/merged + if component in ISOLATED_COMPONENTS or component in non_groupable: + signature_groups[f"isolated_{component}"].append(component) + continue + + # Get signature from any platform (they should all have the same buses) + # Components not in component_buses were filtered out by has_test_files check + comp_platforms = component_buses[component] + for platform, buses in comp_platforms.items(): + if buses: + signature = create_grouping_signature({platform: buses}, platform) + # Group by signature only - platform doesn't matter for batching + signature_groups[signature].append(component) + break # Only use first platform for grouping + else: + # No buses found for any platform - can be grouped together + signature_groups[NO_BUSES_SIGNATURE].append(component) + + # Create batches by keeping signature groups together + # Components with the same signature stay in the same batches + batches = [] + + # Sort signature groups to prioritize groupable components + # 1. Put "isolated_*" signatures last (can't be grouped with others) + # 2. Sort groupable signatures by size (largest first) + # 3. "no_buses" components CAN be grouped together + def sort_key(item): + signature, components = item + is_isolated = signature.startswith("isolated_") + # Put "isolated_*" last (1), groupable first (0) + # Within each category, sort by size (largest first) + return (is_isolated, -len(components)) + + sorted_groups = sorted(signature_groups.items(), key=sort_key) + + # Strategy: Create batches using weighted sizes + # - Isolated components count as 10x (since they can't be grouped/merged) + # - Groupable components count as 1x (can be merged into single builds) + # - This distributes isolated components across more runners + # - Ensures each runner has a good mix of groupable vs isolated components + + current_batch = [] + current_weight = 0 + + for signature, group_components in sorted_groups: + is_isolated = signature.startswith("isolated_") + weight_per_component = ISOLATED_WEIGHT if is_isolated else GROUPABLE_WEIGHT + + for component in group_components: + # Check if adding this component would exceed the batch size + if current_weight + weight_per_component > batch_size and current_batch: + # Start a new batch + batches.append(current_batch) + current_batch = [] + current_weight = 0 + + # Add component to current batch + current_batch.append(component) + current_weight += weight_per_component + + # Don't forget the last batch + if current_batch: + batches.append(current_batch) + + return batches + + +def main() -> int: + """Main entry point.""" + parser = argparse.ArgumentParser( + description="Split components into intelligent batches for CI testing" + ) + parser.add_argument( + "--components", + "-c", + required=True, + help="JSON array of component names", + ) + parser.add_argument( + "--batch-size", + "-b", + type=int, + default=40, + help="Target batch size (default: 40, weighted)", + ) + parser.add_argument( + "--tests-dir", + type=Path, + default=Path("tests/components"), + help="Path to tests/components directory", + ) + parser.add_argument( + "--output", + "-o", + choices=["json", "github"], + default="github", + help="Output format (json or github for GitHub Actions)", + ) + + args = parser.parse_args() + + # Parse component list from JSON + try: + components = json.loads(args.components) + except json.JSONDecodeError as e: + print(f"Error parsing components JSON: {e}", file=sys.stderr) + return 1 + + if not isinstance(components, list): + print("Components must be a JSON array", file=sys.stderr) + return 1 + + # Create intelligent batches + batches = create_intelligent_batches( + components=components, + tests_dir=args.tests_dir, + batch_size=args.batch_size, + ) + + # Convert batches to space-separated strings for CI + batch_strings = [" ".join(batch) for batch in batches] + + if args.output == "json": + # Output as JSON array + print(json.dumps(batch_strings)) + else: + # Output for GitHub Actions (set output) + output_json = json.dumps(batch_strings) + print(f"components={output_json}") + + # Print summary to stderr so it shows in CI logs + # Count actual components being batched + actual_components = sum(len(batch.split()) for batch in batch_strings) + + # Re-analyze to get isolated component counts for summary + _, non_groupable, _ = analyze_all_components(args.tests_dir) + + # Count isolated vs groupable components + all_batched_components = [comp for batch in batches for comp in batch] + isolated_count = sum( + 1 + for comp in all_batched_components + if comp in ISOLATED_COMPONENTS or comp in non_groupable + ) + groupable_count = actual_components - isolated_count + + print("\n=== Intelligent Batch Summary ===", file=sys.stderr) + print(f"Total components requested: {len(components)}", file=sys.stderr) + print(f"Components with test files: {actual_components}", file=sys.stderr) + print(f" - Groupable (weight=1): {groupable_count}", file=sys.stderr) + print(f" - Isolated (weight=10): {isolated_count}", file=sys.stderr) + if actual_components < len(components): + print( + f"Components skipped (no test files): {len(components) - actual_components}", + file=sys.stderr, + ) + print(f"Number of batches: {len(batches)}", file=sys.stderr) + print(f"Batch size target (weighted): {args.batch_size}", file=sys.stderr) + if len(batches) > 0: + print( + f"Average components per batch: {actual_components / len(batches):.1f}", + file=sys.stderr, + ) + print(file=sys.stderr) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/script/test_build_components b/script/test_build_components deleted file mode 100755 index 3796280176..0000000000 --- a/script/test_build_components +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env bash - -set -e - -help() { - echo "Usage: $0 [-e ] [-c ] [-t ]" 1>&2 - echo 1>&2 - echo " - e - Parameter for esphome command. Default compile. Common alternative is config." 1>&2 - echo " - c - Component folder name to test. Default *. E.g. '-c logger'." 1>&2 - echo " - t - Target name to test. Put '-t list' to display all possibilities. E.g. '-t esp32-s2-idf-51'." 1>&2 - exit 1 -} - -# Parse parameter: -# - `e` - Parameter for `esphome` command. Default `compile`. Common alternative is `config`. -# - `c` - Component folder name to test. Default `*`. -esphome_command="compile" -target_component="*" -while getopts e:c:t: flag -do - case $flag in - e) esphome_command=${OPTARG};; - c) target_component=${OPTARG};; - t) requested_target_platform=${OPTARG};; - \?) help;; - esac -done - -cd "$(dirname "$0")/.." - -if ! [ -d "./tests/test_build_components/build" ]; then - mkdir ./tests/test_build_components/build -fi - -start_esphome() { - if [ -n "$requested_target_platform" ] && [ "$requested_target_platform" != "$target_platform_with_version" ]; then - echo "Skipping $target_platform_with_version" - return - fi - # create dynamic yaml file in `build` folder. - # `./tests/test_build_components/build/[target_component].[test_name].[target_platform_with_version].yaml` - component_test_file="./tests/test_build_components/build/$target_component.$test_name.$target_platform_with_version.yaml" - - cp $target_platform_file $component_test_file - if [[ "$OSTYPE" == "darwin"* ]]; then - # macOS sed is...different - sed -i '' "s!\$component_test_file!../../.$f!g" $component_test_file - else - sed -i "s!\$component_test_file!../../.$f!g" $component_test_file - fi - - # Start esphome process - echo "> [$target_component] [$test_name] [$target_platform_with_version]" - set -x - # TODO: Validate escape of Command line substitution value - python3 -m esphome -s component_name $target_component -s component_dir ../../components/$target_component -s test_name $test_name -s target_platform $target_platform $esphome_command $component_test_file - { set +x; } 2>/dev/null -} - -# Find all test yaml files. -# - `./tests/components/[target_component]/[test_name].[target_platform].yaml` -# - `./tests/components/[target_component]/[test_name].all.yaml` -for f in ./tests/components/$target_component/*.*.yaml; do - [ -f "$f" ] || continue - IFS='/' read -r -a folder_name <<< "$f" - target_component="${folder_name[3]}" - - IFS='.' read -r -a file_name <<< "${folder_name[4]}" - test_name="${file_name[0]}" - target_platform="${file_name[1]}" - file_name_parts=${#file_name[@]} - - if [ "$target_platform" = "all" ] || [ $file_name_parts = 2 ]; then - # Test has *not* defined a specific target platform. Need to run tests for all possible target platforms. - - for target_platform_file in ./tests/test_build_components/build_components_base.*.yaml; do - IFS='/' read -r -a folder_name <<< "$target_platform_file" - IFS='.' read -r -a file_name <<< "${folder_name[3]}" - target_platform="${file_name[1]}" - - start_esphome - done - - else - # Test has defined a specific target platform. - - # Validate we have a base test yaml for selected platform. - # The target_platform is sourced from the following location. - # 1. `./tests/test_build_components/build_components_base.[target_platform].yaml` - # 2. `./tests/test_build_components/build_components_base.[target_platform]-ard.yaml` - target_platform_file="./tests/test_build_components/build_components_base.$target_platform.yaml" - if ! [ -f "$target_platform_file" ]; then - echo "No base test file [./tests/test_build_components/build_components_base.$target_platform.yaml] for component test [$f] found." - exit 1 - fi - - for target_platform_file in ./tests/test_build_components/build_components_base.$target_platform*.yaml; do - # trim off "./tests/test_build_components/build_components_base." prefix - target_platform_with_version=${target_platform_file:52} - # ...now remove suffix starting with "." leaving just the test target hardware and software platform (possibly with version) - # For example: "esp32-s3-idf-50" - target_platform_with_version=${target_platform_with_version%.*} - start_esphome - done - fi -done diff --git a/script/test_build_components b/script/test_build_components new file mode 120000 index 0000000000..832a4a72c6 --- /dev/null +++ b/script/test_build_components @@ -0,0 +1 @@ +test_build_components.py \ No newline at end of file diff --git a/script/test_build_components.py b/script/test_build_components.py new file mode 100755 index 0000000000..e27bd695c1 --- /dev/null +++ b/script/test_build_components.py @@ -0,0 +1,931 @@ +#!/usr/bin/env python3 +"""Test ESPHome component builds with intelligent grouping. + +This script replaces the bash test_build_components script with Python, +adding support for intelligent component grouping based on shared bus +configurations to reduce CI build time. + +Features: +- Analyzes components for shared common bus configs +- Groups compatible components together +- Merges configs for grouped components +- Uses --testing-mode for grouped tests +- Maintains backward compatibility with single component testing +""" + +from __future__ import annotations + +import argparse +from collections import defaultdict +import hashlib +import os +from pathlib import Path +import subprocess +import sys + +# Add esphome to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +# pylint: disable=wrong-import-position +from script.analyze_component_buses import ( + BASE_BUS_COMPONENTS, + ISOLATED_COMPONENTS, + NO_BUSES_SIGNATURE, + analyze_all_components, + create_grouping_signature, + is_platform_component, + uses_local_file_references, +) +from script.merge_component_configs import merge_component_configs + +# Platform-specific maximum group sizes +# ESP8266 has limited IRAM and can't handle large component groups +PLATFORM_MAX_GROUP_SIZE = { + "esp8266-ard": 10, # ESP8266 Arduino has limited IRAM + "esp8266-idf": 10, # ESP8266 IDF also has limited IRAM + # BK72xx now uses BK7252 board (1.62MB flash vs 1.03MB) - no limit needed + # Other platforms can handle larger groups +} + + +def show_disk_space_if_ci(esphome_command: str) -> None: + """Show disk space usage if running in CI during compile. + + Args: + esphome_command: The esphome command being run (config/compile/clean) + """ + if os.environ.get("GITHUB_ACTIONS") and esphome_command == "compile": + print("\n" + "=" * 80) + print("Disk Space After Build:") + print("=" * 80) + subprocess.run(["df", "-h"], check=False) + print("=" * 80 + "\n") + + +def find_component_tests( + components_dir: Path, component_pattern: str = "*" +) -> dict[str, list[Path]]: + """Find all component test files. + + Args: + components_dir: Path to tests/components directory + component_pattern: Glob pattern for component names + + Returns: + Dictionary mapping component name to list of test files + """ + component_tests = defaultdict(list) + + for comp_dir in components_dir.glob(component_pattern): + if not comp_dir.is_dir(): + continue + + for test_file in comp_dir.glob("test.*.yaml"): + component_tests[comp_dir.name].append(test_file) + + return dict(component_tests) + + +def parse_test_filename(test_file: Path) -> tuple[str, str]: + """Parse test filename to extract test name and platform. + + Args: + test_file: Path to test file + + Returns: + Tuple of (test_name, platform) + """ + parts = test_file.stem.split(".") + if len(parts) == 2: + return parts[0], parts[1] # test, platform + return parts[0], "all" + + +def get_platform_base_files(base_dir: Path) -> dict[str, list[Path]]: + """Get all platform base files. + + Args: + base_dir: Path to test_build_components directory + + Returns: + Dictionary mapping platform to list of base files (for version variants) + """ + platform_files = defaultdict(list) + + for base_file in base_dir.glob("build_components_base.*.yaml"): + # Extract platform from filename + # e.g., build_components_base.esp32-idf.yaml -> esp32-idf + # or build_components_base.esp32-idf-50.yaml -> esp32-idf + filename = base_file.stem + parts = filename.replace("build_components_base.", "").split("-") + + # Platform is everything before version number (if present) + # Check if last part is a number (version) + platform = "-".join(parts[:-1]) if parts[-1].isdigit() else "-".join(parts) + + platform_files[platform].append(base_file) + + return dict(platform_files) + + +def extract_platform_with_version(base_file: Path) -> str: + """Extract platform with version from base filename. + + Args: + base_file: Path to base file + + Returns: + Platform with version (e.g., "esp32-idf-50" or "esp32-idf") + """ + # Remove "build_components_base." prefix and ".yaml" suffix + return base_file.stem.replace("build_components_base.", "") + + +def run_esphome_test( + component: str, + test_file: Path, + platform: str, + platform_with_version: str, + base_file: Path, + build_dir: Path, + esphome_command: str, + continue_on_fail: bool, + use_testing_mode: bool = False, +) -> tuple[bool, str]: + """Run esphome test for a single component. + + Args: + component: Component name + test_file: Path to component test file + platform: Platform name (e.g., "esp32-idf") + platform_with_version: Platform with version (e.g., "esp32-idf-50") + base_file: Path to platform base file + build_dir: Path to build directory + esphome_command: ESPHome command (config/compile) + continue_on_fail: Whether to continue on failure + use_testing_mode: Whether to use --testing-mode flag + + Returns: + Tuple of (success status, command string) + """ + test_name = test_file.stem.split(".")[0] + + # Create dynamic test file in build directory + output_file = build_dir / f"{component}.{test_name}.{platform_with_version}.yaml" + + # Copy base file and substitute component test file reference + base_content = base_file.read_text() + # Get relative path from build dir to test file + repo_root = Path(__file__).parent.parent + component_test_ref = f"../../{test_file.relative_to(repo_root / 'tests')}" + output_content = base_content.replace("$component_test_file", component_test_ref) + output_file.write_text(output_content) + + # Build esphome command + cmd = [ + sys.executable, + "-m", + "esphome", + ] + + # Add --testing-mode if needed (must be before subcommand) + if use_testing_mode: + cmd.append("--testing-mode") + + # Add substitutions + cmd.extend( + [ + "-s", + "component_name", + component, + "-s", + "component_dir", + f"../../components/{component}", + "-s", + "test_name", + test_name, + "-s", + "target_platform", + platform, + ] + ) + + # Add command and config file + cmd.extend([esphome_command, str(output_file)]) + + # Build command string for display/logging + cmd_str = " ".join(cmd) + + # Run command + print(f"> [{component}] [{test_name}] [{platform_with_version}]") + if use_testing_mode: + print(" (using --testing-mode)") + + try: + result = subprocess.run(cmd, check=False) + success = result.returncode == 0 + + # Show disk space after build in CI during compile + show_disk_space_if_ci(esphome_command) + + if not success and not continue_on_fail: + # Print command immediately for failed tests + print(f"\n{'=' * 80}") + print("FAILED - Command to reproduce:") + print(f"{'=' * 80}") + print(cmd_str) + print() + raise subprocess.CalledProcessError(result.returncode, cmd) + return success, cmd_str + except subprocess.CalledProcessError: + # Re-raise if we're not continuing on fail + if not continue_on_fail: + raise + return False, cmd_str + + +def run_grouped_test( + components: list[str], + platform: str, + platform_with_version: str, + base_file: Path, + build_dir: Path, + tests_dir: Path, + esphome_command: str, + continue_on_fail: bool, +) -> tuple[bool, str]: + """Run esphome test for a group of components with shared bus configs. + + Args: + components: List of component names to test together + platform: Platform name (e.g., "esp32-idf") + platform_with_version: Platform with version (e.g., "esp32-idf-50") + base_file: Path to platform base file + build_dir: Path to build directory + tests_dir: Path to tests/components directory + esphome_command: ESPHome command (config/compile) + continue_on_fail: Whether to continue on failure + + Returns: + Tuple of (success status, command string) + """ + # Create merged config + group_name = "_".join(components[:3]) # Use first 3 components for name + if len(components) > 3: + group_name += f"_plus_{len(components) - 3}" + + # Create unique device name by hashing sorted component list + platform + # This prevents conflicts when different component groups are tested + sorted_components = sorted(components) + hash_input = "_".join(sorted_components) + "_" + platform + group_hash = hashlib.md5(hash_input.encode()).hexdigest()[:8] + device_name = f"comptest{platform.replace('-', '')}{group_hash}" + + merged_config_file = build_dir / f"merged_{group_name}.{platform_with_version}.yaml" + + try: + merge_component_configs( + component_names=components, + platform=platform_with_version, + tests_dir=tests_dir, + output_file=merged_config_file, + ) + except Exception as e: # pylint: disable=broad-exception-caught + print(f"Error merging configs for {components}: {e}") + if not continue_on_fail: + raise + # Return empty command string since we failed before building the command + return False, f"# Failed during config merge: {e}" + + # Create test file that includes merged config + output_file = build_dir / f"test_{group_name}.{platform_with_version}.yaml" + base_content = base_file.read_text() + merged_ref = merged_config_file.name + output_content = base_content.replace("$component_test_file", merged_ref) + output_file.write_text(output_content) + + # Build esphome command with --testing-mode + cmd = [ + sys.executable, + "-m", + "esphome", + "--testing-mode", # Required for grouped tests + "-s", + "component_name", + device_name, # Use unique hash-based device name + "-s", + "component_dir", + "../../components", + "-s", + "test_name", + "merged", + "-s", + "target_platform", + platform, + esphome_command, + str(output_file), + ] + + # Build command string for display/logging + cmd_str = " ".join(cmd) + + # Run command + components_str = ", ".join(components) + print(f"> [GROUPED: {components_str}] [{platform_with_version}]") + print(" (using --testing-mode)") + + try: + result = subprocess.run(cmd, check=False) + success = result.returncode == 0 + + # Show disk space after build in CI during compile + show_disk_space_if_ci(esphome_command) + + if not success and not continue_on_fail: + # Print command immediately for failed tests + print(f"\n{'=' * 80}") + print("FAILED - Command to reproduce:") + print(f"{'=' * 80}") + print(cmd_str) + print() + raise subprocess.CalledProcessError(result.returncode, cmd) + return success, cmd_str + except subprocess.CalledProcessError: + # Re-raise if we're not continuing on fail + if not continue_on_fail: + raise + return False, cmd_str + + +def run_grouped_component_tests( + all_tests: dict[str, list[Path]], + platform_filter: str | None, + platform_bases: dict[str, list[Path]], + tests_dir: Path, + build_dir: Path, + esphome_command: str, + continue_on_fail: bool, +) -> tuple[set[tuple[str, str]], list[str], list[str], dict[str, str]]: + """Run grouped component tests. + + Args: + all_tests: Dictionary mapping component names to test files + platform_filter: Optional platform to filter by + platform_bases: Platform base files mapping + tests_dir: Path to tests/components directory + build_dir: Path to build directory + esphome_command: ESPHome command (config/compile) + continue_on_fail: Whether to continue on failure + + Returns: + Tuple of (tested_components, passed_tests, failed_tests, failed_commands) + """ + tested_components = set() + passed_tests = [] + failed_tests = [] + failed_commands = {} # Map test_id to command string + + # Group components by platform and bus signature + grouped_components: dict[tuple[str, str], list[str]] = defaultdict(list) + print("\n" + "=" * 80) + print("Analyzing components for intelligent grouping...") + print("=" * 80) + component_buses, non_groupable, direct_bus_components = analyze_all_components( + tests_dir + ) + + # Track why components can't be grouped (for detailed output) + non_groupable_reasons = {} + + # Group by (platform, bus_signature) + for component, platforms in component_buses.items(): + if component not in all_tests: + continue + + # Skip components that must be tested in isolation + # These are shown separately and should not be in non_groupable_reasons + if component in ISOLATED_COMPONENTS: + continue + + # Skip base bus components (these test the bus platforms themselves) + if component in BASE_BUS_COMPONENTS: + continue + + # Skip components that use local file references or direct bus configs + if component in non_groupable: + # Track the reason (using pre-calculated results to avoid expensive re-analysis) + if component not in non_groupable_reasons: + if component in direct_bus_components: + non_groupable_reasons[component] = ( + "Defines buses directly (not via packages) - NEEDS MIGRATION" + ) + elif uses_local_file_references(tests_dir / component): + non_groupable_reasons[component] = ( + "Uses local file references ($component_dir)" + ) + elif is_platform_component(tests_dir / component): + non_groupable_reasons[component] = ( + "Platform component (abstract base class)" + ) + else: + non_groupable_reasons[component] = ( + "Uses !extend or !remove directives" + ) + continue + + for platform, buses in platforms.items(): + # Skip if platform doesn't match filter + if platform_filter and not platform.startswith(platform_filter): + continue + + # Create signature for this component's bus configuration + # Components with no buses get NO_BUSES_SIGNATURE so they can be grouped together + if buses: + signature = create_grouping_signature({platform: buses}, platform) + else: + signature = NO_BUSES_SIGNATURE + + # Add to grouped components (including those with no buses) + if signature: + grouped_components[(platform, signature)].append(component) + + # Print detailed grouping plan + print("\nGrouping Plan:") + print("-" * 80) + + # Show isolated components (must test individually due to known issues) + isolated_in_tests = [c for c in ISOLATED_COMPONENTS if c in all_tests] + if isolated_in_tests: + print( + f"\n⚠ {len(isolated_in_tests)} components must be tested in isolation (known build issues):" + ) + for comp in sorted(isolated_in_tests): + reason = ISOLATED_COMPONENTS[comp] + print(f" - {comp}: {reason}") + + # Show base bus components (test the bus platform implementations) + base_bus_in_tests = [c for c in BASE_BUS_COMPONENTS if c in all_tests] + if base_bus_in_tests: + print( + f"\n○ {len(base_bus_in_tests)} base bus platform components (tested individually):" + ) + for comp in sorted(base_bus_in_tests): + print(f" - {comp}") + + # Show excluded components with detailed reasons + if non_groupable_reasons: + excluded_in_tests = [c for c in non_groupable_reasons if c in all_tests] + if excluded_in_tests: + print( + f"\n⚠ {len(excluded_in_tests)} components excluded from grouping (each needs individual build):" + ) + # Group by reason to show summary + direct_bus = [ + c + for c in excluded_in_tests + if "NEEDS MIGRATION" in non_groupable_reasons.get(c, "") + ] + if direct_bus: + print( + f"\n ⚠⚠⚠ {len(direct_bus)} DEFINE BUSES DIRECTLY - NEED MIGRATION TO PACKAGES:" + ) + for comp in sorted(direct_bus): + print(f" - {comp}") + + other_reasons = [ + c + for c in excluded_in_tests + if "NEEDS MIGRATION" not in non_groupable_reasons.get(c, "") + ] + if other_reasons and len(other_reasons) <= 10: + print("\n Other non-groupable components:") + for comp in sorted(other_reasons): + reason = non_groupable_reasons[comp] + print(f" - {comp}: {reason}") + elif other_reasons: + print( + f"\n Other non-groupable components: {len(other_reasons)} components" + ) + + # Distribute no_buses components into other groups to maximize efficiency + # Components with no buses can merge with any bus group since they have no conflicting requirements + no_buses_by_platform: dict[str, list[str]] = {} + for (platform, signature), components in list(grouped_components.items()): + if signature == NO_BUSES_SIGNATURE: + no_buses_by_platform[platform] = components + # Remove from grouped_components - we'll distribute them + del grouped_components[(platform, signature)] + + # Distribute no_buses components into existing groups for each platform + for platform, no_buses_comps in no_buses_by_platform.items(): + # Find all non-empty groups for this platform (excluding no_buses) + platform_groups = [ + (sig, comps) + for (plat, sig), comps in grouped_components.items() + if plat == platform and sig != NO_BUSES_SIGNATURE + ] + + if platform_groups: + # Distribute no_buses components round-robin across existing groups + for i, comp in enumerate(no_buses_comps): + sig, _ = platform_groups[i % len(platform_groups)] + grouped_components[(platform, sig)].append(comp) + else: + # No other groups for this platform - keep no_buses components together + grouped_components[(platform, NO_BUSES_SIGNATURE)] = no_buses_comps + + # Split groups that exceed platform-specific maximum sizes + # ESP8266 has limited IRAM and can't handle large component groups + split_groups = {} + for (platform, signature), components in list(grouped_components.items()): + max_size = PLATFORM_MAX_GROUP_SIZE.get(platform) + if max_size and len(components) > max_size: + # Split this group into smaller groups + print( + f"\n ℹ️ Splitting {platform} group (signature: {signature}) " + f"from {len(components)} to max {max_size} components per group" + ) + # Remove original group + del grouped_components[(platform, signature)] + # Create split groups + for i in range(0, len(components), max_size): + split_components = components[i : i + max_size] + # Create unique signature for each split group + split_signature = f"{signature}_split{i // max_size + 1}" + split_groups[(platform, split_signature)] = split_components + # Add split groups back + grouped_components.update(split_groups) + + groups_to_test = [] + individual_tests = set() # Use set to avoid duplicates + + for (platform, signature), components in sorted(grouped_components.items()): + if len(components) > 1: + groups_to_test.append((platform, signature, components)) + # Note: Don't add single-component groups to individual_tests here + # They'll be added below when we check for ungrouped components + + # Add components that weren't grouped on any platform + for component in all_tests: + if component not in [c for _, _, comps in groups_to_test for c in comps]: + individual_tests.add(component) + + if groups_to_test: + print(f"\n✓ {len(groups_to_test)} groups will be tested together:") + for platform, signature, components in groups_to_test: + component_list = ", ".join(sorted(components)) + print(f" [{platform}] [{signature}]: {component_list}") + print( + f" → {len(components)} components in 1 build (saves {len(components) - 1} builds)" + ) + + if individual_tests: + print(f"\n○ {len(individual_tests)} components will be tested individually:") + sorted_individual = sorted(individual_tests) + for comp in sorted_individual[:10]: + print(f" - {comp}") + if len(individual_tests) > 10: + print(f" ... and {len(individual_tests) - 10} more") + + # Calculate actual build counts based on test files, not component counts + # Without grouping: every test file would be built separately + total_test_files = sum(len(test_files) for test_files in all_tests.values()) + + # With grouping: + # - 1 build per group (regardless of how many components) + # - Individual components still need all their platform builds + individual_test_file_count = sum( + len(all_tests[comp]) for comp in individual_tests if comp in all_tests + ) + + total_grouped_components = sum(len(comps) for _, _, comps in groups_to_test) + total_builds_with_grouping = len(groups_to_test) + individual_test_file_count + builds_saved = total_test_files - total_builds_with_grouping + + print(f"\n{'=' * 80}") + print( + f"Summary: {total_builds_with_grouping} builds total (vs {total_test_files} without grouping)" + ) + print( + f" • {len(groups_to_test)} grouped builds ({total_grouped_components} components)" + ) + print( + f" • {individual_test_file_count} individual builds ({len(individual_tests)} components)" + ) + if total_test_files > 0: + reduction_pct = (builds_saved / total_test_files) * 100 + print(f" • Saves {builds_saved} builds ({reduction_pct:.1f}% reduction)") + print("=" * 80 + "\n") + + # Execute grouped tests + for (platform, signature), components in grouped_components.items(): + # Only group if we have multiple components with same signature + if len(components) <= 1: + continue + + # Filter out components not in our test list + components_to_group = [c for c in components if c in all_tests] + if len(components_to_group) <= 1: + continue + + # Get platform base files + if platform not in platform_bases: + continue + + for base_file in platform_bases[platform]: + platform_with_version = extract_platform_with_version(base_file) + + # Skip if platform filter doesn't match + if platform_filter and platform != platform_filter: + continue + if ( + platform_filter + and platform_with_version != platform_filter + and not platform_with_version.startswith(f"{platform_filter}-") + ): + continue + + # Run grouped test + success, cmd_str = run_grouped_test( + components=components_to_group, + platform=platform, + platform_with_version=platform_with_version, + base_file=base_file, + build_dir=build_dir, + tests_dir=tests_dir, + esphome_command=esphome_command, + continue_on_fail=continue_on_fail, + ) + + # Mark all components as tested + for comp in components_to_group: + tested_components.add((comp, platform_with_version)) + + # Record result for each component - show all components in grouped tests + test_id = ( + f"GROUPED[{','.join(components_to_group)}].{platform_with_version}" + ) + if success: + passed_tests.append(test_id) + else: + failed_tests.append(test_id) + failed_commands[test_id] = cmd_str + + return tested_components, passed_tests, failed_tests, failed_commands + + +def run_individual_component_test( + component: str, + test_file: Path, + platform: str, + platform_with_version: str, + base_file: Path, + build_dir: Path, + esphome_command: str, + continue_on_fail: bool, + tested_components: set[tuple[str, str]], + passed_tests: list[str], + failed_tests: list[str], + failed_commands: dict[str, str], +) -> None: + """Run an individual component test if not already tested in a group. + + Args: + component: Component name + test_file: Test file path + platform: Platform name + platform_with_version: Platform with version + base_file: Base file for platform + build_dir: Build directory + esphome_command: ESPHome command + continue_on_fail: Whether to continue on failure + tested_components: Set of already tested components + passed_tests: List to append passed test IDs + failed_tests: List to append failed test IDs + failed_commands: Dict to store failed test commands + """ + # Skip if already tested in a group + if (component, platform_with_version) in tested_components: + return + + test_name = test_file.stem.split(".")[0] + success, cmd_str = run_esphome_test( + component=component, + test_file=test_file, + platform=platform, + platform_with_version=platform_with_version, + base_file=base_file, + build_dir=build_dir, + esphome_command=esphome_command, + continue_on_fail=continue_on_fail, + ) + test_id = f"{component}.{test_name}.{platform_with_version}" + if success: + passed_tests.append(test_id) + else: + failed_tests.append(test_id) + failed_commands[test_id] = cmd_str + + +def test_components( + component_patterns: list[str], + platform_filter: str | None, + esphome_command: str, + continue_on_fail: bool, + enable_grouping: bool = True, +) -> int: + """Test components with optional intelligent grouping. + + Args: + component_patterns: List of component name patterns + platform_filter: Optional platform to filter by + esphome_command: ESPHome command (config/compile) + continue_on_fail: Whether to continue on failure + enable_grouping: Whether to enable component grouping + + Returns: + Exit code (0 for success, 1 for failure) + """ + # Setup paths + repo_root = Path(__file__).parent.parent + tests_dir = repo_root / "tests" / "components" + build_components_dir = repo_root / "tests" / "test_build_components" + build_dir = build_components_dir / "build" + build_dir.mkdir(parents=True, exist_ok=True) + + # Get platform base files + platform_bases = get_platform_base_files(build_components_dir) + + # Find all component tests + all_tests = {} + for pattern in component_patterns: + all_tests.update(find_component_tests(tests_dir, pattern)) + + if not all_tests: + print(f"No components found matching: {component_patterns}") + return 1 + + print(f"Found {len(all_tests)} components to test") + + # Run tests + failed_tests = [] + passed_tests = [] + tested_components = set() # Track which components were tested in groups + failed_commands = {} # Track commands for failed tests + + # First, run grouped tests if grouping is enabled + if enable_grouping: + ( + tested_components, + passed_tests, + failed_tests, + failed_commands, + ) = run_grouped_component_tests( + all_tests=all_tests, + platform_filter=platform_filter, + platform_bases=platform_bases, + tests_dir=tests_dir, + build_dir=build_dir, + esphome_command=esphome_command, + continue_on_fail=continue_on_fail, + ) + + # Then run individual tests for components not in groups + for component, test_files in sorted(all_tests.items()): + for test_file in test_files: + test_name, platform = parse_test_filename(test_file) + + # Handle "all" platform tests + if platform == "all": + # Run for all platforms + for plat, base_files in platform_bases.items(): + if platform_filter and plat != platform_filter: + continue + + for base_file in base_files: + platform_with_version = extract_platform_with_version(base_file) + run_individual_component_test( + component=component, + test_file=test_file, + platform=plat, + platform_with_version=platform_with_version, + base_file=base_file, + build_dir=build_dir, + esphome_command=esphome_command, + continue_on_fail=continue_on_fail, + tested_components=tested_components, + passed_tests=passed_tests, + failed_tests=failed_tests, + failed_commands=failed_commands, + ) + else: + # Platform-specific test + if platform_filter and platform != platform_filter: + continue + + if platform not in platform_bases: + print(f"No base file for platform: {platform}") + continue + + for base_file in platform_bases[platform]: + platform_with_version = extract_platform_with_version(base_file) + + # Skip if requested platform doesn't match + if ( + platform_filter + and platform_with_version != platform_filter + and not platform_with_version.startswith(f"{platform_filter}-") + ): + continue + + run_individual_component_test( + component=component, + test_file=test_file, + platform=platform, + platform_with_version=platform_with_version, + base_file=base_file, + build_dir=build_dir, + esphome_command=esphome_command, + continue_on_fail=continue_on_fail, + tested_components=tested_components, + passed_tests=passed_tests, + failed_tests=failed_tests, + failed_commands=failed_commands, + ) + + # Print summary + print("\n" + "=" * 80) + print(f"Test Summary: {len(passed_tests)} passed, {len(failed_tests)} failed") + print("=" * 80) + + if failed_tests: + print("\nFailed tests:") + for test in failed_tests: + print(f" - {test}") + + # Print failed commands at the end for easy copy-paste from CI logs + print("\n" + "=" * 80) + print("Failed test commands (copy-paste to reproduce locally):") + print("=" * 80) + for test in failed_tests: + if test in failed_commands: + print(f"\n# {test}") + print(failed_commands[test]) + print() + + return 1 + + return 0 + + +def main() -> int: + """Main entry point.""" + parser = argparse.ArgumentParser( + description="Test ESPHome component builds with intelligent grouping" + ) + parser.add_argument( + "-e", + "--esphome-command", + default="compile", + choices=["config", "compile", "clean"], + help="ESPHome command to run (default: compile)", + ) + parser.add_argument( + "-c", + "--components", + default="*", + help="Component pattern(s) to test (default: *). Comma-separated.", + ) + parser.add_argument( + "-t", + "--target", + help="Target platform to test (e.g., esp32-idf)", + ) + parser.add_argument( + "-f", + "--continue-on-fail", + action="store_true", + help="Continue testing even if a test fails", + ) + parser.add_argument( + "--no-grouping", + action="store_true", + help="Disable component grouping (test each component individually)", + ) + + args = parser.parse_args() + + # Parse component patterns + component_patterns = [p.strip() for p in args.components.split(",")] + + return test_components( + component_patterns=component_patterns, + platform_filter=args.target, + esphome_command=args.esphome_command, + continue_on_fail=args.continue_on_fail, + enable_grouping=not args.no_grouping, + ) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/script/test_component_grouping.py b/script/test_component_grouping.py new file mode 100755 index 0000000000..a2cee6e888 --- /dev/null +++ b/script/test_component_grouping.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +"""Test component grouping by finding and testing groups of components. + +This script analyzes components, finds groups that can be tested together, +and runs test builds for those groups. +""" + +from __future__ import annotations + +import argparse +from pathlib import Path +import subprocess +import sys + +# Add esphome to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from script.analyze_component_buses import ( + analyze_all_components, + group_components_by_signature, +) + + +def test_component_group( + components: list[str], + platform: str, + esphome_command: str = "compile", + dry_run: bool = False, +) -> bool: + """Test a group of components together. + + Args: + components: List of component names to test together + platform: Platform to test on (e.g., "esp32-idf") + esphome_command: ESPHome command to run (config/compile/clean) + dry_run: If True, only print the command without running it + + Returns: + True if test passed, False otherwise + """ + components_str = ",".join(components) + cmd = [ + "./script/test_build_components", + "-c", + components_str, + "-t", + platform, + "-e", + esphome_command, + ] + + print(f"\n{'=' * 80}") + print(f"Testing {len(components)} components on {platform}:") + for comp in components: + print(f" - {comp}") + print(f"{'=' * 80}") + print(f"Command: {' '.join(cmd)}\n") + + if dry_run: + print("[DRY RUN] Skipping actual test") + return True + + try: + result = subprocess.run(cmd, check=False) + return result.returncode == 0 + except Exception as e: + print(f"Error running test: {e}") + return False + + +def main() -> None: + """Main entry point.""" + parser = argparse.ArgumentParser( + description="Test component grouping by finding and testing groups" + ) + parser.add_argument( + "--platform", + "-p", + default="esp32-idf", + help="Platform to test (default: esp32-idf)", + ) + parser.add_argument( + "-e", + "--esphome-command", + default="compile", + choices=["config", "compile", "clean"], + help="ESPHome command to run (default: compile)", + ) + parser.add_argument( + "--all", + action="store_true", + help="Test all components (sets --min-size=1, --max-size=10000, --max-groups=10000)", + ) + parser.add_argument( + "--min-size", + type=int, + default=3, + help="Minimum group size to test (default: 3)", + ) + parser.add_argument( + "--max-size", + type=int, + default=10, + help="Maximum group size to test (default: 10)", + ) + parser.add_argument( + "--max-groups", + type=int, + default=5, + help="Maximum number of groups to test (default: 5)", + ) + parser.add_argument( + "--signature", + "-s", + help="Only test groups with this bus signature (e.g., 'spi', 'i2c', 'uart')", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Print commands without running them", + ) + + args = parser.parse_args() + + # If --all is specified, test all components without grouping + if args.all: + # Get all components from tests/components directory + components_dir = Path("tests/components") + all_components = sorted( + [d.name for d in components_dir.iterdir() if d.is_dir()] + ) + + if not all_components: + print(f"\nNo components found in {components_dir}") + return + + print(f"\nTesting all {len(all_components)} components together") + + success = test_component_group( + all_components, args.platform, args.esphome_command, args.dry_run + ) + + # Print summary + print(f"\n{'=' * 80}") + print("TEST SUMMARY") + print(f"{'=' * 80}") + status = "✅ PASS" if success else "❌ FAIL" + print(f"{status} All components: {len(all_components)} components") + + if not args.dry_run and not success: + sys.exit(1) + return + + print("Analyzing all components...") + components, non_groupable, _ = analyze_all_components(Path("tests/components")) + + print(f"Found {len(components)} components, {len(non_groupable)} non-groupable") + + # Group components by signature for the platform + groups = group_components_by_signature(components, args.platform) + + # Filter and sort groups + filtered_groups = [] + for signature, comp_list in groups.items(): + # Filter by signature if specified + if args.signature and signature != args.signature: + continue + + # Remove non-groupable components + comp_list = [c for c in comp_list if c not in non_groupable] + + # Filter by minimum size + if len(comp_list) < args.min_size: + continue + + # If group is larger than max_size, we'll take a subset later + filtered_groups.append((signature, comp_list)) + + # Sort by group size (largest first) + filtered_groups.sort(key=lambda x: len(x[1]), reverse=True) + + # Limit number of groups + filtered_groups = filtered_groups[: args.max_groups] + + if not filtered_groups: + print("\nNo groups found matching criteria:") + print(f" - Platform: {args.platform}") + print(f" - Size: {args.min_size}-{args.max_size}") + if args.signature: + print(f" - Signature: {args.signature}") + return + + print(f"\nFound {len(filtered_groups)} groups to test:") + for signature, comp_list in filtered_groups: + print(f" [{signature}]: {len(comp_list)} components") + + # Test each group + results = [] + for signature, comp_list in filtered_groups: + # Limit to max_size if group is larger + if len(comp_list) > args.max_size: + comp_list = comp_list[: args.max_size] + + success = test_component_group( + comp_list, args.platform, args.esphome_command, args.dry_run + ) + results.append((signature, comp_list, success)) + + if not args.dry_run and not success: + print(f"\n❌ FAILED: {signature} group") + break + + # Print summary + print(f"\n{'=' * 80}") + print("TEST SUMMARY") + print(f"{'=' * 80}") + for signature, comp_list, success in results: + status = "✅ PASS" if success else "❌ FAIL" + print(f"{status} [{signature}]: {len(comp_list)} components") + + # Exit with error if any tests failed + if not args.dry_run and any(not success for _, _, success in results): + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/tests/components/a01nyub/common.yaml b/tests/components/a01nyub/common.yaml index 0717acfff7..fc0b2d3bfb 100644 --- a/tests/components/a01nyub/common.yaml +++ b/tests/components/a01nyub/common.yaml @@ -1,11 +1,4 @@ -uart: - - id: uart_a01nyub - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: a01nyub id: a01nyub_sensor name: a01nyub Distance - uart_id: uart_a01nyub diff --git a/tests/components/a01nyub/test.esp32-c3-idf.yaml b/tests/components/a01nyub/test.esp32-c3-idf.yaml index b516342f3b..2cda8deaf9 100644 --- a/tests/components/a01nyub/test.esp32-c3-idf.yaml +++ b/tests/components/a01nyub/test.esp32-c3-idf.yaml @@ -1,3 +1,6 @@ +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml + substitutions: tx_pin: GPIO4 rx_pin: GPIO5 diff --git a/tests/components/a01nyub/test.esp32-idf.yaml b/tests/components/a01nyub/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/a01nyub/test.esp32-idf.yaml +++ b/tests/components/a01nyub/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/a01nyub/test.esp8266-ard.yaml b/tests/components/a01nyub/test.esp8266-ard.yaml index b516342f3b..5a05efa259 100644 --- a/tests/components/a01nyub/test.esp8266-ard.yaml +++ b/tests/components/a01nyub/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/a01nyub/test.rp2040-ard.yaml b/tests/components/a01nyub/test.rp2040-ard.yaml index b516342f3b..f1df2daf83 100644 --- a/tests/components/a01nyub/test.rp2040-ard.yaml +++ b/tests/components/a01nyub/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/a02yyuw/common.yaml b/tests/components/a02yyuw/common.yaml index b2e5927ff4..4de8a6eb67 100644 --- a/tests/components/a02yyuw/common.yaml +++ b/tests/components/a02yyuw/common.yaml @@ -1,11 +1,4 @@ -uart: - - id: uart_a02yyuw - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: a02yyuw id: a02yyuw_sensor name: a02yyuw Distance - uart_id: uart_a02yyuw diff --git a/tests/components/a02yyuw/test.esp32-c3-idf.yaml b/tests/components/a02yyuw/test.esp32-c3-idf.yaml index b516342f3b..2cda8deaf9 100644 --- a/tests/components/a02yyuw/test.esp32-c3-idf.yaml +++ b/tests/components/a02yyuw/test.esp32-c3-idf.yaml @@ -1,3 +1,6 @@ +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml + substitutions: tx_pin: GPIO4 rx_pin: GPIO5 diff --git a/tests/components/a02yyuw/test.esp32-idf.yaml b/tests/components/a02yyuw/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/a02yyuw/test.esp32-idf.yaml +++ b/tests/components/a02yyuw/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/a02yyuw/test.esp8266-ard.yaml b/tests/components/a02yyuw/test.esp8266-ard.yaml index b516342f3b..5a05efa259 100644 --- a/tests/components/a02yyuw/test.esp8266-ard.yaml +++ b/tests/components/a02yyuw/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/a02yyuw/test.rp2040-ard.yaml b/tests/components/a02yyuw/test.rp2040-ard.yaml index b516342f3b..f1df2daf83 100644 --- a/tests/components/a02yyuw/test.rp2040-ard.yaml +++ b/tests/components/a02yyuw/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/a4988/test.esp32-idf.yaml b/tests/components/a4988/test.esp32-idf.yaml index 1ca8c0c084..7d46b048e1 100644 --- a/tests/components/a4988/test.esp32-idf.yaml +++ b/tests/components/a4988/test.esp32-idf.yaml @@ -1,6 +1,6 @@ substitutions: step_pin: GPIO22 - dir_pin: GPIO23 + dir_pin: GPIO4 sleep_pin: GPIO25 <<: !include common.yaml diff --git a/tests/components/a4988/test.esp8266-ard.yaml b/tests/components/a4988/test.esp8266-ard.yaml index 22b5677d27..5b1b1293be 100644 --- a/tests/components/a4988/test.esp8266-ard.yaml +++ b/tests/components/a4988/test.esp8266-ard.yaml @@ -1,6 +1,6 @@ substitutions: step_pin: GPIO1 dir_pin: GPIO2 - sleep_pin: GPIO5 + sleep_pin: GPIO0 <<: !include common.yaml diff --git a/tests/components/ac_dimmer/test.esp32-ard.yaml b/tests/components/ac_dimmer/test.esp32-ard.yaml index 3ec069f430..eaa4901f03 100644 --- a/tests/components/ac_dimmer/test.esp32-ard.yaml +++ b/tests/components/ac_dimmer/test.esp32-ard.yaml @@ -1,5 +1,5 @@ substitutions: - gate_pin: GPIO18 - zero_cross_pin: GPIO19 + gate_pin: GPIO4 + zero_cross_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/ac_dimmer/test.esp8266-ard.yaml b/tests/components/ac_dimmer/test.esp8266-ard.yaml index 5d2d42b713..2f50b04956 100644 --- a/tests/components/ac_dimmer/test.esp8266-ard.yaml +++ b/tests/components/ac_dimmer/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - gate_pin: GPIO5 - zero_cross_pin: GPIO4 + gate_pin: GPIO0 + zero_cross_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/adc/common.yaml b/tests/components/adc/common.yaml deleted file mode 100644 index ebdd1aece5..0000000000 --- a/tests/components/adc/common.yaml +++ /dev/null @@ -1,11 +0,0 @@ -sensor: - - id: my_sensor - platform: adc - name: ADC Test sensor - update_interval: "1:01" - attenuation: 2.5db - unit_of_measurement: "°C" - icon: "mdi:water-percent" - accuracy_decimals: 5 - setup_priority: -100 - force_update: true diff --git a/tests/components/adc/test.bk72xx-ard.yaml b/tests/components/adc/test.bk72xx-ard.yaml index 0a3d5d1fdc..0645333a81 100644 --- a/tests/components/adc/test.bk72xx-ard.yaml +++ b/tests/components/adc/test.bk72xx-ard.yaml @@ -1,7 +1,11 @@ -packages: - base: !include common.yaml - sensor: - - id: !extend my_sensor + - id: my_sensor + platform: adc pin: P23 - attenuation: !remove + name: ADC Test sensor + update_interval: "1:01" + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc/test.esp32-c3-idf.yaml b/tests/components/adc/test.esp32-c3-idf.yaml index ea3b00a85f..e764f0fe21 100644 --- a/tests/components/adc/test.esp32-c3-idf.yaml +++ b/tests/components/adc/test.esp32-c3-idf.yaml @@ -1,6 +1,12 @@ -packages: - base: !include common.yaml - sensor: - - id: !extend my_sensor - pin: 4 + - id: my_sensor + platform: adc + pin: GPIO1 + name: ADC Test sensor + update_interval: "1:01" + attenuation: 2.5db + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc/test.esp32-idf.yaml b/tests/components/adc/test.esp32-idf.yaml index e6a1fd3bd9..ff1e3bb919 100644 --- a/tests/components/adc/test.esp32-idf.yaml +++ b/tests/components/adc/test.esp32-idf.yaml @@ -1,6 +1,12 @@ -packages: - base: !include common.yaml - sensor: - - id: !extend my_sensor + - id: my_sensor + platform: adc pin: A0 + name: ADC Test sensor + update_interval: "1:01" + attenuation: 2.5db + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc/test.esp32-p4-idf.yaml b/tests/components/adc/test.esp32-p4-idf.yaml index 97844cf398..b77dc299c2 100644 --- a/tests/components/adc/test.esp32-p4-idf.yaml +++ b/tests/components/adc/test.esp32-p4-idf.yaml @@ -1,6 +1,12 @@ -packages: - base: !include common.yaml - sensor: - - id: !extend my_sensor - pin: GPIO50 + - id: my_sensor + platform: adc + pin: GPIO16 + name: ADC Test sensor + update_interval: "1:01" + attenuation: 2.5db + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc/test.esp32-s2-idf.yaml b/tests/components/adc/test.esp32-s2-idf.yaml index bbd91c5e5a..e764f0fe21 100644 --- a/tests/components/adc/test.esp32-s2-idf.yaml +++ b/tests/components/adc/test.esp32-s2-idf.yaml @@ -1,6 +1,12 @@ -packages: - base: !include common.yaml - sensor: - - id: !extend my_sensor - pin: 1 + - id: my_sensor + platform: adc + pin: GPIO1 + name: ADC Test sensor + update_interval: "1:01" + attenuation: 2.5db + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc/test.esp32-s3-idf.yaml b/tests/components/adc/test.esp32-s3-idf.yaml index bbd91c5e5a..e764f0fe21 100644 --- a/tests/components/adc/test.esp32-s3-idf.yaml +++ b/tests/components/adc/test.esp32-s3-idf.yaml @@ -1,6 +1,12 @@ -packages: - base: !include common.yaml - sensor: - - id: !extend my_sensor - pin: 1 + - id: my_sensor + platform: adc + pin: GPIO1 + name: ADC Test sensor + update_interval: "1:01" + attenuation: 2.5db + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc/test.esp8266-ard.yaml b/tests/components/adc/test.esp8266-ard.yaml index bcb3620cfc..4cc865bb5d 100644 --- a/tests/components/adc/test.esp8266-ard.yaml +++ b/tests/components/adc/test.esp8266-ard.yaml @@ -1,7 +1,11 @@ -packages: - base: !include common.yaml - sensor: - - id: !extend my_sensor + - id: my_sensor + platform: adc pin: VCC - attenuation: !remove + name: ADC Test sensor + update_interval: "1:01" + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc/test.ln882x-ard.yaml b/tests/components/adc/test.ln882x-ard.yaml index 0622cd7b27..face38b647 100644 --- a/tests/components/adc/test.ln882x-ard.yaml +++ b/tests/components/adc/test.ln882x-ard.yaml @@ -1,7 +1,11 @@ -packages: - base: !include common.yaml - sensor: - - id: !extend my_sensor - pin: PA0 - attenuation: !remove + - id: my_sensor + platform: adc + pin: A5 + name: ADC Test sensor + update_interval: "1:01" + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc/test.rp2040-ard.yaml b/tests/components/adc/test.rp2040-ard.yaml index bcb3620cfc..4cc865bb5d 100644 --- a/tests/components/adc/test.rp2040-ard.yaml +++ b/tests/components/adc/test.rp2040-ard.yaml @@ -1,7 +1,11 @@ -packages: - base: !include common.yaml - sensor: - - id: !extend my_sensor + - id: my_sensor + platform: adc pin: VCC - attenuation: !remove + name: ADC Test sensor + update_interval: "1:01" + unit_of_measurement: "°C" + icon: "mdi:water-percent" + accuracy_decimals: 5 + setup_priority: -100 + force_update: true diff --git a/tests/components/adc128s102/common.yaml b/tests/components/adc128s102/common.yaml index 5f1638a7e2..b909310bdf 100644 --- a/tests/components/adc128s102/common.yaml +++ b/tests/components/adc128s102/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_adc128s102 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - adc128s102: cs_pin: ${cs_pin} id: adc128s102_adc diff --git a/tests/components/adc128s102/test.esp32-c3-idf.yaml b/tests/components/adc128s102/test.esp32-c3-idf.yaml index 24da4b5452..a60568a736 100644 --- a/tests/components/adc128s102/test.esp32-c3-idf.yaml +++ b/tests/components/adc128s102/test.esp32-c3-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO2 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/adc128s102/test.esp32-idf.yaml b/tests/components/adc128s102/test.esp32-idf.yaml index aba72f0614..9bb524aa65 100644 --- a/tests/components/adc128s102/test.esp32-idf.yaml +++ b/tests/components/adc128s102/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO12 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/adc128s102/test.esp8266-ard.yaml b/tests/components/adc128s102/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/adc128s102/test.esp8266-ard.yaml +++ b/tests/components/adc128s102/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/adc128s102/test.rp2040-ard.yaml b/tests/components/adc128s102/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/adc128s102/test.rp2040-ard.yaml +++ b/tests/components/adc128s102/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ade7880/common.yaml b/tests/components/ade7880/common.yaml index 0aa388a325..0b0b560282 100644 --- a/tests/components/ade7880/common.yaml +++ b/tests/components/ade7880/common.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_ade7880 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ade7880 - i2c_id: i2c_ade7880 + i2c_id: i2c_bus irq0_pin: ${irq0_pin} irq1_pin: ${irq1_pin} reset_pin: ${reset_pin} diff --git a/tests/components/ade7880/test.esp32-c3-idf.yaml b/tests/components/ade7880/test.esp32-c3-idf.yaml index 87db3e9427..7d5b41fc5a 100644 --- a/tests/components/ade7880/test.esp32-c3-idf.yaml +++ b/tests/components/ade7880/test.esp32-c3-idf.yaml @@ -1,8 +1,9 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq0_pin: GPIO6 irq1_pin: GPIO7 - reset_pin: GPIO10 + reset_pin: GPIO9 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ade7880/test.esp32-idf.yaml b/tests/components/ade7880/test.esp32-idf.yaml index 685b49ff32..9db2e50049 100644 --- a/tests/components/ade7880/test.esp32-idf.yaml +++ b/tests/components/ade7880/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq0_pin: GPIO13 irq1_pin: GPIO15 - reset_pin: GPIO16 + reset_pin: GPIO12 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ade7880/test.esp8266-ard.yaml b/tests/components/ade7880/test.esp8266-ard.yaml index 685b49ff32..81a04d0724 100644 --- a/tests/components/ade7880/test.esp8266-ard.yaml +++ b/tests/components/ade7880/test.esp8266-ard.yaml @@ -1,8 +1,9 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq0_pin: GPIO13 irq1_pin: GPIO15 reset_pin: GPIO16 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ade7880/test.rp2040-ard.yaml b/tests/components/ade7880/test.rp2040-ard.yaml index 685b49ff32..f531f852ae 100644 --- a/tests/components/ade7880/test.rp2040-ard.yaml +++ b/tests/components/ade7880/test.rp2040-ard.yaml @@ -1,8 +1,9 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq0_pin: GPIO13 irq1_pin: GPIO15 reset_pin: GPIO16 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ade7953_i2c/common.yaml b/tests/components/ade7953_i2c/common.yaml index a2d163567d..8b2a9588fe 100644 --- a/tests/components/ade7953_i2c/common.yaml +++ b/tests/components/ade7953_i2c/common.yaml @@ -1,20 +1,13 @@ -i2c: - - id: i2c_ade7953 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ade7953_i2c + i2c_id: i2c_bus irq_pin: ${irq_pin} voltage: name: ADE7953 Voltage - id: ade7953_voltage current_a: name: ADE7953 Current A - id: ade7953_current_a current_b: name: ADE7953 Current B - id: ade7953_current_b power_factor_a: name: ADE7953 Power Factor A power_factor_b: diff --git a/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml b/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml index 799acabd5a..59296a1e6e 100644 --- a/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.esp32-idf.yaml b/tests/components/ade7953_i2c/test.esp32-idf.yaml index 2c57d412f6..49629536e7 100644 --- a/tests/components/ade7953_i2c/test.esp32-idf.yaml +++ b/tests/components/ade7953_i2c/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 irq_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.esp8266-ard.yaml b/tests/components/ade7953_i2c/test.esp8266-ard.yaml index c8e6a43f44..dc7609ab37 100644 --- a/tests/components/ade7953_i2c/test.esp8266-ard.yaml +++ b/tests/components/ade7953_i2c/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.rp2040-ard.yaml b/tests/components/ade7953_i2c/test.rp2040-ard.yaml index 799acabd5a..b80562ad22 100644 --- a/tests/components/ade7953_i2c/test.rp2040-ard.yaml +++ b/tests/components/ade7953_i2c/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ade7953_spi/common.yaml b/tests/components/ade7953_spi/common.yaml index 706f31f22c..30b5258a2a 100644 --- a/tests/components/ade7953_spi/common.yaml +++ b/tests/components/ade7953_spi/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_ade7953 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: ade7953_spi cs_pin: ${cs_pin} diff --git a/tests/components/ade7953_spi/test.esp32-c3-idf.yaml b/tests/components/ade7953_spi/test.esp32-c3-idf.yaml index fcf35f528e..5e7e2dc82c 100644 --- a/tests/components/ade7953_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ade7953_spi/test.esp32-c3-idf.yaml @@ -1,8 +1,7 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 irq_pin: GPIO9 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.esp32-idf.yaml b/tests/components/ade7953_spi/test.esp32-idf.yaml index e00f522dd4..19791e24b7 100644 --- a/tests/components/ade7953_spi/test.esp32-idf.yaml +++ b/tests/components/ade7953_spi/test.esp32-idf.yaml @@ -1,8 +1,8 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 irq_pin: GPIO13 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.esp8266-ard.yaml b/tests/components/ade7953_spi/test.esp8266-ard.yaml index b90e661ec0..8475dc4c50 100644 --- a/tests/components/ade7953_spi/test.esp8266-ard.yaml +++ b/tests/components/ade7953_spi/test.esp8266-ard.yaml @@ -1,8 +1,11 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 irq_pin: GPIO5 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.rp2040-ard.yaml b/tests/components/ade7953_spi/test.rp2040-ard.yaml index 8f5941e1b2..7c4a74a236 100644 --- a/tests/components/ade7953_spi/test.rp2040-ard.yaml +++ b/tests/components/ade7953_spi/test.rp2040-ard.yaml @@ -5,4 +5,7 @@ substitutions: irq_pin: GPIO5 cs_pin: GPIO6 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ads1115/common.yaml b/tests/components/ads1115/common.yaml index 297877d2d8..4724dc5a14 100644 --- a/tests/components/ads1115/common.yaml +++ b/tests/components/ads1115/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_ads1115 - scl: ${scl_pin} - sda: ${sda_pin} - ads1115: + i2c_id: i2c_bus address: 0x48 sensor: diff --git a/tests/components/ads1115/test.esp32-c3-idf.yaml b/tests/components/ads1115/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ads1115/test.esp32-c3-idf.yaml +++ b/tests/components/ads1115/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ads1115/test.esp32-idf.yaml b/tests/components/ads1115/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ads1115/test.esp32-idf.yaml +++ b/tests/components/ads1115/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ads1115/test.esp8266-ard.yaml b/tests/components/ads1115/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ads1115/test.esp8266-ard.yaml +++ b/tests/components/ads1115/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ads1115/test.rp2040-ard.yaml b/tests/components/ads1115/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ads1115/test.rp2040-ard.yaml +++ b/tests/components/ads1115/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ags10/common.yaml b/tests/components/ags10/common.yaml index 0c4c3513cf..0551871e59 100644 --- a/tests/components/ags10/common.yaml +++ b/tests/components/ags10/common.yaml @@ -1,9 +1,3 @@ -i2c: - - id: i2c_ags10 - scl: ${scl_pin} - sda: ${sda_pin} - frequency: 10kHz - sensor: - platform: ags10 id: ags10_1 diff --git a/tests/components/ags10/test.esp32-c3-idf.yaml b/tests/components/ags10/test.esp32-c3-idf.yaml index ee2c29ca4e..72703301a1 100644 --- a/tests/components/ags10/test.esp32-c3-idf.yaml +++ b/tests/components/ags10/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ags10/test.esp32-idf.yaml b/tests/components/ags10/test.esp32-idf.yaml index 63c3bd6afd..7a5d01898a 100644 --- a/tests/components/ags10/test.esp32-idf.yaml +++ b/tests/components/ags10/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ags10/test.esp8266-ard.yaml b/tests/components/ags10/test.esp8266-ard.yaml index ee2c29ca4e..9e23bb3778 100644 --- a/tests/components/ags10/test.esp8266-ard.yaml +++ b/tests/components/ags10/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/aht10/common.yaml b/tests/components/aht10/common.yaml index 721af09bb4..d7c3f9364f 100644 --- a/tests/components/aht10/common.yaml +++ b/tests/components/aht10/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_aht10 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: aht10 + i2c_id: i2c_bus temperature: name: Temperature humidity: diff --git a/tests/components/aht10/test.esp32-c3-idf.yaml b/tests/components/aht10/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/aht10/test.esp32-c3-idf.yaml +++ b/tests/components/aht10/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/aht10/test.esp32-idf.yaml b/tests/components/aht10/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/aht10/test.esp32-idf.yaml +++ b/tests/components/aht10/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/aht10/test.esp8266-ard.yaml b/tests/components/aht10/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/aht10/test.esp8266-ard.yaml +++ b/tests/components/aht10/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/aht10/test.rp2040-ard.yaml b/tests/components/aht10/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/aht10/test.rp2040-ard.yaml +++ b/tests/components/aht10/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/aic3204/common.yaml b/tests/components/aic3204/common.yaml index 6e939bd260..5f175faee3 100644 --- a/tests/components/aic3204/common.yaml +++ b/tests/components/aic3204/common.yaml @@ -6,10 +6,6 @@ esphome: - audio_dac.set_volume: volume: 50% -i2c: - - id: i2c_aic3204 - scl: ${scl_pin} - sda: ${sda_pin} - audio_dac: - platform: aic3204 + i2c_id: i2c_bus diff --git a/tests/components/aic3204/test.esp32-c3-idf.yaml b/tests/components/aic3204/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/aic3204/test.esp32-c3-idf.yaml +++ b/tests/components/aic3204/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/aic3204/test.esp32-idf.yaml b/tests/components/aic3204/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/aic3204/test.esp32-idf.yaml +++ b/tests/components/aic3204/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/aic3204/test.esp8266-ard.yaml b/tests/components/aic3204/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/aic3204/test.esp8266-ard.yaml +++ b/tests/components/aic3204/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml b/tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml +++ b/tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/airthings_wave_mini/test.esp32-idf.yaml b/tests/components/airthings_wave_mini/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/airthings_wave_mini/test.esp32-idf.yaml +++ b/tests/components/airthings_wave_mini/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml b/tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml +++ b/tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/airthings_wave_plus/test.esp32-idf.yaml b/tests/components/airthings_wave_plus/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/airthings_wave_plus/test.esp32-idf.yaml +++ b/tests/components/airthings_wave_plus/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/alpha3/test.esp32-c3-idf.yaml b/tests/components/alpha3/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/alpha3/test.esp32-c3-idf.yaml +++ b/tests/components/alpha3/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/alpha3/test.esp32-idf.yaml b/tests/components/alpha3/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/alpha3/test.esp32-idf.yaml +++ b/tests/components/alpha3/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/am2315c/common.yaml b/tests/components/am2315c/common.yaml index ab4656c17d..362fe19e4d 100644 --- a/tests/components/am2315c/common.yaml +++ b/tests/components/am2315c/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_am2315c - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: am2315c + i2c_id: i2c_bus temperature: name: Temperature humidity: diff --git a/tests/components/am2315c/test.esp32-c3-idf.yaml b/tests/components/am2315c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/am2315c/test.esp32-c3-idf.yaml +++ b/tests/components/am2315c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/am2315c/test.esp32-idf.yaml b/tests/components/am2315c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/am2315c/test.esp32-idf.yaml +++ b/tests/components/am2315c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/am2315c/test.esp8266-ard.yaml b/tests/components/am2315c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/am2315c/test.esp8266-ard.yaml +++ b/tests/components/am2315c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/am2315c/test.rp2040-ard.yaml b/tests/components/am2315c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/am2315c/test.rp2040-ard.yaml +++ b/tests/components/am2315c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/am2320/common.yaml b/tests/components/am2320/common.yaml index c0982b8818..d67ca3e564 100644 --- a/tests/components/am2320/common.yaml +++ b/tests/components/am2320/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_am2320 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: am2320 + i2c_id: i2c_bus temperature: name: Temperature humidity: diff --git a/tests/components/am2320/test.esp32-c3-idf.yaml b/tests/components/am2320/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/am2320/test.esp32-c3-idf.yaml +++ b/tests/components/am2320/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/am2320/test.esp32-idf.yaml b/tests/components/am2320/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/am2320/test.esp32-idf.yaml +++ b/tests/components/am2320/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/am2320/test.esp8266-ard.yaml b/tests/components/am2320/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/am2320/test.esp8266-ard.yaml +++ b/tests/components/am2320/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/am2320/test.rp2040-ard.yaml b/tests/components/am2320/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/am2320/test.rp2040-ard.yaml +++ b/tests/components/am2320/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/am43/test.esp32-c3-idf.yaml b/tests/components/am43/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/am43/test.esp32-c3-idf.yaml +++ b/tests/components/am43/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/am43/test.esp32-idf.yaml b/tests/components/am43/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/am43/test.esp32-idf.yaml +++ b/tests/components/am43/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/animation/test.esp32-c3-idf.yaml b/tests/components/animation/test.esp32-c3-idf.yaml index 18aa2a5b06..a08a683333 100644 --- a/tests/components/animation/test.esp32-c3-idf.yaml +++ b/tests/components/animation/test.esp32-c3-idf.yaml @@ -1,17 +1,13 @@ -spi: - - id: spi_main_lcd - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml + animation: !include common.yaml display: - platform: ili9xxx id: main_lcd + spi_id: spi_bus model: ili9342 cs_pin: 8 dc_pin: 9 reset_pin: 10 invert_colors: false - -packages: - animation: !include common.yaml diff --git a/tests/components/animation/test.esp32-idf.yaml b/tests/components/animation/test.esp32-idf.yaml index 7d9fe45bff..c28e9584dd 100644 --- a/tests/components/animation/test.esp32-idf.yaml +++ b/tests/components/animation/test.esp32-idf.yaml @@ -1,17 +1,13 @@ -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + animation: !include common.yaml display: - platform: ili9xxx id: main_lcd + spi_id: spi_bus model: ili9342 cs_pin: 12 dc_pin: 13 reset_pin: 21 invert_colors: false - -packages: - animation: !include common.yaml diff --git a/tests/components/animation/test.esp8266-ard.yaml b/tests/components/animation/test.esp8266-ard.yaml index 9548c7fbeb..11a7117d91 100644 --- a/tests/components/animation/test.esp8266-ard.yaml +++ b/tests/components/animation/test.esp8266-ard.yaml @@ -1,17 +1,13 @@ -spi: - - id: spi_main_lcd - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + animation: !include common.yaml display: - platform: ili9xxx id: main_lcd + spi_id: spi_bus model: ili9342 cs_pin: 5 dc_pin: 15 reset_pin: 16 invert_colors: false - -packages: - animation: !include common.yaml diff --git a/tests/components/animation/test.rp2040-ard.yaml b/tests/components/animation/test.rp2040-ard.yaml index efb3f2907c..32fb4efb04 100644 --- a/tests/components/animation/test.rp2040-ard.yaml +++ b/tests/components/animation/test.rp2040-ard.yaml @@ -1,17 +1,13 @@ -spi: - - id: spi_main_lcd - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + animation: !include common.yaml display: - platform: ili9xxx id: main_lcd + spi_id: spi_bus model: ili9342 cs_pin: 20 dc_pin: 21 reset_pin: 22 invert_colors: false - -packages: - animation: !include common.yaml diff --git a/tests/components/anova/test.esp32-c3-idf.yaml b/tests/components/anova/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/anova/test.esp32-c3-idf.yaml +++ b/tests/components/anova/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/anova/test.esp32-idf.yaml b/tests/components/anova/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/anova/test.esp32-idf.yaml +++ b/tests/components/anova/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/apds9306/common.yaml b/tests/components/apds9306/common.yaml index b3828e62ff..dc34f47645 100644 --- a/tests/components/apds9306/common.yaml +++ b/tests/components/apds9306/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_apds9306 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: apds9306 + i2c_id: i2c_bus name: "APDS9306 Light Level" gain: 3 bit_width: 16 diff --git a/tests/components/apds9306/test.esp32-c3-idf.yaml b/tests/components/apds9306/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/apds9306/test.esp32-c3-idf.yaml +++ b/tests/components/apds9306/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/apds9306/test.esp32-idf.yaml b/tests/components/apds9306/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/apds9306/test.esp32-idf.yaml +++ b/tests/components/apds9306/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/apds9306/test.esp8266-ard.yaml b/tests/components/apds9306/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/apds9306/test.esp8266-ard.yaml +++ b/tests/components/apds9306/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/apds9306/test.rp2040-ard.yaml b/tests/components/apds9306/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/apds9306/test.rp2040-ard.yaml +++ b/tests/components/apds9306/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/apds9960/common.yaml b/tests/components/apds9960/common.yaml index de7706648a..c14212d263 100644 --- a/tests/components/apds9960/common.yaml +++ b/tests/components/apds9960/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_apds9960 - scl: ${scl_pin} - sda: ${sda_pin} - apds9960: + i2c_id: i2c_bus address: 0x20 update_interval: 60s diff --git a/tests/components/apds9960/test.esp32-c3-idf.yaml b/tests/components/apds9960/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/apds9960/test.esp32-c3-idf.yaml +++ b/tests/components/apds9960/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/apds9960/test.esp32-idf.yaml b/tests/components/apds9960/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/apds9960/test.esp32-idf.yaml +++ b/tests/components/apds9960/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/apds9960/test.esp8266-ard.yaml b/tests/components/apds9960/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/apds9960/test.esp8266-ard.yaml +++ b/tests/components/apds9960/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/apds9960/test.rp2040-ard.yaml b/tests/components/apds9960/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/apds9960/test.rp2040-ard.yaml +++ b/tests/components/apds9960/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/api/common-base.yaml b/tests/components/api/common-base.yaml new file mode 100644 index 0000000000..6483d5a997 --- /dev/null +++ b/tests/components/api/common-base.yaml @@ -0,0 +1,89 @@ +esphome: + on_boot: + then: + - homeassistant.event: + event: esphome.button_pressed + data: + message: Button was pressed + - homeassistant.action: + action: notify.html5 + data: + message: Button was pressed + - homeassistant.tag_scanned: pulse + - homeassistant.action: + action: weather.get_forecasts + data: + entity_id: weather.forecast_home + type: hourly + capture_response: true + on_success: + - lambda: |- + JsonObjectConst next_hour = response["response"]["weather.forecast_home"]["forecast"][0]; + float next_temperature = next_hour["temperature"].as(); + ESP_LOGD("main", "Next hour temperature: %f", next_temperature); + on_error: + - lambda: |- + ESP_LOGE("main", "Action failed with error: %s", error.c_str()); + - homeassistant.action: + action: weather.get_forecasts + data: + entity_id: weather.forecast_home + type: hourly + capture_response: true + response_template: "{{ response['weather.forecast_home']['forecast'][0]['temperature'] }}" + on_success: + - lambda: |- + float temperature = response["response"].as(); + ESP_LOGD("main", "Next hour temperature: %f", temperature); + - homeassistant.action: + action: light.toggle + data: + entity_id: light.demo_light + on_success: + - logger.log: "Toggled demo light" + on_error: + - logger.log: "Failed to toggle demo light" + +api: + port: 8000 + reboot_timeout: 0min + actions: + - action: hello_world + variables: + name: string + then: + - logger.log: + format: Hello World %s! + args: + - name.c_str() + - action: empty_action + then: + - logger.log: Action Called + - action: all_types + variables: + bool_: bool + int_: int + float_: float + string_: string + then: + - logger.log: Something happened + - action: array_types + variables: + bool_arr: bool[] + int_arr: int[] + float_arr: float[] + string_arr: string[] + then: + - logger.log: + # yamllint disable rule:line-length + format: "Bool: %s (%u), Int: %ld (%u), Float: %f (%u), String: %s (%u)" + # yamllint enable rule:line-length + args: + - YESNO(bool_arr[0]) + - bool_arr.size() + - (long) int_arr[0] + - int_arr.size() + - float_arr[0] + - float_arr.size() + - string_arr[0].c_str() + - string_arr.size() diff --git a/tests/components/api/common.yaml b/tests/components/api/common.yaml index d87ae56ec2..6115838b6d 100644 --- a/tests/components/api/common.yaml +++ b/tests/components/api/common.yaml @@ -1,91 +1,5 @@ -esphome: - on_boot: - then: - - homeassistant.event: - event: esphome.button_pressed - data: - message: Button was pressed - - homeassistant.action: - action: notify.html5 - data: - message: Button was pressed - - homeassistant.tag_scanned: pulse - - homeassistant.action: - action: weather.get_forecasts - data: - entity_id: weather.forecast_home - type: hourly - capture_response: true - on_success: - - lambda: |- - JsonObjectConst next_hour = response["response"]["weather.forecast_home"]["forecast"][0]; - float next_temperature = next_hour["temperature"].as(); - ESP_LOGD("main", "Next hour temperature: %f", next_temperature); - on_error: - - lambda: |- - ESP_LOGE("main", "Action failed with error: %s", error.c_str()); - - homeassistant.action: - action: weather.get_forecasts - data: - entity_id: weather.forecast_home - type: hourly - capture_response: true - response_template: "{{ response['weather.forecast_home']['forecast'][0]['temperature'] }}" - on_success: - - lambda: |- - float temperature = response["response"].as(); - ESP_LOGD("main", "Next hour temperature: %f", temperature); - - homeassistant.action: - action: light.toggle - data: - entity_id: light.demo_light - on_success: - - logger.log: "Toggled demo light" - on_error: - - logger.log: "Failed to toggle demo light" +<<: !include common-base.yaml api: - port: 8000 - reboot_timeout: 0min encryption: key: bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU= - actions: - - action: hello_world - variables: - name: string - then: - - logger.log: - format: Hello World %s! - args: - - name.c_str() - - action: empty_action - then: - - logger.log: Action Called - - action: all_types - variables: - bool_: bool - int_: int - float_: float - string_: string - then: - - logger.log: Something happened - - action: array_types - variables: - bool_arr: bool[] - int_arr: int[] - float_arr: float[] - string_arr: string[] - then: - - logger.log: - # yamllint disable rule:line-length - format: "Bool: %s (%u), Int: %ld (%u), Float: %f (%u), String: %s (%u)" - # yamllint enable rule:line-length - args: - - YESNO(bool_arr[0]) - - bool_arr.size() - - (long) int_arr[0] - - int_arr.size() - - float_arr[0] - - float_arr.size() - - string_arr[0].c_str() - - string_arr.size() diff --git a/tests/components/api/test-dynamic-encryption.esp32-idf.yaml b/tests/components/api/test-dynamic-encryption.esp32-idf.yaml index d8f8c247f4..504871716b 100644 --- a/tests/components/api/test-dynamic-encryption.esp32-idf.yaml +++ b/tests/components/api/test-dynamic-encryption.esp32-idf.yaml @@ -1,10 +1,5 @@ -packages: - common: !include common.yaml +<<: !include common-base.yaml wifi: ssid: MySSID password: password1 - -api: - encryption: - key: !remove diff --git a/tests/components/as3935_i2c/common.yaml b/tests/components/as3935_i2c/common.yaml index d76cc37fc1..a758bb7f56 100644 --- a/tests/components/as3935_i2c/common.yaml +++ b/tests/components/as3935_i2c/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_as3935 - scl: ${scl_pin} - sda: ${sda_pin} - as3935_i2c: + i2c_id: i2c_bus irq_pin: ${irq_pin} binary_sensor: diff --git a/tests/components/as3935_i2c/test.esp32-c3-idf.yaml b/tests/components/as3935_i2c/test.esp32-c3-idf.yaml index 799acabd5a..59296a1e6e 100644 --- a/tests/components/as3935_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/as3935_i2c/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/as3935_i2c/test.esp32-idf.yaml b/tests/components/as3935_i2c/test.esp32-idf.yaml index 2c57d412f6..49629536e7 100644 --- a/tests/components/as3935_i2c/test.esp32-idf.yaml +++ b/tests/components/as3935_i2c/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 irq_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/as3935_i2c/test.esp8266-ard.yaml b/tests/components/as3935_i2c/test.esp8266-ard.yaml index c8e6a43f44..dc7609ab37 100644 --- a/tests/components/as3935_i2c/test.esp8266-ard.yaml +++ b/tests/components/as3935_i2c/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/as3935_i2c/test.rp2040-ard.yaml b/tests/components/as3935_i2c/test.rp2040-ard.yaml index 799acabd5a..b80562ad22 100644 --- a/tests/components/as3935_i2c/test.rp2040-ard.yaml +++ b/tests/components/as3935_i2c/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/as3935_spi/common.yaml b/tests/components/as3935_spi/common.yaml index c3fb93dff1..5898d5d365 100644 --- a/tests/components/as3935_spi/common.yaml +++ b/tests/components/as3935_spi/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_as3935 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - as3935_spi: cs_pin: ${cs_pin} irq_pin: ${irq_pin} diff --git a/tests/components/as3935_spi/test.esp32-c3-idf.yaml b/tests/components/as3935_spi/test.esp32-c3-idf.yaml index fcf35f528e..5e7e2dc82c 100644 --- a/tests/components/as3935_spi/test.esp32-c3-idf.yaml +++ b/tests/components/as3935_spi/test.esp32-c3-idf.yaml @@ -1,8 +1,7 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 irq_pin: GPIO9 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/as3935_spi/test.esp32-idf.yaml b/tests/components/as3935_spi/test.esp32-idf.yaml index e00f522dd4..19791e24b7 100644 --- a/tests/components/as3935_spi/test.esp32-idf.yaml +++ b/tests/components/as3935_spi/test.esp32-idf.yaml @@ -1,8 +1,8 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 irq_pin: GPIO13 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/as3935_spi/test.esp8266-ard.yaml b/tests/components/as3935_spi/test.esp8266-ard.yaml index b90e661ec0..8475dc4c50 100644 --- a/tests/components/as3935_spi/test.esp8266-ard.yaml +++ b/tests/components/as3935_spi/test.esp8266-ard.yaml @@ -1,8 +1,11 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 irq_pin: GPIO5 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/as3935_spi/test.rp2040-ard.yaml b/tests/components/as3935_spi/test.rp2040-ard.yaml index 8f5941e1b2..7c4a74a236 100644 --- a/tests/components/as3935_spi/test.rp2040-ard.yaml +++ b/tests/components/as3935_spi/test.rp2040-ard.yaml @@ -5,4 +5,7 @@ substitutions: irq_pin: GPIO5 cs_pin: GPIO6 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/as5600/common.yaml b/tests/components/as5600/common.yaml index 860f5bf803..d867c66a21 100644 --- a/tests/components/as5600/common.yaml +++ b/tests/components/as5600/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_as5600 - scl: ${scl_pin} - sda: ${sda_pin} - as5600: + i2c_id: i2c_bus dir_pin: ${dir_pin} direction: clockwise start_position: 90deg diff --git a/tests/components/as5600/test.esp32-c3-idf.yaml b/tests/components/as5600/test.esp32-c3-idf.yaml index a0623c91e5..03a87ed6c4 100644 --- a/tests/components/as5600/test.esp32-c3-idf.yaml +++ b/tests/components/as5600/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 dir_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/as5600/test.esp32-idf.yaml b/tests/components/as5600/test.esp32-idf.yaml index fa08763501..9d25a7f09a 100644 --- a/tests/components/as5600/test.esp32-idf.yaml +++ b/tests/components/as5600/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 dir_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/as5600/test.esp8266-ard.yaml b/tests/components/as5600/test.esp8266-ard.yaml index 5e27f8c134..8d18740b95 100644 --- a/tests/components/as5600/test.esp8266-ard.yaml +++ b/tests/components/as5600/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 dir_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/as5600/test.rp2040-ard.yaml b/tests/components/as5600/test.rp2040-ard.yaml index a0623c91e5..4bcbb99c81 100644 --- a/tests/components/as5600/test.rp2040-ard.yaml +++ b/tests/components/as5600/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 dir_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/as7341/common.yaml b/tests/components/as7341/common.yaml index 0351b344c6..3f94656c74 100644 --- a/tests/components/as7341/common.yaml +++ b/tests/components/as7341/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_as7341 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: as7341 + i2c_id: i2c_bus update_interval: 15s gain: X8 atime: 120 diff --git a/tests/components/as7341/test.esp32-c3-idf.yaml b/tests/components/as7341/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/as7341/test.esp32-c3-idf.yaml +++ b/tests/components/as7341/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/as7341/test.esp32-idf.yaml b/tests/components/as7341/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/as7341/test.esp32-idf.yaml +++ b/tests/components/as7341/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/as7341/test.esp8266-ard.yaml b/tests/components/as7341/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/as7341/test.esp8266-ard.yaml +++ b/tests/components/as7341/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/as7341/test.rp2040-ard.yaml b/tests/components/as7341/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/as7341/test.rp2040-ard.yaml +++ b/tests/components/as7341/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/at581x/common.yaml b/tests/components/at581x/common.yaml index 018a0fded1..425be47c42 100644 --- a/tests/components/at581x/common.yaml +++ b/tests/components/at581x/common.yaml @@ -16,13 +16,9 @@ esphome: id: waveradar at581x: + i2c_id: i2c_bus id: waveradar -i2c: - - id: i2c_at581x - scl: ${scl_pin} - sda: ${sda_pin} - switch: - platform: at581x name: Enable Radar diff --git a/tests/components/at581x/test.esp32-c3-idf.yaml b/tests/components/at581x/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/at581x/test.esp32-c3-idf.yaml +++ b/tests/components/at581x/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/at581x/test.esp32-idf.yaml b/tests/components/at581x/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/at581x/test.esp32-idf.yaml +++ b/tests/components/at581x/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/at581x/test.esp8266-ard.yaml b/tests/components/at581x/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/at581x/test.esp8266-ard.yaml +++ b/tests/components/at581x/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/at581x/test.rp2040-ard.yaml b/tests/components/at581x/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/at581x/test.rp2040-ard.yaml +++ b/tests/components/at581x/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/atc_mithermometer/test.esp32-c3-idf.yaml b/tests/components/atc_mithermometer/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/atc_mithermometer/test.esp32-c3-idf.yaml +++ b/tests/components/atc_mithermometer/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/atc_mithermometer/test.esp32-idf.yaml b/tests/components/atc_mithermometer/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/atc_mithermometer/test.esp32-idf.yaml +++ b/tests/components/atc_mithermometer/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/atm90e26/common.yaml b/tests/components/atm90e26/common.yaml index 49c3a73ec8..478be7b8b3 100644 --- a/tests/components/atm90e26/common.yaml +++ b/tests/components/atm90e26/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_atm90e26 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: atm90e26 cs_pin: ${cs_pin} diff --git a/tests/components/atm90e26/test.esp32-c3-idf.yaml b/tests/components/atm90e26/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/atm90e26/test.esp32-c3-idf.yaml +++ b/tests/components/atm90e26/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/atm90e26/test.esp32-idf.yaml b/tests/components/atm90e26/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/atm90e26/test.esp32-idf.yaml +++ b/tests/components/atm90e26/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/atm90e26/test.esp8266-ard.yaml b/tests/components/atm90e26/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/atm90e26/test.esp8266-ard.yaml +++ b/tests/components/atm90e26/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/atm90e26/test.rp2040-ard.yaml b/tests/components/atm90e26/test.rp2040-ard.yaml index c8bfab0023..5d0c35c2d2 100644 --- a/tests/components/atm90e26/test.rp2040-ard.yaml +++ b/tests/components/atm90e26/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO6 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/atm90e32/common.yaml b/tests/components/atm90e32/common.yaml index 3eeed8395f..b8b480ab62 100644 --- a/tests/components/atm90e32/common.yaml +++ b/tests/components/atm90e32/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_atm90e32 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: atm90e32 cs_pin: ${cs_pin} diff --git a/tests/components/atm90e32/test.esp32-c3-idf.yaml b/tests/components/atm90e32/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/atm90e32/test.esp32-c3-idf.yaml +++ b/tests/components/atm90e32/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/atm90e32/test.esp32-idf.yaml b/tests/components/atm90e32/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/atm90e32/test.esp32-idf.yaml +++ b/tests/components/atm90e32/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/atm90e32/test.esp8266-ard.yaml b/tests/components/atm90e32/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/atm90e32/test.esp8266-ard.yaml +++ b/tests/components/atm90e32/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/atm90e32/test.rp2040-ard.yaml b/tests/components/atm90e32/test.rp2040-ard.yaml index c8bfab0023..5d0c35c2d2 100644 --- a/tests/components/atm90e32/test.rp2040-ard.yaml +++ b/tests/components/atm90e32/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO6 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/axs15231/common.yaml b/tests/components/axs15231/common.yaml index 1c0c79975f..3f07af80ea 100644 --- a/tests/components/axs15231/common.yaml +++ b/tests/components/axs15231/common.yaml @@ -1,20 +1,18 @@ -i2c: - - id: i2c_axs15231 - scl: 3 - sda: 21 - display: - platform: ssd1306_i2c + i2c_id: i2c_bus id: ssd1306_display model: SSD1306_128X64 reset_pin: 19 pages: - - id: page1 + - id: axs15231_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); touchscreen: - platform: axs15231 + i2c_id: i2c_bus + id: axs15231_touchscreen display: ssd1306_display interrupt_pin: 20 reset_pin: 18 diff --git a/tests/components/axs15231/test.esp32-c3-idf.yaml b/tests/components/axs15231/test.esp32-c3-idf.yaml index dade44d145..9990d96d29 100644 --- a/tests/components/axs15231/test.esp32-c3-idf.yaml +++ b/tests/components/axs15231/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/axs15231/test.esp32-idf.yaml b/tests/components/axs15231/test.esp32-idf.yaml index dade44d145..b47e39c389 100644 --- a/tests/components/axs15231/test.esp32-idf.yaml +++ b/tests/components/axs15231/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/axs15231/test.esp8266-ard.yaml b/tests/components/axs15231/test.esp8266-ard.yaml index c09d139574..eb599da773 100644 --- a/tests/components/axs15231/test.esp8266-ard.yaml +++ b/tests/components/axs15231/test.esp8266-ard.yaml @@ -1,10 +1,9 @@ -i2c: - - id: i2c_axs15231 - scl: 5 - sda: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml display: - platform: ssd1306_i2c + i2c_id: i2c_bus id: ssd1306_display model: SSD1306_128X64 reset_pin: 13 @@ -15,5 +14,6 @@ display: touchscreen: - platform: axs15231 + i2c_id: i2c_bus display: ssd1306_display interrupt_pin: 12 diff --git a/tests/components/axs15231/test.rp2040-ard.yaml b/tests/components/axs15231/test.rp2040-ard.yaml index dade44d145..319a7c71a6 100644 --- a/tests/components/axs15231/test.rp2040-ard.yaml +++ b/tests/components/axs15231/test.rp2040-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/b_parasite/test.esp32-c3-idf.yaml b/tests/components/b_parasite/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/b_parasite/test.esp32-c3-idf.yaml +++ b/tests/components/b_parasite/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/b_parasite/test.esp32-idf.yaml b/tests/components/b_parasite/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/b_parasite/test.esp32-idf.yaml +++ b/tests/components/b_parasite/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/bedjet/test.esp32-c3-idf.yaml b/tests/components/bedjet/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/bedjet/test.esp32-c3-idf.yaml +++ b/tests/components/bedjet/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/bedjet/test.esp32-idf.yaml b/tests/components/bedjet/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/bedjet/test.esp32-idf.yaml +++ b/tests/components/bedjet/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/bh1750/common.yaml b/tests/components/bh1750/common.yaml index c0e0bc1c59..46ea99b7e3 100644 --- a/tests/components/bh1750/common.yaml +++ b/tests/components/bh1750/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_bh1750 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: bh1750 + i2c_id: i2c_bus name: Living Room Brightness address: 0x23 update_interval: 30s diff --git a/tests/components/bh1750/test.esp32-c3-idf.yaml b/tests/components/bh1750/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/bh1750/test.esp32-c3-idf.yaml +++ b/tests/components/bh1750/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bh1750/test.esp32-idf.yaml b/tests/components/bh1750/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/bh1750/test.esp32-idf.yaml +++ b/tests/components/bh1750/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/bh1750/test.esp8266-ard.yaml b/tests/components/bh1750/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/bh1750/test.esp8266-ard.yaml +++ b/tests/components/bh1750/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/bh1750/test.rp2040-ard.yaml b/tests/components/bh1750/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/bh1750/test.rp2040-ard.yaml +++ b/tests/components/bh1750/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/bl0906/common.yaml b/tests/components/bl0906/common.yaml index 29b82a5958..006aa682f1 100644 --- a/tests/components/bl0906/common.yaml +++ b/tests/components/bl0906/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_bl0906 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 19200 - sensor: - platform: bl0906 id: bl diff --git a/tests/components/bl0906/test.esp32-c3-idf.yaml b/tests/components/bl0906/test.esp32-c3-idf.yaml index c79d14c740..147d967dd4 100644 --- a/tests/components/bl0906/test.esp32-c3-idf.yaml +++ b/tests/components/bl0906/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bl0906/test.esp32-idf.yaml b/tests/components/bl0906/test.esp32-idf.yaml index 811f6b72a6..76222997a8 100644 --- a/tests/components/bl0906/test.esp32-idf.yaml +++ b/tests/components/bl0906/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/bl0906/test.esp8266-ard.yaml b/tests/components/bl0906/test.esp8266-ard.yaml index 3b44f9c9c3..ac781ea834 100644 --- a/tests/components/bl0906/test.esp8266-ard.yaml +++ b/tests/components/bl0906/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO1 rx_pin: GPIO3 +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bl0906/test.rp2040-ard.yaml b/tests/components/bl0906/test.rp2040-ard.yaml index b516342f3b..f4dada6605 100644 --- a/tests/components/bl0906/test.rp2040-ard.yaml +++ b/tests/components/bl0906/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bl0939/common.yaml b/tests/components/bl0939/common.yaml index 7a6b635b70..a47aa05606 100644 --- a/tests/components/bl0939/common.yaml +++ b/tests/components/bl0939/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_bl0939 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: bl0939 voltage: diff --git a/tests/components/bl0939/test.esp32-c3-idf.yaml b/tests/components/bl0939/test.esp32-c3-idf.yaml index c79d14c740..4b7c8351a7 100644 --- a/tests/components/bl0939/test.esp32-c3-idf.yaml +++ b/tests/components/bl0939/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bl0939/test.esp32-idf.yaml b/tests/components/bl0939/test.esp32-idf.yaml index 811f6b72a6..64baa4ec9d 100644 --- a/tests/components/bl0939/test.esp32-idf.yaml +++ b/tests/components/bl0939/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO12 rx_pin: GPIO14 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/bl0939/test.esp8266-ard.yaml b/tests/components/bl0939/test.esp8266-ard.yaml index 3b44f9c9c3..89ca3ab5ae 100644 --- a/tests/components/bl0939/test.esp8266-ard.yaml +++ b/tests/components/bl0939/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO1 rx_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bl0939/test.rp2040-ard.yaml b/tests/components/bl0939/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/bl0939/test.rp2040-ard.yaml +++ b/tests/components/bl0939/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bl0940/common.yaml b/tests/components/bl0940/common.yaml index 443f3b0ff0..0b73fd6d55 100644 --- a/tests/components/bl0940/common.yaml +++ b/tests/components/bl0940/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_bl0939 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - button: - platform: bl0940 bl0940_id: test_id diff --git a/tests/components/bl0940/test.esp32-c3-idf.yaml b/tests/components/bl0940/test.esp32-c3-idf.yaml index c79d14c740..4b7c8351a7 100644 --- a/tests/components/bl0940/test.esp32-c3-idf.yaml +++ b/tests/components/bl0940/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bl0940/test.esp32-idf.yaml b/tests/components/bl0940/test.esp32-idf.yaml index 811f6b72a6..64baa4ec9d 100644 --- a/tests/components/bl0940/test.esp32-idf.yaml +++ b/tests/components/bl0940/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO12 rx_pin: GPIO14 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/bl0940/test.esp8266-ard.yaml b/tests/components/bl0940/test.esp8266-ard.yaml index 3b44f9c9c3..89ca3ab5ae 100644 --- a/tests/components/bl0940/test.esp8266-ard.yaml +++ b/tests/components/bl0940/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO1 rx_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bl0940/test.rp2040-ard.yaml b/tests/components/bl0940/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/bl0940/test.rp2040-ard.yaml +++ b/tests/components/bl0940/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bl0942/common.yaml b/tests/components/bl0942/common.yaml index 32da24885f..1aaab8bb86 100644 --- a/tests/components/bl0942/common.yaml +++ b/tests/components/bl0942/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_bl0939 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: bl0942 reset: true diff --git a/tests/components/bl0942/test.bk72xx-ard.yaml b/tests/components/bl0942/test.bk72xx-ard.yaml index 96e13c83a9..0caf71ba1f 100644 --- a/tests/components/bl0942/test.bk72xx-ard.yaml +++ b/tests/components/bl0942/test.bk72xx-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: TX1 - rx_pin: RX1 +packages: + uart: !include ../../test_build_components/common/uart/bk72xx-ard.yaml <<: !include common.yaml diff --git a/tests/components/bl0942/test.esp32-c3-idf.yaml b/tests/components/bl0942/test.esp32-c3-idf.yaml index c79d14c740..4b7c8351a7 100644 --- a/tests/components/bl0942/test.esp32-c3-idf.yaml +++ b/tests/components/bl0942/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bl0942/test.esp32-idf.yaml b/tests/components/bl0942/test.esp32-idf.yaml index 811f6b72a6..64baa4ec9d 100644 --- a/tests/components/bl0942/test.esp32-idf.yaml +++ b/tests/components/bl0942/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO12 rx_pin: GPIO14 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/bl0942/test.esp8266-ard.yaml b/tests/components/bl0942/test.esp8266-ard.yaml index 3b44f9c9c3..89ca3ab5ae 100644 --- a/tests/components/bl0942/test.esp8266-ard.yaml +++ b/tests/components/bl0942/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO1 rx_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bl0942/test.rp2040-ard.yaml b/tests/components/bl0942/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/bl0942/test.rp2040-ard.yaml +++ b/tests/components/bl0942/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ble_client/test.esp32-c3-idf.yaml b/tests/components/ble_client/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/ble_client/test.esp32-c3-idf.yaml +++ b/tests/components/ble_client/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ble_client/test.esp32-idf.yaml b/tests/components/ble_client/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/ble_client/test.esp32-idf.yaml +++ b/tests/components/ble_client/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ble_presence/test.esp32-c3-idf.yaml b/tests/components/ble_presence/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/ble_presence/test.esp32-c3-idf.yaml +++ b/tests/components/ble_presence/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ble_presence/test.esp32-idf.yaml b/tests/components/ble_presence/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/ble_presence/test.esp32-idf.yaml +++ b/tests/components/ble_presence/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ble_rssi/test.esp32-c3-idf.yaml b/tests/components/ble_rssi/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/ble_rssi/test.esp32-c3-idf.yaml +++ b/tests/components/ble_rssi/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ble_rssi/test.esp32-idf.yaml b/tests/components/ble_rssi/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/ble_rssi/test.esp32-idf.yaml +++ b/tests/components/ble_rssi/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ble_scanner/test.esp32-c3-idf.yaml b/tests/components/ble_scanner/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/ble_scanner/test.esp32-c3-idf.yaml +++ b/tests/components/ble_scanner/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ble_scanner/test.esp32-idf.yaml b/tests/components/ble_scanner/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/ble_scanner/test.esp32-idf.yaml +++ b/tests/components/ble_scanner/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/bme280_i2c/common.yaml b/tests/components/bme280_i2c/common.yaml index e74ce9bf6d..e6d41d209c 100644 --- a/tests/components/bme280_i2c/common.yaml +++ b/tests/components/bme280_i2c/common.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_bme280 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: bme280_i2c - i2c_id: i2c_bme280 + i2c_id: i2c_bus address: 0x76 temperature: id: bme280_temperature diff --git a/tests/components/bme280_i2c/test.esp32-c3-idf.yaml b/tests/components/bme280_i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/bme280_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/bme280_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bme280_i2c/test.esp32-idf.yaml b/tests/components/bme280_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/bme280_i2c/test.esp32-idf.yaml +++ b/tests/components/bme280_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/bme280_i2c/test.esp8266-ard.yaml b/tests/components/bme280_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/bme280_i2c/test.esp8266-ard.yaml +++ b/tests/components/bme280_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/bme280_i2c/test.rp2040-ard.yaml b/tests/components/bme280_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/bme280_i2c/test.rp2040-ard.yaml +++ b/tests/components/bme280_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/bme280_spi/common.yaml b/tests/components/bme280_spi/common.yaml index 303ecf9f73..9a50b410fb 100644 --- a/tests/components/bme280_spi/common.yaml +++ b/tests/components/bme280_spi/common.yaml @@ -1,12 +1,5 @@ -spi: - - id: spi_bme280 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: bme280_spi - spi_id: spi_bme280 cs_pin: ${cs_pin} temperature: id: bme280_temperature diff --git a/tests/components/bme280_spi/test.esp32-c3-idf.yaml b/tests/components/bme280_spi/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/bme280_spi/test.esp32-c3-idf.yaml +++ b/tests/components/bme280_spi/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bme280_spi/test.esp32-idf.yaml b/tests/components/bme280_spi/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/bme280_spi/test.esp32-idf.yaml +++ b/tests/components/bme280_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/bme280_spi/test.esp8266-ard.yaml b/tests/components/bme280_spi/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/bme280_spi/test.esp8266-ard.yaml +++ b/tests/components/bme280_spi/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bme280_spi/test.rp2040-ard.yaml b/tests/components/bme280_spi/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/bme280_spi/test.rp2040-ard.yaml +++ b/tests/components/bme280_spi/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bme680/common.yaml b/tests/components/bme680/common.yaml index 13a42488f2..d5a7267060 100644 --- a/tests/components/bme680/common.yaml +++ b/tests/components/bme680/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_bme680 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: bme680 + i2c_id: i2c_bus temperature: name: BME680 Temperature oversampling: 16x diff --git a/tests/components/bme680/test.esp32-c3-idf.yaml b/tests/components/bme680/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/bme680/test.esp32-c3-idf.yaml +++ b/tests/components/bme680/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bme680/test.esp32-idf.yaml b/tests/components/bme680/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/bme680/test.esp32-idf.yaml +++ b/tests/components/bme680/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/bme680/test.esp8266-ard.yaml b/tests/components/bme680/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/bme680/test.esp8266-ard.yaml +++ b/tests/components/bme680/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/bme680/test.rp2040-ard.yaml b/tests/components/bme680/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/bme680/test.rp2040-ard.yaml +++ b/tests/components/bme680/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/bme680_bsec/common.yaml b/tests/components/bme680_bsec/common.yaml index 7d2e9e210b..1a78ab2ae0 100644 --- a/tests/components/bme680_bsec/common.yaml +++ b/tests/components/bme680_bsec/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_bme680 - scl: ${scl_pin} - sda: ${sda_pin} - bme680_bsec: + i2c_id: i2c_bus address: 0x77 sensor: diff --git a/tests/components/bme680_bsec/test.esp32-ard.yaml b/tests/components/bme680_bsec/test.esp32-ard.yaml index 3b761d3fc1..7c503b0ccb 100644 --- a/tests/components/bme680_bsec/test.esp32-ard.yaml +++ b/tests/components/bme680_bsec/test.esp32-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml <<: !include common.yaml diff --git a/tests/components/bme680_bsec/test.esp8266-ard.yaml b/tests/components/bme680_bsec/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/bme680_bsec/test.esp8266-ard.yaml +++ b/tests/components/bme680_bsec/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/common.yaml b/tests/components/bme68x_bsec2_i2c/common.yaml index b8a16ee7bb..bee964f433 100644 --- a/tests/components/bme68x_bsec2_i2c/common.yaml +++ b/tests/components/bme68x_bsec2_i2c/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_bme68x - scl: ${scl_pin} - sda: ${sda_pin} - bme68x_bsec2_i2c: + i2c_id: i2c_bus address: 0x76 model: bme688 algorithm_output: classification diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml index 84a9dd4bb4..9990d96d29 100644 --- a/tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO6 - sda_pin: GPIO7 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-idf.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/bme68x_bsec2_i2c/test.esp32-idf.yaml +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-s2-idf.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-s2-idf.yaml index 63c3bd6afd..54f59a59fc 100644 --- a/tests/components/bme68x_bsec2_i2c/test.esp32-s2-idf.yaml +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-s2-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-s2-idf.yaml <<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml index 63c3bd6afd..0fd8684a2c 100644 --- a/tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp8266-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/bme68x_bsec2_i2c/test.esp8266-ard.yaml +++ b/tests/components/bme68x_bsec2_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.rp2040-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/bme68x_bsec2_i2c/test.rp2040-ard.yaml +++ b/tests/components/bme68x_bsec2_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/bmi160/common.yaml b/tests/components/bmi160/common.yaml index 6aa9aa6ed0..7375732db2 100644 --- a/tests/components/bmi160/common.yaml +++ b/tests/components/bmi160/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_bmi160 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: bmi160 + i2c_id: i2c_bus address: 0x68 acceleration_x: name: BMI160 Accel X diff --git a/tests/components/bmi160/test.esp32-c3-idf.yaml b/tests/components/bmi160/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/bmi160/test.esp32-c3-idf.yaml +++ b/tests/components/bmi160/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmi160/test.esp32-idf.yaml b/tests/components/bmi160/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/bmi160/test.esp32-idf.yaml +++ b/tests/components/bmi160/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmi160/test.esp8266-ard.yaml b/tests/components/bmi160/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/bmi160/test.esp8266-ard.yaml +++ b/tests/components/bmi160/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/bmi160/test.rp2040-ard.yaml b/tests/components/bmi160/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/bmi160/test.rp2040-ard.yaml +++ b/tests/components/bmi160/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/bmp085/common.yaml b/tests/components/bmp085/common.yaml index 219bc51fbb..ad358f4409 100644 --- a/tests/components/bmp085/common.yaml +++ b/tests/components/bmp085/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_bmp085 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: bmp085 + i2c_id: i2c_bus temperature: name: Outside Temperature pressure: diff --git a/tests/components/bmp085/test.esp32-c3-idf.yaml b/tests/components/bmp085/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/bmp085/test.esp32-c3-idf.yaml +++ b/tests/components/bmp085/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmp085/test.esp32-idf.yaml b/tests/components/bmp085/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/bmp085/test.esp32-idf.yaml +++ b/tests/components/bmp085/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmp085/test.esp8266-ard.yaml b/tests/components/bmp085/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/bmp085/test.esp8266-ard.yaml +++ b/tests/components/bmp085/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/bmp085/test.rp2040-ard.yaml b/tests/components/bmp085/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/bmp085/test.rp2040-ard.yaml +++ b/tests/components/bmp085/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/bmp280_i2c/common.yaml b/tests/components/bmp280_i2c/common.yaml index edf52b2cd4..785343de7d 100644 --- a/tests/components/bmp280_i2c/common.yaml +++ b/tests/components/bmp280_i2c/common.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_bmp280 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: bmp280_i2c - i2c_id: i2c_bmp280 + i2c_id: i2c_bus address: 0x77 temperature: id: bmp280_temperature diff --git a/tests/components/bmp280_i2c/test.esp32-c3-idf.yaml b/tests/components/bmp280_i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/bmp280_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/bmp280_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.esp32-idf.yaml b/tests/components/bmp280_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/bmp280_i2c/test.esp32-idf.yaml +++ b/tests/components/bmp280_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.esp8266-ard.yaml b/tests/components/bmp280_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/bmp280_i2c/test.esp8266-ard.yaml +++ b/tests/components/bmp280_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.rp2040-ard.yaml b/tests/components/bmp280_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/bmp280_i2c/test.rp2040-ard.yaml +++ b/tests/components/bmp280_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/bmp280_spi/common.yaml b/tests/components/bmp280_spi/common.yaml index 798804de5b..fa88967ca4 100644 --- a/tests/components/bmp280_spi/common.yaml +++ b/tests/components/bmp280_spi/common.yaml @@ -1,12 +1,5 @@ -spi: - - id: spi_bmp280 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: bmp280_spi - spi_id: spi_bmp280 cs_pin: ${cs_pin} temperature: id: bmp280_temperature diff --git a/tests/components/bmp280_spi/test.esp32-c3-idf.yaml b/tests/components/bmp280_spi/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/bmp280_spi/test.esp32-c3-idf.yaml +++ b/tests/components/bmp280_spi/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.esp32-idf.yaml b/tests/components/bmp280_spi/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/bmp280_spi/test.esp32-idf.yaml +++ b/tests/components/bmp280_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.esp8266-ard.yaml b/tests/components/bmp280_spi/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/bmp280_spi/test.esp8266-ard.yaml +++ b/tests/components/bmp280_spi/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.rp2040-ard.yaml b/tests/components/bmp280_spi/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/bmp280_spi/test.rp2040-ard.yaml +++ b/tests/components/bmp280_spi/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bmp3xx_i2c/common.yaml b/tests/components/bmp3xx_i2c/common.yaml index 6641b7a1b8..ebc4921b84 100644 --- a/tests/components/bmp3xx_i2c/common.yaml +++ b/tests/components/bmp3xx_i2c/common.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_bmp3xx - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: bmp3xx_i2c - i2c_id: i2c_bmp3xx + i2c_id: i2c_bus address: 0x77 temperature: name: BMP Temperature diff --git a/tests/components/bmp3xx_i2c/test.esp32-c3-idf.yaml b/tests/components/bmp3xx_i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/bmp3xx_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/bmp3xx_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmp3xx_i2c/test.esp32-idf.yaml b/tests/components/bmp3xx_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/bmp3xx_i2c/test.esp32-idf.yaml +++ b/tests/components/bmp3xx_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmp3xx_i2c/test.esp8266-ard.yaml b/tests/components/bmp3xx_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/bmp3xx_i2c/test.esp8266-ard.yaml +++ b/tests/components/bmp3xx_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/bmp3xx_i2c/test.rp2040-ard.yaml b/tests/components/bmp3xx_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/bmp3xx_i2c/test.rp2040-ard.yaml +++ b/tests/components/bmp3xx_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/bmp3xx_spi/common.yaml b/tests/components/bmp3xx_spi/common.yaml index 8d5f897661..d6acef1833 100644 --- a/tests/components/bmp3xx_spi/common.yaml +++ b/tests/components/bmp3xx_spi/common.yaml @@ -1,12 +1,5 @@ -spi: - - id: spi_bmp3xx - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: bmp3xx_spi - spi_id: spi_bmp3xx cs_pin: ${cs_pin} temperature: name: BMP Temperature diff --git a/tests/components/bmp3xx_spi/test.esp32-c3-idf.yaml b/tests/components/bmp3xx_spi/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/bmp3xx_spi/test.esp32-c3-idf.yaml +++ b/tests/components/bmp3xx_spi/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmp3xx_spi/test.esp32-idf.yaml b/tests/components/bmp3xx_spi/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/bmp3xx_spi/test.esp32-idf.yaml +++ b/tests/components/bmp3xx_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/bmp3xx_spi/test.esp8266-ard.yaml b/tests/components/bmp3xx_spi/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/bmp3xx_spi/test.esp8266-ard.yaml +++ b/tests/components/bmp3xx_spi/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bmp3xx_spi/test.rp2040-ard.yaml b/tests/components/bmp3xx_spi/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/bmp3xx_spi/test.rp2040-ard.yaml +++ b/tests/components/bmp3xx_spi/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/bmp581/common.yaml b/tests/components/bmp581/common.yaml index 71ad4bfb1a..250b1f5857 100644 --- a/tests/components/bmp581/common.yaml +++ b/tests/components/bmp581/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_bmp581 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: bmp581 + i2c_id: i2c_bus temperature: name: BMP581 Temperature iir_filter: 2x diff --git a/tests/components/bmp581/test.esp32-c3-idf.yaml b/tests/components/bmp581/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/bmp581/test.esp32-c3-idf.yaml +++ b/tests/components/bmp581/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmp581/test.esp32-idf.yaml b/tests/components/bmp581/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/bmp581/test.esp32-idf.yaml +++ b/tests/components/bmp581/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/bmp581/test.esp8266-ard.yaml b/tests/components/bmp581/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/bmp581/test.esp8266-ard.yaml +++ b/tests/components/bmp581/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/bmp581/test.rp2040-ard.yaml b/tests/components/bmp581/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/bmp581/test.rp2040-ard.yaml +++ b/tests/components/bmp581/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/bp1658cj/test.esp32-idf.yaml b/tests/components/bp1658cj/test.esp32-idf.yaml index d295973e3f..a4ecdb6c49 100644 --- a/tests/components/bp1658cj/test.esp32-idf.yaml +++ b/tests/components/bp1658cj/test.esp32-idf.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 + clock_pin: GPIO4 + data_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/bp1658cj/test.esp8266-ard.yaml b/tests/components/bp1658cj/test.esp8266-ard.yaml index 7808481215..7c7f1e1a11 100644 --- a/tests/components/bp1658cj/test.esp8266-ard.yaml +++ b/tests/components/bp1658cj/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 + clock_pin: GPIO0 + data_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/bp5758d/test.esp32-idf.yaml b/tests/components/bp5758d/test.esp32-idf.yaml index d295973e3f..a4ecdb6c49 100644 --- a/tests/components/bp5758d/test.esp32-idf.yaml +++ b/tests/components/bp5758d/test.esp32-idf.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 + clock_pin: GPIO4 + data_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/bp5758d/test.esp8266-ard.yaml b/tests/components/bp5758d/test.esp8266-ard.yaml index 7808481215..7c7f1e1a11 100644 --- a/tests/components/bp5758d/test.esp8266-ard.yaml +++ b/tests/components/bp5758d/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 + clock_pin: GPIO0 + data_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/camera/common.yaml b/tests/components/camera/common.yaml index 3daf1e8565..76cca4cf94 100644 --- a/tests/components/camera/common.yaml +++ b/tests/components/camera/common.yaml @@ -1,18 +1,2 @@ -esphome: - includes: - - ../../../esphome/components/camera/ - -script: - - id: interface_compile_check - then: - - lambda: |- - using namespace esphome::camera; - class MockCamera : public Camera { - public: - void add_image_callback(std::function)> &&callback) override {} - CameraImageReader *create_image_reader() override { return 0; } - void request_image(CameraRequester requester) override {} - void start_stream(CameraRequester requester) override {} - void stop_stream(CameraRequester requester) override {} - }; - MockCamera* camera = new MockCamera(); +# Camera is a base component auto-loaded by esp32_camera +# The hardware configuration comes from the camera package diff --git a/tests/components/camera/test.esp32-idf.yaml b/tests/components/camera/test.esp32-idf.yaml index dade44d145..3d93dd6418 100644 --- a/tests/components/camera/test.esp32-idf.yaml +++ b/tests/components/camera/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + camera: !include ../../test_build_components/common/camera/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/camera_encoder/test.esp32-idf.yaml b/tests/components/camera_encoder/test.esp32-idf.yaml index dade44d145..3d93dd6418 100644 --- a/tests/components/camera_encoder/test.esp32-idf.yaml +++ b/tests/components/camera_encoder/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + camera: !include ../../test_build_components/common/camera/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/canbus/test.esp32-idf.yaml b/tests/components/canbus/test.esp32-idf.yaml index dade44d145..2d29656c94 100644 --- a/tests/components/canbus/test.esp32-idf.yaml +++ b/tests/components/canbus/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/cap1188/common.yaml b/tests/components/cap1188/common.yaml index e83bf5d5d2..3e4ed972ff 100644 --- a/tests/components/cap1188/common.yaml +++ b/tests/components/cap1188/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_cap1188 - scl: ${scl_pin} - sda: ${sda_pin} - cap1188: id: cap1188_component + i2c_id: i2c_bus address: 0x29 reset_pin: ${reset_pin} touch_threshold: 0x20 diff --git a/tests/components/cap1188/test.esp32-c3-idf.yaml b/tests/components/cap1188/test.esp32-c3-idf.yaml index 1e6670c196..c97f30d52c 100644 --- a/tests/components/cap1188/test.esp32-c3-idf.yaml +++ b/tests/components/cap1188/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/cap1188/test.esp32-idf.yaml b/tests/components/cap1188/test.esp32-idf.yaml index 1ca773e06c..4ff2241ec9 100644 --- a/tests/components/cap1188/test.esp32-idf.yaml +++ b/tests/components/cap1188/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/cap1188/test.esp8266-ard.yaml b/tests/components/cap1188/test.esp8266-ard.yaml index dfdc12a3d1..b8bb94edde 100644 --- a/tests/components/cap1188/test.esp8266-ard.yaml +++ b/tests/components/cap1188/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/cap1188/test.rp2040-ard.yaml b/tests/components/cap1188/test.rp2040-ard.yaml index 1e6670c196..1bf10642c5 100644 --- a/tests/components/cap1188/test.rp2040-ard.yaml +++ b/tests/components/cap1188/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ccs811/common.yaml b/tests/components/ccs811/common.yaml index a781996c66..0d912fd3ac 100644 --- a/tests/components/ccs811/common.yaml +++ b/tests/components/ccs811/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ccs811 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ccs811 + i2c_id: i2c_bus eco2: name: CCS811 eCO2 tvoc: diff --git a/tests/components/ccs811/test.esp32-c3-idf.yaml b/tests/components/ccs811/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ccs811/test.esp32-c3-idf.yaml +++ b/tests/components/ccs811/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ccs811/test.esp32-idf.yaml b/tests/components/ccs811/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ccs811/test.esp32-idf.yaml +++ b/tests/components/ccs811/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ccs811/test.esp8266-ard.yaml b/tests/components/ccs811/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ccs811/test.esp8266-ard.yaml +++ b/tests/components/ccs811/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ccs811/test.rp2040-ard.yaml b/tests/components/ccs811/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ccs811/test.rp2040-ard.yaml +++ b/tests/components/ccs811/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ch422g/common.yaml b/tests/components/ch422g/common.yaml index d65956ecac..ad3707eee0 100644 --- a/tests/components/ch422g/common.yaml +++ b/tests/components/ch422g/common.yaml @@ -1,5 +1,6 @@ ch422g: - id: ch422g_hub + i2c_id: i2c_bus binary_sensor: - platform: gpio diff --git a/tests/components/ch422g/test.esp32-c3-idf.yaml b/tests/components/ch422g/test.esp32-c3-idf.yaml index cd822cb308..9990d96d29 100644 --- a/tests/components/ch422g/test.esp32-c3-idf.yaml +++ b/tests/components/ch422g/test.esp32-c3-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_ch422g - scl: 5 - sda: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ch422g/test.esp32-idf.yaml b/tests/components/ch422g/test.esp32-idf.yaml index cd3f1bbeef..b47e39c389 100644 --- a/tests/components/ch422g/test.esp32-idf.yaml +++ b/tests/components/ch422g/test.esp32-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_ch422g - scl: 16 - sda: 17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ch422g/test.esp8266-ard.yaml b/tests/components/ch422g/test.esp8266-ard.yaml index cd822cb308..4a98b9388a 100644 --- a/tests/components/ch422g/test.esp8266-ard.yaml +++ b/tests/components/ch422g/test.esp8266-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_ch422g - scl: 5 - sda: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ch422g/test.rp2040-ard.yaml b/tests/components/ch422g/test.rp2040-ard.yaml index cd822cb308..319a7c71a6 100644 --- a/tests/components/ch422g/test.rp2040-ard.yaml +++ b/tests/components/ch422g/test.rp2040-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_ch422g - scl: 5 - sda: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/chsc6x/test.esp32-c3-idf.yaml b/tests/components/chsc6x/test.esp32-c3-idf.yaml index b0f55eb2e6..f32f147a44 100644 --- a/tests/components/chsc6x/test.esp32-c3-idf.yaml +++ b/tests/components/chsc6x/test.esp32-c3-idf.yaml @@ -1,19 +1,14 @@ -i2c: - - id: i2c_chsc6x - scl: 3 - sda: 9 - -spi: - clk_pin: 5 - mosi_pin: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml display: - platform: ili9xxx id: ili9xxx_display model: GC9A01A invert_colors: True - cs_pin: 18 - dc_pin: 19 + cs_pin: 10 + dc_pin: 6 pages: - id: page1 lambda: |- diff --git a/tests/components/chsc6x/test.esp32-idf.yaml b/tests/components/chsc6x/test.esp32-idf.yaml index 9bc58b66f6..ea3686d8bd 100644 --- a/tests/components/chsc6x/test.esp32-idf.yaml +++ b/tests/components/chsc6x/test.esp32-idf.yaml @@ -1,19 +1,14 @@ -i2c: - - id: i2c_chsc6x - scl: 3 - sda: 21 - -spi: - clk_pin: 16 - mosi_pin: 17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml display: - platform: ili9xxx id: ili9xxx_display model: GC9A01A invert_colors: True - cs_pin: 18 - dc_pin: 19 + cs_pin: 22 + dc_pin: 21 pages: - id: page1 lambda: |- diff --git a/tests/components/chsc6x/test.rp2040-ard.yaml b/tests/components/chsc6x/test.rp2040-ard.yaml index dbd0d59fc4..89cc1b7477 100644 --- a/tests/components/chsc6x/test.rp2040-ard.yaml +++ b/tests/components/chsc6x/test.rp2040-ard.yaml @@ -1,19 +1,14 @@ -i2c: - - id: i2c_chsc6x - scl: 1 - sda: 0 - -spi: - clk_pin: 2 - mosi_pin: 3 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml display: - platform: ili9xxx id: ili9xxx_display model: GC9A01A invert_colors: True - cs_pin: 18 - dc_pin: 19 + cs_pin: 20 + dc_pin: 21 pages: - id: page1 lambda: |- @@ -22,4 +17,4 @@ display: touchscreen: - platform: chsc6x display: ili9xxx_display - interrupt_pin: 20 + interrupt_pin: 22 diff --git a/tests/components/cm1106/common.yaml b/tests/components/cm1106/common.yaml index a01e78024e..ed2fd67007 100644 --- a/tests/components/cm1106/common.yaml +++ b/tests/components/cm1106/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_cm1106 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: cm1106 co2: diff --git a/tests/components/cm1106/test.esp32-c3-idf.yaml b/tests/components/cm1106/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/cm1106/test.esp32-c3-idf.yaml +++ b/tests/components/cm1106/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/cm1106/test.esp32-idf.yaml b/tests/components/cm1106/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/cm1106/test.esp32-idf.yaml +++ b/tests/components/cm1106/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/cm1106/test.esp8266-ard.yaml b/tests/components/cm1106/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/cm1106/test.esp8266-ard.yaml +++ b/tests/components/cm1106/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/cm1106/test.rp2040-ard.yaml b/tests/components/cm1106/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/cm1106/test.rp2040-ard.yaml +++ b/tests/components/cm1106/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/const/common.yaml b/tests/components/const/common.yaml index f4b15f2b90..109db65b63 100644 --- a/tests/components/const/common.yaml +++ b/tests/components/const/common.yaml @@ -1,9 +1,3 @@ -spi: - id: quad_spi - clk_pin: 15 - type: quad - data_pins: [14, 10, 16, 12] - display: - platform: qspi_dbi model: RM690B0 diff --git a/tests/components/const/test.esp32-s3-idf.yaml b/tests/components/const/test.esp32-s3-idf.yaml index dade44d145..c335dee1f3 100644 --- a/tests/components/const/test.esp32-s3-idf.yaml +++ b/tests/components/const/test.esp32-s3-idf.yaml @@ -1 +1,4 @@ +packages: + qspi: !include ../../test_build_components/common/qspi/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/cs5460a/common.yaml b/tests/components/cs5460a/common.yaml index d97b01716b..9ecd934eda 100644 --- a/tests/components/cs5460a/common.yaml +++ b/tests/components/cs5460a/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_cs5460a - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: cs5460a id: cs5460a1 diff --git a/tests/components/cs5460a/test.esp32-c3-idf.yaml b/tests/components/cs5460a/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/cs5460a/test.esp32-c3-idf.yaml +++ b/tests/components/cs5460a/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/cs5460a/test.esp32-idf.yaml b/tests/components/cs5460a/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/cs5460a/test.esp32-idf.yaml +++ b/tests/components/cs5460a/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/cs5460a/test.esp8266-ard.yaml b/tests/components/cs5460a/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/cs5460a/test.esp8266-ard.yaml +++ b/tests/components/cs5460a/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/cs5460a/test.rp2040-ard.yaml b/tests/components/cs5460a/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/cs5460a/test.rp2040-ard.yaml +++ b/tests/components/cs5460a/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/cse7761/common.yaml b/tests/components/cse7761/common.yaml index 60cce3864a..77b19957b4 100644 --- a/tests/components/cse7761/common.yaml +++ b/tests/components/cse7761/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_cse7761 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 38400 - sensor: - platform: cse7761 voltage: diff --git a/tests/components/cse7761/test.esp32-c3-idf.yaml b/tests/components/cse7761/test.esp32-c3-idf.yaml index c79d14c740..4e11c6e7cb 100644 --- a/tests/components/cse7761/test.esp32-c3-idf.yaml +++ b/tests/components/cse7761/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 +packages: + uart_38400: !include ../../test_build_components/common/uart_38400/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/cse7761/test.esp32-idf.yaml b/tests/components/cse7761/test.esp32-idf.yaml index 811f6b72a6..a6a8fee7e9 100644 --- a/tests/components/cse7761/test.esp32-idf.yaml +++ b/tests/components/cse7761/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO12 rx_pin: GPIO14 +packages: + uart_38400: !include ../../test_build_components/common/uart_38400/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/cse7761/test.esp8266-ard.yaml b/tests/components/cse7761/test.esp8266-ard.yaml index 3b44f9c9c3..134274ffb8 100644 --- a/tests/components/cse7761/test.esp8266-ard.yaml +++ b/tests/components/cse7761/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO1 rx_pin: GPIO3 +packages: + uart_38400: !include ../../test_build_components/common/uart_38400/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/cse7761/test.rp2040-ard.yaml b/tests/components/cse7761/test.rp2040-ard.yaml index b516342f3b..b813e0f7f1 100644 --- a/tests/components/cse7761/test.rp2040-ard.yaml +++ b/tests/components/cse7761/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart_38400: !include ../../test_build_components/common/uart_38400/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/cse7766/common.yaml b/tests/components/cse7766/common.yaml index f12b135a77..6db19691f1 100644 --- a/tests/components/cse7766/common.yaml +++ b/tests/components/cse7766/common.yaml @@ -1,10 +1,3 @@ -uart: - - id: uart_cse7766 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 4800 - parity: EVEN - sensor: - platform: cse7766 voltage: diff --git a/tests/components/cse7766/test.esp32-c3-idf.yaml b/tests/components/cse7766/test.esp32-c3-idf.yaml index c79d14c740..dc95c985c7 100644 --- a/tests/components/cse7766/test.esp32-c3-idf.yaml +++ b/tests/components/cse7766/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 +packages: + uart_4800_even: !include ../../test_build_components/common/uart_4800_even/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/cse7766/test.esp32-idf.yaml b/tests/components/cse7766/test.esp32-idf.yaml index 811f6b72a6..911b867708 100644 --- a/tests/components/cse7766/test.esp32-idf.yaml +++ b/tests/components/cse7766/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 +packages: + uart_4800_even: !include ../../test_build_components/common/uart_4800_even/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/cse7766/test.esp8266-ard.yaml b/tests/components/cse7766/test.esp8266-ard.yaml index 3b44f9c9c3..77e529ca48 100644 --- a/tests/components/cse7766/test.esp8266-ard.yaml +++ b/tests/components/cse7766/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO1 rx_pin: GPIO3 +packages: + uart_4800_even: !include ../../test_build_components/common/uart_4800_even/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/cse7766/test.rp2040-ard.yaml b/tests/components/cse7766/test.rp2040-ard.yaml index b516342f3b..b7056670ef 100644 --- a/tests/components/cse7766/test.rp2040-ard.yaml +++ b/tests/components/cse7766/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart_4800_even: !include ../../test_build_components/common/uart_4800_even/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/cst226/common.yaml b/tests/components/cst226/common.yaml index d0b8ea3a86..79d7e7fd53 100644 --- a/tests/components/cst226/common.yaml +++ b/tests/components/cst226/common.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_cst226 - scl: ${scl_pin} - sda: ${sda_pin} - -spi: - - id: spi_ili9xxx - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - - id: my_display + - id: cst226_display platform: ili9xxx model: ili9342 cs_pin: ${cs_pin} @@ -19,7 +9,9 @@ display: touchscreen: - id: ts_cst226 + i2c_id: i2c_bus platform: cst226 + display: cst226_display interrupt_pin: ${interrupt_pin} reset_pin: ${reset_pin} diff --git a/tests/components/cst226/test.esp32-c3-idf.yaml b/tests/components/cst226/test.esp32-c3-idf.yaml index 2f9bd72882..ffc12867d0 100644 --- a/tests/components/cst226/test.esp32-c3-idf.yaml +++ b/tests/components/cst226/test.esp32-c3-idf.yaml @@ -1,12 +1,11 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - cs_pin: GPIO8 + cs_pin: GPIO7 dc_pin: GPIO9 - disp_reset_pin: GPIO10 - scl_pin: GPIO0 - sda_pin: GPIO1 + disp_reset_pin: GPIO18 interrupt_pin: GPIO2 reset_pin: GPIO3 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/cst226/test.esp32-idf.yaml b/tests/components/cst226/test.esp32-idf.yaml index 11e2c4fd43..984f08db47 100644 --- a/tests/components/cst226/test.esp32-idf.yaml +++ b/tests/components/cst226/test.esp32-idf.yaml @@ -1,12 +1,12 @@ substitutions: - clk_pin: GPIO0 - mosi_pin: GPIO2 cs_pin: GPIO4 dc_pin: GPIO5 disp_reset_pin: GPIO12 - scl_pin: GPIO13 - sda_pin: GPIO14 interrupt_pin: GPIO15 - reset_pin: GPIO16 + reset_pin: GPIO25 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/cst816/common.yaml b/tests/components/cst816/common.yaml index 9400e4eef0..889a477dd2 100644 --- a/tests/components/cst816/common.yaml +++ b/tests/components/cst816/common.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_cst816 - scl: ${scl_pin} - sda: ${sda_pin} - -spi: - - id: spi_ili9xxx - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - - id: my_display + - id: cst816_display platform: ili9xxx dimensions: 480x320 model: ST7796 @@ -25,7 +15,9 @@ display: touchscreen: - id: ts_cst816 + i2c_id: i2c_bus platform: cst816 + display: cst816_display interrupt_pin: ${interrupt_pin} reset_pin: ${reset_pin} skip_probe: false @@ -36,6 +28,7 @@ touchscreen: binary_sensor: - platform: touchscreen + touchscreen_id: ts_cst816 name: Home Button use_raw: true x_min: 0 diff --git a/tests/components/cst816/test.esp32-c3-idf.yaml b/tests/components/cst816/test.esp32-c3-idf.yaml index 2f9bd72882..ffc12867d0 100644 --- a/tests/components/cst816/test.esp32-c3-idf.yaml +++ b/tests/components/cst816/test.esp32-c3-idf.yaml @@ -1,12 +1,11 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - cs_pin: GPIO8 + cs_pin: GPIO7 dc_pin: GPIO9 - disp_reset_pin: GPIO10 - scl_pin: GPIO0 - sda_pin: GPIO1 + disp_reset_pin: GPIO18 interrupt_pin: GPIO2 reset_pin: GPIO3 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/cst816/test.esp32-idf.yaml b/tests/components/cst816/test.esp32-idf.yaml index 11e2c4fd43..984f08db47 100644 --- a/tests/components/cst816/test.esp32-idf.yaml +++ b/tests/components/cst816/test.esp32-idf.yaml @@ -1,12 +1,12 @@ substitutions: - clk_pin: GPIO0 - mosi_pin: GPIO2 cs_pin: GPIO4 dc_pin: GPIO5 disp_reset_pin: GPIO12 - scl_pin: GPIO13 - sda_pin: GPIO14 interrupt_pin: GPIO15 - reset_pin: GPIO16 + reset_pin: GPIO25 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/current_based/common.yaml b/tests/components/current_based/common.yaml index 25dc9671b7..503c4596e9 100644 --- a/tests/components/current_based/common.yaml +++ b/tests/components/current_based/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ade7953_i2c + i2c_id: i2c_bus irq_pin: ${irq_pin} voltage: name: ADE7953 Voltage diff --git a/tests/components/current_based/test.esp32-c3-idf.yaml b/tests/components/current_based/test.esp32-c3-idf.yaml index 799acabd5a..59296a1e6e 100644 --- a/tests/components/current_based/test.esp32-c3-idf.yaml +++ b/tests/components/current_based/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/current_based/test.esp32-idf.yaml b/tests/components/current_based/test.esp32-idf.yaml index 2c57d412f6..49629536e7 100644 --- a/tests/components/current_based/test.esp32-idf.yaml +++ b/tests/components/current_based/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 irq_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/current_based/test.esp8266-ard.yaml b/tests/components/current_based/test.esp8266-ard.yaml index c8e6a43f44..dc7609ab37 100644 --- a/tests/components/current_based/test.esp8266-ard.yaml +++ b/tests/components/current_based/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/current_based/test.rp2040-ard.yaml b/tests/components/current_based/test.rp2040-ard.yaml index 799acabd5a..b80562ad22 100644 --- a/tests/components/current_based/test.rp2040-ard.yaml +++ b/tests/components/current_based/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/cwww/common.yaml b/tests/components/cwww/common.yaml index 0ad5beeaae..7fa5ab668c 100644 --- a/tests/components/cwww/common.yaml +++ b/tests/components/cwww/common.yaml @@ -1,11 +1,3 @@ -output: - - platform: ${light_platform} - id: light_output_1 - pin: ${pin_o1} - - platform: ${light_platform} - id: light_output_2 - pin: ${pin_o2} - light: - platform: cwww name: CWWW Light diff --git a/tests/components/cwww/test.esp32-c3-idf.yaml b/tests/components/cwww/test.esp32-c3-idf.yaml index 982394ded6..51571b34cf 100644 --- a/tests/components/cwww/test.esp32-c3-idf.yaml +++ b/tests/components/cwww/test.esp32-c3-idf.yaml @@ -3,12 +3,15 @@ substitutions: pin_o1: GPIO6 pin_o2: GPIO7 -packages: - device_base: !include common.yaml - output: - - id: !extend light_output_1 + - platform: ${light_platform} + id: light_output_1 + pin: ${pin_o1} channel: 0 - - id: !extend light_output_2 + - platform: ${light_platform} + id: light_output_2 + pin: ${pin_o2} channel: 1 phase_angle: 180° + +<<: !include common.yaml diff --git a/tests/components/cwww/test.esp32-idf.yaml b/tests/components/cwww/test.esp32-idf.yaml index d2a998e6b3..01edf0b0b5 100644 --- a/tests/components/cwww/test.esp32-idf.yaml +++ b/tests/components/cwww/test.esp32-idf.yaml @@ -3,12 +3,15 @@ substitutions: pin_o1: GPIO16 pin_o2: GPIO17 -packages: - device_base: !include common.yaml - output: - - id: !extend light_output_1 + - platform: ${light_platform} + id: light_output_1 + pin: ${pin_o1} channel: 0 - - id: !extend light_output_2 + - platform: ${light_platform} + id: light_output_2 + pin: ${pin_o2} channel: 1 phase_angle: 180° + +<<: !include common.yaml diff --git a/tests/components/cwww/test.esp8266-ard.yaml b/tests/components/cwww/test.esp8266-ard.yaml index 75a5b9d64d..49d73b7d3d 100644 --- a/tests/components/cwww/test.esp8266-ard.yaml +++ b/tests/components/cwww/test.esp8266-ard.yaml @@ -3,4 +3,12 @@ substitutions: pin_o1: GPIO12 pin_o2: GPIO13 +output: + - platform: ${light_platform} + id: light_output_1 + pin: ${pin_o1} + - platform: ${light_platform} + id: light_output_2 + pin: ${pin_o2} + <<: !include common.yaml diff --git a/tests/components/cwww/test.rp2040-ard.yaml b/tests/components/cwww/test.rp2040-ard.yaml index 537177aca1..ba8e0ad071 100644 --- a/tests/components/cwww/test.rp2040-ard.yaml +++ b/tests/components/cwww/test.rp2040-ard.yaml @@ -3,4 +3,12 @@ substitutions: pin_o1: GPIO12 pin_o2: GPIO13 +output: + - platform: ${light_platform} + id: light_output_1 + pin: ${pin_o1} + - platform: ${light_platform} + id: light_output_2 + pin: ${pin_o2} + <<: !include common.yaml diff --git a/tests/components/dac7678/common.yaml b/tests/components/dac7678/common.yaml index efad81a5ff..6c9c032686 100644 --- a/tests/components/dac7678/common.yaml +++ b/tests/components/dac7678/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_dac7678 - scl: ${scl_pin} - sda: ${sda_pin} - dac7678: + i2c_id: i2c_bus address: 0x4A id: dac7678_hub internal_reference: true diff --git a/tests/components/dac7678/test.esp32-c3-idf.yaml b/tests/components/dac7678/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/dac7678/test.esp32-c3-idf.yaml +++ b/tests/components/dac7678/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/dac7678/test.esp32-idf.yaml b/tests/components/dac7678/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/dac7678/test.esp32-idf.yaml +++ b/tests/components/dac7678/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/dac7678/test.esp8266-ard.yaml b/tests/components/dac7678/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/dac7678/test.esp8266-ard.yaml +++ b/tests/components/dac7678/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/dac7678/test.rp2040-ard.yaml b/tests/components/dac7678/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/dac7678/test.rp2040-ard.yaml +++ b/tests/components/dac7678/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/daikin_arc/test.esp8266-ard.yaml b/tests/components/daikin_arc/test.esp8266-ard.yaml index 8e08490d0c..5698a7ef5f 100644 --- a/tests/components/daikin_arc/test.esp8266-ard.yaml +++ b/tests/components/daikin_arc/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO5 - rx_pin: GPIO4 + tx_pin: GPIO0 + rx_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/dallas_temp/common.yaml b/tests/components/dallas_temp/common.yaml index fb51f4818e..6d865d8e93 100644 --- a/tests/components/dallas_temp/common.yaml +++ b/tests/components/dallas_temp/common.yaml @@ -1,6 +1,6 @@ one_wire: - platform: gpio - pin: 4 + pin: ${one_wire_pin} sensor: - platform: dallas_temp diff --git a/tests/components/dallas_temp/test.esp32-c3-idf.yaml b/tests/components/dallas_temp/test.esp32-c3-idf.yaml index dade44d145..49bf988eb4 100644 --- a/tests/components/dallas_temp/test.esp32-c3-idf.yaml +++ b/tests/components/dallas_temp/test.esp32-c3-idf.yaml @@ -1 +1,7 @@ +substitutions: + one_wire_pin: "4" + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/dallas_temp/test.esp32-idf.yaml b/tests/components/dallas_temp/test.esp32-idf.yaml index dade44d145..7f9a7d4c2c 100644 --- a/tests/components/dallas_temp/test.esp32-idf.yaml +++ b/tests/components/dallas_temp/test.esp32-idf.yaml @@ -1 +1,7 @@ +substitutions: + one_wire_pin: "4" + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/dallas_temp/test.esp8266-ard.yaml b/tests/components/dallas_temp/test.esp8266-ard.yaml index dade44d145..f58b3e0ff3 100644 --- a/tests/components/dallas_temp/test.esp8266-ard.yaml +++ b/tests/components/dallas_temp/test.esp8266-ard.yaml @@ -1 +1,7 @@ +substitutions: + one_wire_pin: "13" + +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/dallas_temp/test.rp2040-ard.yaml b/tests/components/dallas_temp/test.rp2040-ard.yaml index dade44d145..d86645aa64 100644 --- a/tests/components/dallas_temp/test.rp2040-ard.yaml +++ b/tests/components/dallas_temp/test.rp2040-ard.yaml @@ -1 +1,7 @@ +substitutions: + one_wire_pin: "10" + +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/daly_bms/common.yaml b/tests/components/daly_bms/common.yaml index a4cb849f9f..222999e25e 100644 --- a/tests/components/daly_bms/common.yaml +++ b/tests/components/daly_bms/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_daly_bms - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 4800 - daly_bms: update_interval: 20s diff --git a/tests/components/daly_bms/test.esp32-c3-idf.yaml b/tests/components/daly_bms/test.esp32-c3-idf.yaml index c79d14c740..4b7c8351a7 100644 --- a/tests/components/daly_bms/test.esp32-c3-idf.yaml +++ b/tests/components/daly_bms/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/daly_bms/test.esp32-idf.yaml b/tests/components/daly_bms/test.esp32-idf.yaml index 811f6b72a6..64baa4ec9d 100644 --- a/tests/components/daly_bms/test.esp32-idf.yaml +++ b/tests/components/daly_bms/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO12 rx_pin: GPIO14 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/daly_bms/test.esp8266-ard.yaml b/tests/components/daly_bms/test.esp8266-ard.yaml index 3b44f9c9c3..89ca3ab5ae 100644 --- a/tests/components/daly_bms/test.esp8266-ard.yaml +++ b/tests/components/daly_bms/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO1 rx_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/daly_bms/test.rp2040-ard.yaml b/tests/components/daly_bms/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/daly_bms/test.rp2040-ard.yaml +++ b/tests/components/daly_bms/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/dfplayer/common.yaml b/tests/components/dfplayer/common.yaml index d7446141c3..5d2540c275 100644 --- a/tests/components/dfplayer/common.yaml +++ b/tests/components/dfplayer/common.yaml @@ -26,12 +26,6 @@ esphome: - dfplayer.volume_down - dfplayer.sleep -uart: - - id: uart_dfplayer - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - dfplayer: on_finished_playback: then: diff --git a/tests/components/dfplayer/test.esp32-c3-idf.yaml b/tests/components/dfplayer/test.esp32-c3-idf.yaml index c79d14c740..4b7c8351a7 100644 --- a/tests/components/dfplayer/test.esp32-c3-idf.yaml +++ b/tests/components/dfplayer/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/dfplayer/test.esp32-idf.yaml b/tests/components/dfplayer/test.esp32-idf.yaml index 811f6b72a6..64baa4ec9d 100644 --- a/tests/components/dfplayer/test.esp32-idf.yaml +++ b/tests/components/dfplayer/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO12 rx_pin: GPIO14 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/dfplayer/test.esp8266-ard.yaml b/tests/components/dfplayer/test.esp8266-ard.yaml index 3b44f9c9c3..89ca3ab5ae 100644 --- a/tests/components/dfplayer/test.esp8266-ard.yaml +++ b/tests/components/dfplayer/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO1 rx_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/dfplayer/test.rp2040-ard.yaml b/tests/components/dfplayer/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/dfplayer/test.rp2040-ard.yaml +++ b/tests/components/dfplayer/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/common.yaml b/tests/components/dfrobot_sen0395/common.yaml index 8c349911d3..7f980574ed 100644 --- a/tests/components/dfrobot_sen0395/common.yaml +++ b/tests/components/dfrobot_sen0395/common.yaml @@ -14,12 +14,6 @@ esphome: sensitivity: 6 - dfrobot_sen0395.reset -uart: - - id: uart_dfrobot_sen0395 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - dfrobot_sen0395: - id: mmwave diff --git a/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml b/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml index c79d14c740..4b7c8351a7 100644 --- a/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml +++ b/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO7 - rx_pin: GPIO8 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.esp32-idf.yaml b/tests/components/dfrobot_sen0395/test.esp32-idf.yaml index 811f6b72a6..64baa4ec9d 100644 --- a/tests/components/dfrobot_sen0395/test.esp32-idf.yaml +++ b/tests/components/dfrobot_sen0395/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO12 rx_pin: GPIO14 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.esp8266-ard.yaml b/tests/components/dfrobot_sen0395/test.esp8266-ard.yaml index 3b44f9c9c3..89ca3ab5ae 100644 --- a/tests/components/dfrobot_sen0395/test.esp8266-ard.yaml +++ b/tests/components/dfrobot_sen0395/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO1 rx_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.rp2040-ard.yaml b/tests/components/dfrobot_sen0395/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/dfrobot_sen0395/test.rp2040-ard.yaml +++ b/tests/components/dfrobot_sen0395/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/dht12/common.yaml b/tests/components/dht12/common.yaml index 91346e0e27..2602ae13cf 100644 --- a/tests/components/dht12/common.yaml +++ b/tests/components/dht12/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_dht12 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: dht12 + i2c_id: i2c_bus temperature: name: DHT12 Temperature humidity: diff --git a/tests/components/dht12/test.esp32-c3-idf.yaml b/tests/components/dht12/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/dht12/test.esp32-c3-idf.yaml +++ b/tests/components/dht12/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/dht12/test.esp32-idf.yaml b/tests/components/dht12/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/dht12/test.esp32-idf.yaml +++ b/tests/components/dht12/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/dht12/test.esp8266-ard.yaml b/tests/components/dht12/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/dht12/test.esp8266-ard.yaml +++ b/tests/components/dht12/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/dht12/test.rp2040-ard.yaml b/tests/components/dht12/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/dht12/test.rp2040-ard.yaml +++ b/tests/components/dht12/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/display/common.yaml b/tests/components/display/common.yaml index 4fc4fafa25..27abb23e03 100644 --- a/tests/components/display/common.yaml +++ b/tests/components/display/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 - display: - platform: ili9xxx id: main_lcd diff --git a/tests/components/dps310/common.yaml b/tests/components/dps310/common.yaml index e6c0c9fab8..847a79f2e6 100644 --- a/tests/components/dps310/common.yaml +++ b/tests/components/dps310/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_dps310 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: dps310 + i2c_id: i2c_bus temperature: name: DPS310 Temperature pressure: diff --git a/tests/components/dps310/test.esp32-c3-idf.yaml b/tests/components/dps310/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/dps310/test.esp32-c3-idf.yaml +++ b/tests/components/dps310/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/dps310/test.esp32-idf.yaml b/tests/components/dps310/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/dps310/test.esp32-idf.yaml +++ b/tests/components/dps310/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/dps310/test.esp8266-ard.yaml b/tests/components/dps310/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/dps310/test.esp8266-ard.yaml +++ b/tests/components/dps310/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/dps310/test.rp2040-ard.yaml b/tests/components/dps310/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/dps310/test.rp2040-ard.yaml +++ b/tests/components/dps310/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ds1307/common.yaml b/tests/components/ds1307/common.yaml index 3466a9eec8..cdd2b9cf56 100644 --- a/tests/components/ds1307/common.yaml +++ b/tests/components/ds1307/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_ds1307 - scl: ${scl_pin} - sda: ${sda_pin} - time: - platform: ds1307 + i2c_id: i2c_bus id: ds1307_time update_interval: never diff --git a/tests/components/ds1307/test.esp32-c3-idf.yaml b/tests/components/ds1307/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ds1307/test.esp32-c3-idf.yaml +++ b/tests/components/ds1307/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ds1307/test.esp32-idf.yaml b/tests/components/ds1307/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ds1307/test.esp32-idf.yaml +++ b/tests/components/ds1307/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ds1307/test.esp8266-ard.yaml b/tests/components/ds1307/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ds1307/test.esp8266-ard.yaml +++ b/tests/components/ds1307/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ds1307/test.rp2040-ard.yaml b/tests/components/ds1307/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ds1307/test.rp2040-ard.yaml +++ b/tests/components/ds1307/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ds2484/common.yaml b/tests/components/ds2484/common.yaml index 9d2882a3c0..1e5dcd7dba 100644 --- a/tests/components/ds2484/common.yaml +++ b/tests/components/ds2484/common.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_ds2484 - scl: ${scl_pin} - sda: ${sda_pin} - one_wire: platform: ds2484 - i2c_id: i2c_ds2484 + i2c_id: i2c_bus address: 0x18 active_pullup: true strong_pullup: false diff --git a/tests/components/ds2484/test.esp32-c3-idf.yaml b/tests/components/ds2484/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ds2484/test.esp32-c3-idf.yaml +++ b/tests/components/ds2484/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ds2484/test.esp32-idf.yaml b/tests/components/ds2484/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ds2484/test.esp32-idf.yaml +++ b/tests/components/ds2484/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ds2484/test.esp8266-ard.yaml b/tests/components/ds2484/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ds2484/test.esp8266-ard.yaml +++ b/tests/components/ds2484/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ds2484/test.rp2040-ard.yaml b/tests/components/ds2484/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ds2484/test.rp2040-ard.yaml +++ b/tests/components/ds2484/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/dsmr/common.yaml b/tests/components/dsmr/common.yaml index 2901b811fe..038bf2806b 100644 --- a/tests/components/dsmr/common.yaml +++ b/tests/components/dsmr/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_dsmr - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - dsmr: decryption_key: 00112233445566778899aabbccddeeff max_telegram_length: 1000 diff --git a/tests/components/dsmr/test.esp32-ard.yaml b/tests/components/dsmr/test.esp32-ard.yaml index 7a65a48ec0..f218b297aa 100644 --- a/tests/components/dsmr/test.esp32-ard.yaml +++ b/tests/components/dsmr/test.esp32-ard.yaml @@ -1,6 +1,7 @@ substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 request_pin: GPIO15 +packages: + uart: !include ../../test_build_components/common/uart/esp32-ard.yaml + <<: !include common.yaml diff --git a/tests/components/dsmr/test.esp8266-ard.yaml b/tests/components/dsmr/test.esp8266-ard.yaml index a47bd58806..08bcf16fc9 100644 --- a/tests/components/dsmr/test.esp8266-ard.yaml +++ b/tests/components/dsmr/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 request_pin: GPIO15 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/dsmr/test.rp2040-ard.yaml b/tests/components/dsmr/test.rp2040-ard.yaml index 72998506ac..8684cb76aa 100644 --- a/tests/components/dsmr/test.rp2040-ard.yaml +++ b/tests/components/dsmr/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 request_pin: GPIO6 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ee895/common.yaml b/tests/components/ee895/common.yaml index 63d77abcaf..f1b17ca9d9 100644 --- a/tests/components/ee895/common.yaml +++ b/tests/components/ee895/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ee895 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ee895 + i2c_id: i2c_bus address: 0x5F co2: name: EE895 CO2 diff --git a/tests/components/ee895/test.esp32-c3-idf.yaml b/tests/components/ee895/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ee895/test.esp32-c3-idf.yaml +++ b/tests/components/ee895/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ee895/test.esp32-idf.yaml b/tests/components/ee895/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ee895/test.esp32-idf.yaml +++ b/tests/components/ee895/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ee895/test.esp8266-ard.yaml b/tests/components/ee895/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ee895/test.esp8266-ard.yaml +++ b/tests/components/ee895/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ee895/test.rp2040-ard.yaml b/tests/components/ee895/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ee895/test.rp2040-ard.yaml +++ b/tests/components/ee895/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ektf2232/common.yaml b/tests/components/ektf2232/common.yaml index 91f09b4710..8ab57be46f 100644 --- a/tests/components/ektf2232/common.yaml +++ b/tests/components/ektf2232/common.yaml @@ -1,20 +1,18 @@ -i2c: - - id: i2c_ektf2232 - scl: ${scl_pin} - sda: ${sda_pin} - display: - platform: ssd1306_i2c + i2c_id: i2c_bus id: ssd1306_display model: SSD1306_128X64 reset_pin: ${display_reset_pin} pages: - - id: page1 + - id: ektf2232_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); touchscreen: - platform: ektf2232 + i2c_id: i2c_bus + id: ektf2232_touchscreen interrupt_pin: ${interrupt_pin} reset_pin: ${touch_reset_pin} display: ssd1306_display diff --git a/tests/components/ektf2232/test.esp32-c3-idf.yaml b/tests/components/ektf2232/test.esp32-c3-idf.yaml index 4d793a3242..708d352a59 100644 --- a/tests/components/ektf2232/test.esp32-c3-idf.yaml +++ b/tests/components/ektf2232/test.esp32-c3-idf.yaml @@ -1,8 +1,9 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 display_reset_pin: GPIO3 interrupt_pin: GPIO6 touch_reset_pin: GPIO7 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ektf2232/test.esp32-idf.yaml b/tests/components/ektf2232/test.esp32-idf.yaml index 7d3f2ca7a2..3fab081bed 100644 --- a/tests/components/ektf2232/test.esp32-idf.yaml +++ b/tests/components/ektf2232/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 display_reset_pin: GPIO13 interrupt_pin: GPIO14 touch_reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ektf2232/test.esp8266-ard.yaml b/tests/components/ektf2232/test.esp8266-ard.yaml index a87e9dfd45..38a3894deb 100644 --- a/tests/components/ektf2232/test.esp8266-ard.yaml +++ b/tests/components/ektf2232/test.esp8266-ard.yaml @@ -1,8 +1,9 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 display_reset_pin: GPIO3 - interrupt_pin: GPIO12 - touch_reset_pin: GPIO13 + interrupt_pin: GPIO15 + touch_reset_pin: GPIO16 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ektf2232/test.rp2040-ard.yaml b/tests/components/ektf2232/test.rp2040-ard.yaml index 4d793a3242..cda1b67715 100644 --- a/tests/components/ektf2232/test.rp2040-ard.yaml +++ b/tests/components/ektf2232/test.rp2040-ard.yaml @@ -1,8 +1,9 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 display_reset_pin: GPIO3 interrupt_pin: GPIO6 touch_reset_pin: GPIO7 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/emc2101/common.yaml b/tests/components/emc2101/common.yaml index 795b02c6d3..d9e6fe2b96 100644 --- a/tests/components/emc2101/common.yaml +++ b/tests/components/emc2101/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_emc2101 - scl: ${scl_pin} - sda: ${sda_pin} - emc2101: + i2c_id: i2c_bus pwm: resolution: 8 diff --git a/tests/components/emc2101/test.esp32-c3-idf.yaml b/tests/components/emc2101/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/emc2101/test.esp32-c3-idf.yaml +++ b/tests/components/emc2101/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/emc2101/test.esp32-idf.yaml b/tests/components/emc2101/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/emc2101/test.esp32-idf.yaml +++ b/tests/components/emc2101/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/emc2101/test.esp8266-ard.yaml b/tests/components/emc2101/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/emc2101/test.esp8266-ard.yaml +++ b/tests/components/emc2101/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/emc2101/test.rp2040-ard.yaml b/tests/components/emc2101/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/emc2101/test.rp2040-ard.yaml +++ b/tests/components/emc2101/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/emmeti/test.esp8266-ard.yaml b/tests/components/emmeti/test.esp8266-ard.yaml index 2fb00aea61..1c9baa4ea3 100644 --- a/tests/components/emmeti/test.esp8266-ard.yaml +++ b/tests/components/emmeti/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - remote_transmitter_pin: GPIO4 - remote_receiver_pin: GPIO5 + remote_transmitter_pin: GPIO0 + remote_receiver_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/ens160_i2c/common.yaml b/tests/components/ens160_i2c/common.yaml index 39a5b35067..685c8d3fee 100644 --- a/tests/components/ens160_i2c/common.yaml +++ b/tests/components/ens160_i2c/common.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_ens160 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ens160_i2c - i2c_id: i2c_ens160 + i2c_id: i2c_bus address: 0x53 eco2: name: "ENS160 eCO2" diff --git a/tests/components/ens160_i2c/test.esp32-c3-idf.yaml b/tests/components/ens160_i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ens160_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/ens160_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ens160_i2c/test.esp32-idf.yaml b/tests/components/ens160_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ens160_i2c/test.esp32-idf.yaml +++ b/tests/components/ens160_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ens160_i2c/test.esp8266-ard.yaml b/tests/components/ens160_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ens160_i2c/test.esp8266-ard.yaml +++ b/tests/components/ens160_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ens160_i2c/test.rp2040-ard.yaml b/tests/components/ens160_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ens160_i2c/test.rp2040-ard.yaml +++ b/tests/components/ens160_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ens160_spi/common.yaml b/tests/components/ens160_spi/common.yaml index c8b663272f..53000a5e96 100644 --- a/tests/components/ens160_spi/common.yaml +++ b/tests/components/ens160_spi/common.yaml @@ -1,12 +1,5 @@ -spi: - - id: spi_ens160 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: ens160_spi - spi_id: spi_ens160 cs_pin: ${cs_pin} eco2: name: "ENS160 eCO2" diff --git a/tests/components/ens160_spi/test.esp32-c3-idf.yaml b/tests/components/ens160_spi/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/ens160_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ens160_spi/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ens160_spi/test.esp32-idf.yaml b/tests/components/ens160_spi/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/ens160_spi/test.esp32-idf.yaml +++ b/tests/components/ens160_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ens160_spi/test.esp8266-ard.yaml b/tests/components/ens160_spi/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/ens160_spi/test.esp8266-ard.yaml +++ b/tests/components/ens160_spi/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ens160_spi/test.rp2040-ard.yaml b/tests/components/ens160_spi/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/ens160_spi/test.rp2040-ard.yaml +++ b/tests/components/ens160_spi/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ens210/common.yaml b/tests/components/ens210/common.yaml index b276154449..6bc8f824cc 100644 --- a/tests/components/ens210/common.yaml +++ b/tests/components/ens210/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ens210 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ens210 + i2c_id: i2c_bus temperature: name: ENS210 Temperature humidity: diff --git a/tests/components/ens210/test.esp32-c3-idf.yaml b/tests/components/ens210/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ens210/test.esp32-c3-idf.yaml +++ b/tests/components/ens210/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ens210/test.esp32-idf.yaml b/tests/components/ens210/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ens210/test.esp32-idf.yaml +++ b/tests/components/ens210/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ens210/test.esp8266-ard.yaml b/tests/components/ens210/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ens210/test.esp8266-ard.yaml +++ b/tests/components/ens210/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ens210/test.rp2040-ard.yaml b/tests/components/ens210/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ens210/test.rp2040-ard.yaml +++ b/tests/components/ens210/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/epaper_spi/test.esp32-s3-idf.yaml b/tests/components/epaper_spi/test.esp32-s3-idf.yaml index 3d8d62a7ca..34aefb82b4 100644 --- a/tests/components/epaper_spi/test.esp32-s3-idf.yaml +++ b/tests/components/epaper_spi/test.esp32-s3-idf.yaml @@ -1,9 +1,9 @@ -spi: - clk_pin: GPIO7 - mosi_pin: GPIO9 +packages: + spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml display: - platform: epaper_spi + spi_id: spi_bus model: 7.3in-spectra-e6 cs_pin: GPIO5 dc_pin: GPIO17 diff --git a/tests/components/es7210/common.yaml b/tests/components/es7210/common.yaml index 3fab177cb3..b9f0b0c3e9 100644 --- a/tests/components/es7210/common.yaml +++ b/tests/components/es7210/common.yaml @@ -4,13 +4,9 @@ esphome: - audio_adc.set_mic_gain: 0db - audio_adc.set_mic_gain: !lambda 'return 4;' -i2c: - - id: i2c_aic3204 - scl: ${scl_pin} - sda: ${sda_pin} - audio_adc: - platform: es7210 + i2c_id: i2c_bus id: es7210_adc bits_per_sample: 16bit sample_rate: 16000 diff --git a/tests/components/es7210/test.esp32-c3-idf.yaml b/tests/components/es7210/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/es7210/test.esp32-c3-idf.yaml +++ b/tests/components/es7210/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/es7210/test.esp32-idf.yaml b/tests/components/es7210/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/es7210/test.esp32-idf.yaml +++ b/tests/components/es7210/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/es7243e/common.yaml b/tests/components/es7243e/common.yaml index 3de76909e9..0fcfb97273 100644 --- a/tests/components/es7243e/common.yaml +++ b/tests/components/es7243e/common.yaml @@ -4,11 +4,7 @@ esphome: - audio_adc.set_mic_gain: 0db - audio_adc.set_mic_gain: !lambda 'return 4;' -i2c: - - id: i2c_es7243e - scl: ${scl_pin} - sda: ${sda_pin} - audio_adc: - platform: es7243e + i2c_id: i2c_bus id: es7243e_adc diff --git a/tests/components/es7243e/test.esp32-c3-idf.yaml b/tests/components/es7243e/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/es7243e/test.esp32-c3-idf.yaml +++ b/tests/components/es7243e/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/es7243e/test.esp32-idf.yaml b/tests/components/es7243e/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/es7243e/test.esp32-idf.yaml +++ b/tests/components/es7243e/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/es8156/common.yaml b/tests/components/es8156/common.yaml index addaa0b70a..c41a332bd7 100644 --- a/tests/components/es8156/common.yaml +++ b/tests/components/es8156/common.yaml @@ -6,10 +6,6 @@ esphome: - audio_dac.set_volume: volume: 50% -i2c: - - id: i2c_es8156 - scl: ${scl_pin} - sda: ${sda_pin} - audio_dac: - platform: es8156 + i2c_id: i2c_bus diff --git a/tests/components/es8156/test.esp32-c3-idf.yaml b/tests/components/es8156/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/es8156/test.esp32-c3-idf.yaml +++ b/tests/components/es8156/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/es8156/test.esp32-idf.yaml b/tests/components/es8156/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/es8156/test.esp32-idf.yaml +++ b/tests/components/es8156/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/es8156/test.esp8266-ard.yaml b/tests/components/es8156/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/es8156/test.esp8266-ard.yaml +++ b/tests/components/es8156/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/es8311/common.yaml b/tests/components/es8311/common.yaml index d833d1c043..e855761ef4 100644 --- a/tests/components/es8311/common.yaml +++ b/tests/components/es8311/common.yaml @@ -6,10 +6,6 @@ esphome: - audio_dac.set_volume: volume: 50% -i2c: - - id: i2c_aic3204 - scl: ${scl_pin} - sda: ${sda_pin} - audio_dac: - platform: es8311 + i2c_id: i2c_bus diff --git a/tests/components/es8311/test.esp32-c3-idf.yaml b/tests/components/es8311/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/es8311/test.esp32-c3-idf.yaml +++ b/tests/components/es8311/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/es8311/test.esp32-idf.yaml b/tests/components/es8311/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/es8311/test.esp32-idf.yaml +++ b/tests/components/es8311/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/es8311/test.esp8266-ard.yaml b/tests/components/es8311/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/es8311/test.esp8266-ard.yaml +++ b/tests/components/es8311/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/es8388/common.yaml b/tests/components/es8388/common.yaml index 6a63de5aa1..cc7465ab93 100644 --- a/tests/components/es8388/common.yaml +++ b/tests/components/es8388/common.yaml @@ -7,13 +7,9 @@ esphome: - audio_dac.set_volume: volume: 50% -i2c: - - id: i2c_es8388 - scl: ${scl_pin} - sda: ${sda_pin} - audio_dac: - platform: es8388 + i2c_id: i2c_bus id: es8388_parent select: diff --git a/tests/components/es8388/test.esp32-c3-idf.yaml b/tests/components/es8388/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/es8388/test.esp32-c3-idf.yaml +++ b/tests/components/es8388/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/es8388/test.esp32-idf.yaml b/tests/components/es8388/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/es8388/test.esp32-idf.yaml +++ b/tests/components/es8388/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/es8388/test.esp8266-ard.yaml b/tests/components/es8388/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/es8388/test.esp8266-ard.yaml +++ b/tests/components/es8388/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/esp32_ble_client/test.esp32-c3-idf.yaml b/tests/components/esp32_ble_client/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/esp32_ble_client/test.esp32-c3-idf.yaml +++ b/tests/components/esp32_ble_client/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/esp32_ble_client/test.esp32-idf.yaml b/tests/components/esp32_ble_client/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/esp32_ble_client/test.esp32-idf.yaml +++ b/tests/components/esp32_ble_client/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/esp32_ble_tracker/test.esp32-c3-idf.yaml b/tests/components/esp32_ble_tracker/test.esp32-c3-idf.yaml index b71896bad5..ea6f0d4022 100644 --- a/tests/components/esp32_ble_tracker/test.esp32-c3-idf.yaml +++ b/tests/components/esp32_ble_tracker/test.esp32-c3-idf.yaml @@ -1,3 +1,6 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml esp32_ble_tracker: diff --git a/tests/components/esp32_ble_tracker/test.esp32-idf.yaml b/tests/components/esp32_ble_tracker/test.esp32-idf.yaml index 1ffcfb9988..8f6e3c5731 100644 --- a/tests/components/esp32_ble_tracker/test.esp32-idf.yaml +++ b/tests/components/esp32_ble_tracker/test.esp32-idf.yaml @@ -1,3 +1,6 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml esp32_ble_tracker: diff --git a/tests/components/esp32_camera/common.yaml b/tests/components/esp32_camera/common.yaml index 64f75c699a..eac5109bc8 100644 --- a/tests/components/esp32_camera/common.yaml +++ b/tests/components/esp32_camera/common.yaml @@ -1,29 +1 @@ -esp32_camera: - name: ESP32 Camera - data_pins: - - number: 17 - - number: 35 - - number: 34 - - number: 5 - - number: 39 - - number: 18 - - number: 36 - - number: 19 - vsync_pin: 22 - href_pin: 26 - pixel_clock_pin: 21 - external_clock: - pin: 27 - frequency: 20MHz - i2c_pins: - sda: 25 - scl: 23 - reset_pin: 15 - power_down_pin: 1 - resolution: 640x480 - jpeg_quality: 10 - frame_buffer_location: PSRAM - on_image: - then: - - lambda: |- - ESP_LOGD("main", "image len=%d, data=%c", image.length, image.data[0]); +# ESP32 camera hardware configuration comes from the camera package diff --git a/tests/components/esp32_camera/test.esp32-idf.yaml b/tests/components/esp32_camera/test.esp32-idf.yaml index dade44d145..3d93dd6418 100644 --- a/tests/components/esp32_camera/test.esp32-idf.yaml +++ b/tests/components/esp32_camera/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + camera: !include ../../test_build_components/common/camera/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/esp32_camera_web_server/common.yaml b/tests/components/esp32_camera_web_server/common.yaml index fe2a6a2739..2a1858f681 100644 --- a/tests/components/esp32_camera_web_server/common.yaml +++ b/tests/components/esp32_camera_web_server/common.yaml @@ -1,32 +1,3 @@ -esp32_camera: - name: ESP32 Camera - data_pins: - - number: 17 - - number: 35 - - number: 34 - - number: 5 - - number: 39 - - number: 18 - - number: 36 - - number: 19 - vsync_pin: 22 - href_pin: 26 - pixel_clock_pin: 21 - external_clock: - pin: 27 - frequency: 20MHz - i2c_pins: - sda: 25 - scl: 23 - reset_pin: 15 - power_down_pin: 1 - resolution: 640x480 - jpeg_quality: 10 - on_image: - then: - - lambda: |- - ESP_LOGD("main", "image len=%d, data=%c", image.length, image.data[0]); - esp32_camera_web_server: - port: 8080 mode: stream diff --git a/tests/components/esp32_camera_web_server/test.esp32-idf.yaml b/tests/components/esp32_camera_web_server/test.esp32-idf.yaml index dade44d145..3d93dd6418 100644 --- a/tests/components/esp32_camera_web_server/test.esp32-idf.yaml +++ b/tests/components/esp32_camera_web_server/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + camera: !include ../../test_build_components/common/camera/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/esp32_can/test.esp32-c3-idf.yaml b/tests/components/esp32_can/test.esp32-c3-idf.yaml index c79d14c740..22effb1bd0 100644 --- a/tests/components/esp32_can/test.esp32-c3-idf.yaml +++ b/tests/components/esp32_can/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: tx_pin: GPIO7 - rx_pin: GPIO8 + rx_pin: GPIO9 <<: !include common.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-s3-idf.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-s3-idf.yaml index ad273903b2..6bf0639a52 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-s3-idf.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-s3-idf.yaml @@ -2,11 +2,22 @@ substitutions: pin1: GPIO3 pin2: GPIO4 -packages: - common: !include common.yaml - +# WARNING: Using !extend or !remove prevents automatic component grouping in CI, making builds slower. light: - - id: !extend led_strip1 + - platform: esp32_rmt_led_strip + id: led_strip1 + pin: ${pin1} + num_leds: 60 + rgb_order: GRB + chipset: ws2812 use_dma: "true" - - id: !extend led_strip2 + - platform: esp32_rmt_led_strip + id: led_strip2 + pin: ${pin2} + num_leds: 60 + rgb_order: RGB + bit0_high: 100us + bit0_low: 100us + bit1_high: 100us + bit1_low: 100us use_dma: "false" diff --git a/tests/components/espnow/test.esp32-idf.yaml b/tests/components/espnow/test.esp32-idf.yaml index dade44d145..b47e39c389 100644 --- a/tests/components/espnow/test.esp32-idf.yaml +++ b/tests/components/espnow/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ethernet_info/test.esp32-idf.yaml b/tests/components/ethernet_info/test.esp32-idf.yaml index dade44d145..b47e39c389 100644 --- a/tests/components/ethernet_info/test.esp32-idf.yaml +++ b/tests/components/ethernet_info/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/exposure_notifications/test.esp32-c3-idf.yaml b/tests/components/exposure_notifications/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/exposure_notifications/test.esp32-c3-idf.yaml +++ b/tests/components/exposure_notifications/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/exposure_notifications/test.esp32-idf.yaml b/tests/components/exposure_notifications/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/exposure_notifications/test.esp32-idf.yaml +++ b/tests/components/exposure_notifications/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ezo/common.yaml b/tests/components/ezo/common.yaml index edeebd1a12..79afbb8aa6 100644 --- a/tests/components/ezo/common.yaml +++ b/tests/components/ezo/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ezo - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ezo + i2c_id: i2c_bus id: ph_ezo address: 99 unit_of_measurement: pH diff --git a/tests/components/ezo/test.esp32-c3-idf.yaml b/tests/components/ezo/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ezo/test.esp32-c3-idf.yaml +++ b/tests/components/ezo/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ezo/test.esp32-idf.yaml b/tests/components/ezo/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ezo/test.esp32-idf.yaml +++ b/tests/components/ezo/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ezo/test.esp8266-ard.yaml b/tests/components/ezo/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ezo/test.esp8266-ard.yaml +++ b/tests/components/ezo/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ezo/test.rp2040-ard.yaml b/tests/components/ezo/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ezo/test.rp2040-ard.yaml +++ b/tests/components/ezo/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ezo_pmp/common.yaml b/tests/components/ezo_pmp/common.yaml index 00919797be..bd2fd9193c 100644 --- a/tests/components/ezo_pmp/common.yaml +++ b/tests/components/ezo_pmp/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_ezo_pmp - scl: ${scl_pin} - sda: ${sda_pin} - ezo_pmp: + i2c_id: i2c_bus id: hcl_pump update_interval: 1s diff --git a/tests/components/ezo_pmp/test.esp32-c3-idf.yaml b/tests/components/ezo_pmp/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ezo_pmp/test.esp32-c3-idf.yaml +++ b/tests/components/ezo_pmp/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.esp32-idf.yaml b/tests/components/ezo_pmp/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ezo_pmp/test.esp32-idf.yaml +++ b/tests/components/ezo_pmp/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.esp8266-ard.yaml b/tests/components/ezo_pmp/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ezo_pmp/test.esp8266-ard.yaml +++ b/tests/components/ezo_pmp/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.rp2040-ard.yaml b/tests/components/ezo_pmp/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ezo_pmp/test.rp2040-ard.yaml +++ b/tests/components/ezo_pmp/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/fingerprint_grow/common.yaml b/tests/components/fingerprint_grow/common.yaml index e759ea5a02..2d2fc61437 100644 --- a/tests/components/fingerprint_grow/common.yaml +++ b/tests/components/fingerprint_grow/common.yaml @@ -9,12 +9,6 @@ esphome: finger_id: 2 - fingerprint_grow.delete_all: -uart: - - id: uart_fingerprint_grow - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 57600 - fingerprint_grow: sensing_pin: ${sensing_pin} password: 0x12FE37DC diff --git a/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml b/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml index faab50e152..dff4f7fd92 100644 --- a/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml +++ b/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml @@ -1,6 +1,6 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 sensing_pin: GPIO6 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.esp32-idf.yaml b/tests/components/fingerprint_grow/test.esp32-idf.yaml index 4aef3d8be6..7149f07d16 100644 --- a/tests/components/fingerprint_grow/test.esp32-idf.yaml +++ b/tests/components/fingerprint_grow/test.esp32-idf.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO14 sensing_pin: GPIO15 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.esp8266-ard.yaml b/tests/components/fingerprint_grow/test.esp8266-ard.yaml index f2a864596a..204fdf0302 100644 --- a/tests/components/fingerprint_grow/test.esp8266-ard.yaml +++ b/tests/components/fingerprint_grow/test.esp8266-ard.yaml @@ -1,6 +1,9 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 sensing_pin: GPIO15 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.rp2040-ard.yaml b/tests/components/fingerprint_grow/test.rp2040-ard.yaml index faab50e152..80ea1d22ca 100644 --- a/tests/components/fingerprint_grow/test.rp2040-ard.yaml +++ b/tests/components/fingerprint_grow/test.rp2040-ard.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO5 sensing_pin: GPIO6 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/font/common.yaml b/tests/components/font/common.yaml index fb50fc3336..2d2e970536 100644 --- a/tests/components/font/common.yaml +++ b/tests/components/font/common.yaml @@ -47,10 +47,6 @@ font: id: bdf_font size: 7 -i2c: - scl: ${i2c_scl} - sda: ${i2c_sda} - display: - platform: ssd1306_i2c id: ssd1306_display diff --git a/tests/components/font/test.esp32-c3-idf.yaml b/tests/components/font/test.esp32-c3-idf.yaml index ad14a2e9a6..2090db7e6d 100644 --- a/tests/components/font/test.esp32-c3-idf.yaml +++ b/tests/components/font/test.esp32-c3-idf.yaml @@ -1,7 +1,7 @@ substitutions: - i2c_scl: GPIO5 - i2c_sda: GPIO4 display_reset_pin: GPIO3 packages: - common: !include common.yaml + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/font/test.esp32-idf.yaml b/tests/components/font/test.esp32-idf.yaml index d98600a51b..a6a94a39da 100644 --- a/tests/components/font/test.esp32-idf.yaml +++ b/tests/components/font/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - i2c_scl: GPIO16 - i2c_sda: GPIO17 display_reset_pin: GPIO13 packages: - common: !include common.yaml + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/font/test.esp8266-ard.yaml b/tests/components/font/test.esp8266-ard.yaml index ad14a2e9a6..173f7dfaa5 100644 --- a/tests/components/font/test.esp8266-ard.yaml +++ b/tests/components/font/test.esp8266-ard.yaml @@ -1,7 +1,7 @@ substitutions: - i2c_scl: GPIO5 - i2c_sda: GPIO4 display_reset_pin: GPIO3 packages: - common: !include common.yaml + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + +<<: !include common.yaml diff --git a/tests/components/font/test.rp2040-ard.yaml b/tests/components/font/test.rp2040-ard.yaml index ad14a2e9a6..aeecf352b4 100644 --- a/tests/components/font/test.rp2040-ard.yaml +++ b/tests/components/font/test.rp2040-ard.yaml @@ -1,7 +1,7 @@ substitutions: - i2c_scl: GPIO5 - i2c_sda: GPIO4 display_reset_pin: GPIO3 packages: - common: !include common.yaml + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + +<<: !include common.yaml diff --git a/tests/components/fs3000/common.yaml b/tests/components/fs3000/common.yaml index e87ac28aa9..e8ec4d0773 100644 --- a/tests/components/fs3000/common.yaml +++ b/tests/components/fs3000/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_fs3000 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: fs3000 + i2c_id: i2c_bus name: Air Velocity model: 1005 update_interval: 60s diff --git a/tests/components/fs3000/test.esp32-c3-idf.yaml b/tests/components/fs3000/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/fs3000/test.esp32-c3-idf.yaml +++ b/tests/components/fs3000/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/fs3000/test.esp32-idf.yaml b/tests/components/fs3000/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/fs3000/test.esp32-idf.yaml +++ b/tests/components/fs3000/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/fs3000/test.esp8266-ard.yaml b/tests/components/fs3000/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/fs3000/test.esp8266-ard.yaml +++ b/tests/components/fs3000/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/fs3000/test.rp2040-ard.yaml b/tests/components/fs3000/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/fs3000/test.rp2040-ard.yaml +++ b/tests/components/fs3000/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ft5x06/common.yaml b/tests/components/ft5x06/common.yaml index 322ee88b6d..b1f7ca4cc9 100644 --- a/tests/components/ft5x06/common.yaml +++ b/tests/components/ft5x06/common.yaml @@ -1,20 +1,19 @@ -i2c: - - id: i2c_ft5x06 - scl: ${scl_pin} - sda: ${sda_pin} - display: - platform: ssd1306_i2c - id: ssd1306_display + i2c_id: i2c_bus + id: ft5x06_display model: SSD1306_128X64 reset_pin: ${reset_pin} pages: - - id: page1 + - id: ft5x06_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); touchscreen: - platform: ft5x06 + i2c_id: i2c_bus + display: ft5x06_display + id: ft5x06_touchscreen on_touch: - logger.log: format: Touch at (%d, %d) diff --git a/tests/components/ft5x06/test.esp32-c3-idf.yaml b/tests/components/ft5x06/test.esp32-c3-idf.yaml index 1e6670c196..c97f30d52c 100644 --- a/tests/components/ft5x06/test.esp32-c3-idf.yaml +++ b/tests/components/ft5x06/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ft5x06/test.esp32-idf.yaml b/tests/components/ft5x06/test.esp32-idf.yaml index 1ca773e06c..4ff2241ec9 100644 --- a/tests/components/ft5x06/test.esp32-idf.yaml +++ b/tests/components/ft5x06/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ft5x06/test.esp8266-ard.yaml b/tests/components/ft5x06/test.esp8266-ard.yaml index dfdc12a3d1..b8bb94edde 100644 --- a/tests/components/ft5x06/test.esp8266-ard.yaml +++ b/tests/components/ft5x06/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ft5x06/test.rp2040-ard.yaml b/tests/components/ft5x06/test.rp2040-ard.yaml index 1e6670c196..1bf10642c5 100644 --- a/tests/components/ft5x06/test.rp2040-ard.yaml +++ b/tests/components/ft5x06/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ft63x6/common.yaml b/tests/components/ft63x6/common.yaml index 1fae6da5f4..ea5a60a2f6 100644 --- a/tests/components/ft63x6/common.yaml +++ b/tests/components/ft63x6/common.yaml @@ -1,25 +1,19 @@ -spi: - - id: spi_ft63x6 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - -i2c: - - id: i2c_ft63x6 - scl: ${scl_pin} - sda: ${sda_pin} - display: - platform: ssd1306_i2c - id: ssd1306_display + i2c_id: i2c_bus + id: ft63x6_display model: SSD1306_128X64 reset_pin: ${reset_pin} pages: - - id: page1 + - id: ft63x6_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); touchscreen: - platform: ft63x6 + i2c_id: i2c_bus + display: ft63x6_display + id: ft63x6_touchscreen interrupt_pin: ${interrupt_pin} transform: swap_xy: true @@ -42,6 +36,7 @@ touchscreen: binary_sensor: - platform: touchscreen + touchscreen_id: ft63x6_touchscreen name: Bottom Left Touch use_raw: true x_min: 0 diff --git a/tests/components/ft63x6/test.esp32-c3-idf.yaml b/tests/components/ft63x6/test.esp32-c3-idf.yaml index 397ac1e464..febf38d8e1 100644 --- a/tests/components/ft63x6/test.esp32-c3-idf.yaml +++ b/tests/components/ft63x6/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - scl_pin: GPIO0 - sda_pin: GPIO1 interrupt_pin: GPIO2 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ft63x6/test.esp32-idf.yaml b/tests/components/ft63x6/test.esp32-idf.yaml index 47b5796e8b..590f6a919c 100644 --- a/tests/components/ft63x6/test.esp32-idf.yaml +++ b/tests/components/ft63x6/test.esp32-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO0 - mosi_pin: GPIO2 - scl_pin: GPIO13 - sda_pin: GPIO14 interrupt_pin: GPIO15 - reset_pin: GPIO16 + reset_pin: GPIO4 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ft63x6/test.esp8266-ard.yaml b/tests/components/ft63x6/test.esp8266-ard.yaml index a4223733af..d6b6903029 100644 --- a/tests/components/ft63x6/test.esp8266-ard.yaml +++ b/tests/components/ft63x6/test.esp8266-ard.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - scl_pin: GPIO5 - sda_pin: GPIO4 interrupt_pin: GPIO12 reset_pin: GPIO16 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ft63x6/test.rp2040-ard.yaml b/tests/components/ft63x6/test.rp2040-ard.yaml index 397ac1e464..2dd70ff33a 100644 --- a/tests/components/ft63x6/test.rp2040-ard.yaml +++ b/tests/components/ft63x6/test.rp2040-ard.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - scl_pin: GPIO0 - sda_pin: GPIO1 interrupt_pin: GPIO2 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/gcja5/common.yaml b/tests/components/gcja5/common.yaml index 8f6250045d..64374c0e50 100644 --- a/tests/components/gcja5/common.yaml +++ b/tests/components/gcja5/common.yaml @@ -1,12 +1,6 @@ -uart: - - id: uart_gcja5 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - parity: EVEN - sensor: - platform: gcja5 + uart_id: uart_bus pm_1_0: name: "Particulate Matter <1.0µm Concentration" pm_2_5: diff --git a/tests/components/gcja5/test.esp32-c3-idf.yaml b/tests/components/gcja5/test.esp32-c3-idf.yaml index b516342f3b..236529042f 100644 --- a/tests/components/gcja5/test.esp32-c3-idf.yaml +++ b/tests/components/gcja5/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart_9600_even: !include ../../test_build_components/common/uart_9600_even/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/gcja5/test.esp32-idf.yaml b/tests/components/gcja5/test.esp32-idf.yaml index 811f6b72a6..5ce1861902 100644 --- a/tests/components/gcja5/test.esp32-idf.yaml +++ b/tests/components/gcja5/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 +packages: + uart_9600_even: !include ../../test_build_components/common/uart_9600_even/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/gcja5/test.esp8266-ard.yaml b/tests/components/gcja5/test.esp8266-ard.yaml index b516342f3b..a3f8cf43d4 100644 --- a/tests/components/gcja5/test.esp8266-ard.yaml +++ b/tests/components/gcja5/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart_9600_even: !include ../../test_build_components/common/uart_9600_even/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/gcja5/test.rp2040-ard.yaml b/tests/components/gcja5/test.rp2040-ard.yaml index b516342f3b..7c1f4f41e2 100644 --- a/tests/components/gcja5/test.rp2040-ard.yaml +++ b/tests/components/gcja5/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart_9600_even: !include ../../test_build_components/common/uart_9600_even/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/gdk101/common.yaml b/tests/components/gdk101/common.yaml index 7805ad43f0..4eb5586ade 100644 --- a/tests/components/gdk101/common.yaml +++ b/tests/components/gdk101/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_gdk101 - scl: ${scl_pin} - sda: ${sda_pin} - gdk101: + i2c_id: i2c_bus id: my_gdk101 sensor: diff --git a/tests/components/gdk101/test.esp32-idf.yaml b/tests/components/gdk101/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/gdk101/test.esp32-idf.yaml +++ b/tests/components/gdk101/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/gdk101/test.esp8266-ard.yaml b/tests/components/gdk101/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/gdk101/test.esp8266-ard.yaml +++ b/tests/components/gdk101/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/gdk101/test.rp2040-ard.yaml b/tests/components/gdk101/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/gdk101/test.rp2040-ard.yaml +++ b/tests/components/gdk101/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/gl_r01_i2c/common.yaml b/tests/components/gl_r01_i2c/common.yaml index fe0705bdc6..272a94d349 100644 --- a/tests/components/gl_r01_i2c/common.yaml +++ b/tests/components/gl_r01_i2c/common.yaml @@ -1,12 +1,7 @@ -i2c: - - id: i2c_gl_r01_i2c - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: gl_r01_i2c + i2c_id: i2c_bus id: tof name: "ToF sensor" - i2c_id: i2c_gl_r01_i2c address: 0x74 update_interval: 15s diff --git a/tests/components/gl_r01_i2c/test.esp32-c3-idf.yaml b/tests/components/gl_r01_i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/gl_r01_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/gl_r01_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/gl_r01_i2c/test.esp32-idf.yaml b/tests/components/gl_r01_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/gl_r01_i2c/test.esp32-idf.yaml +++ b/tests/components/gl_r01_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/gl_r01_i2c/test.esp8266-ard.yaml b/tests/components/gl_r01_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/gl_r01_i2c/test.esp8266-ard.yaml +++ b/tests/components/gl_r01_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/gl_r01_i2c/test.rp2040-ard.yaml b/tests/components/gl_r01_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/gl_r01_i2c/test.rp2040-ard.yaml +++ b/tests/components/gl_r01_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/gp2y1010au0f/test.esp8266-ard.yaml b/tests/components/gp2y1010au0f/test.esp8266-ard.yaml index a61053426a..c6a7c7bf13 100644 --- a/tests/components/gp2y1010au0f/test.esp8266-ard.yaml +++ b/tests/components/gp2y1010au0f/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: adc_pin: A0 - output_pin: GPIO5 + output_pin: GPIO0 <<: !include common.yaml diff --git a/tests/components/gp8403/common.yaml b/tests/components/gp8403/common.yaml index 7074185273..b04fbc4fbc 100644 --- a/tests/components/gp8403/common.yaml +++ b/tests/components/gp8403/common.yaml @@ -1,12 +1,9 @@ -i2c: - - id: i2c_gp8403 - scl: ${scl_pin} - sda: ${sda_pin} - gp8403: - id: gp8403_5v + i2c_id: i2c_bus voltage: 5V - id: gp8403_10v + i2c_id: i2c_bus voltage: 10V output: diff --git a/tests/components/gp8403/test.esp32-c3-idf.yaml b/tests/components/gp8403/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/gp8403/test.esp32-c3-idf.yaml +++ b/tests/components/gp8403/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/gp8403/test.esp32-idf.yaml b/tests/components/gp8403/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/gp8403/test.esp32-idf.yaml +++ b/tests/components/gp8403/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/gp8403/test.esp8266-ard.yaml b/tests/components/gp8403/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/gp8403/test.esp8266-ard.yaml +++ b/tests/components/gp8403/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/gp8403/test.rp2040-ard.yaml b/tests/components/gp8403/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/gp8403/test.rp2040-ard.yaml +++ b/tests/components/gp8403/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/gpio/test.esp8266-ard.yaml b/tests/components/gpio/test.esp8266-ard.yaml index 09f41abb79..e1660ec47c 100644 --- a/tests/components/gpio/test.esp8266-ard.yaml +++ b/tests/components/gpio/test.esp8266-ard.yaml @@ -1,6 +1,6 @@ substitutions: - binary_sensor_pin: GPIO12 - output_pin: GPIO13 - switch_pin: GPIO14 + binary_sensor_pin: GPIO0 + output_pin: GPIO2 + switch_pin: GPIO15 <<: !include common.yaml diff --git a/tests/components/gps/common.yaml b/tests/components/gps/common.yaml index 53dc67e457..a99e3ef7e0 100644 --- a/tests/components/gps/common.yaml +++ b/tests/components/gps/common.yaml @@ -1,10 +1,3 @@ -uart: - - id: uart_gps - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - parity: EVEN - gps: latitude: name: "Latitude" diff --git a/tests/components/gps/test.esp32-c3-idf.yaml b/tests/components/gps/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/gps/test.esp32-c3-idf.yaml +++ b/tests/components/gps/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/gps/test.esp32-idf.yaml b/tests/components/gps/test.esp32-idf.yaml index 811f6b72a6..64baa4ec9d 100644 --- a/tests/components/gps/test.esp32-idf.yaml +++ b/tests/components/gps/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO12 rx_pin: GPIO14 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/gps/test.esp8266-ard.yaml b/tests/components/gps/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/gps/test.esp8266-ard.yaml +++ b/tests/components/gps/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/gps/test.rp2040-ard.yaml b/tests/components/gps/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/gps/test.rp2040-ard.yaml +++ b/tests/components/gps/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/graph/common.yaml b/tests/components/graph/common.yaml index d2a6a9422d..578de5a60c 100644 --- a/tests/components/graph/common.yaml +++ b/tests/components/graph/common.yaml @@ -1,8 +1,3 @@ -i2c: - - id: i2c_graph - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: template id: some_sensor @@ -20,6 +15,6 @@ display: model: SSD1306_128X64 reset_pin: ${reset_pin} pages: - - id: page1 + - id: graph_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/graph/test.esp32-c3-idf.yaml b/tests/components/graph/test.esp32-c3-idf.yaml index 1e6670c196..c97f30d52c 100644 --- a/tests/components/graph/test.esp32-c3-idf.yaml +++ b/tests/components/graph/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/graph/test.esp32-idf.yaml b/tests/components/graph/test.esp32-idf.yaml index 1ca773e06c..4ff2241ec9 100644 --- a/tests/components/graph/test.esp32-idf.yaml +++ b/tests/components/graph/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/graph/test.esp8266-ard.yaml b/tests/components/graph/test.esp8266-ard.yaml index dfdc12a3d1..b8bb94edde 100644 --- a/tests/components/graph/test.esp8266-ard.yaml +++ b/tests/components/graph/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/graph/test.rp2040-ard.yaml b/tests/components/graph/test.rp2040-ard.yaml index 1e6670c196..1bf10642c5 100644 --- a/tests/components/graph/test.rp2040-ard.yaml +++ b/tests/components/graph/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/graphical_display_menu/common.yaml b/tests/components/graphical_display_menu/common.yaml index d979a6a30e..0b8f20d64b 100644 --- a/tests/components/graphical_display_menu/common.yaml +++ b/tests/components/graphical_display_menu/common.yaml @@ -1,15 +1,10 @@ -i2c: - - id: i2c_graphical_display_menu - scl: ${scl_pin} - sda: ${sda_pin} - display: - platform: ssd1306_i2c id: ssd1306_display model: SSD1306_128X64 reset_pin: ${reset_pin} pages: - - id: page1 + - id: graphical_display_menu_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml b/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml index 1e6670c196..c97f30d52c 100644 --- a/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml +++ b/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.esp32-idf.yaml b/tests/components/graphical_display_menu/test.esp32-idf.yaml index 1ca773e06c..4ff2241ec9 100644 --- a/tests/components/graphical_display_menu/test.esp32-idf.yaml +++ b/tests/components/graphical_display_menu/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.esp8266-ard.yaml b/tests/components/graphical_display_menu/test.esp8266-ard.yaml index dfdc12a3d1..b8bb94edde 100644 --- a/tests/components/graphical_display_menu/test.esp8266-ard.yaml +++ b/tests/components/graphical_display_menu/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.rp2040-ard.yaml b/tests/components/graphical_display_menu/test.rp2040-ard.yaml index 1e6670c196..1bf10642c5 100644 --- a/tests/components/graphical_display_menu/test.rp2040-ard.yaml +++ b/tests/components/graphical_display_menu/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/common.yaml b/tests/components/grove_gas_mc_v2/common.yaml index dbdb950033..0729e6b9c7 100644 --- a/tests/components/grove_gas_mc_v2/common.yaml +++ b/tests/components/grove_gas_mc_v2/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_grove_gas_mc_v2 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: grove_gas_mc_v2 + i2c_id: i2c_bus nitrogen_dioxide: name: "Nitrogen Dioxide" ethanol: diff --git a/tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml b/tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml +++ b/tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml b/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml +++ b/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml b/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml +++ b/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml b/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml +++ b/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/common.yaml b/tests/components/grove_tb6612fng/common.yaml index 0db6c00bf7..52d5ead96e 100644 --- a/tests/components/grove_tb6612fng/common.yaml +++ b/tests/components/grove_tb6612fng/common.yaml @@ -13,11 +13,7 @@ esphome: channel: 1 id: test_motor -i2c: - - id: i2c_grove_tb6612fng - scl: ${scl_pin} - sda: ${sda_pin} - grove_tb6612fng: id: test_motor + i2c_id: i2c_bus address: 0x14 diff --git a/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml b/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml +++ b/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.esp32-idf.yaml b/tests/components/grove_tb6612fng/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/grove_tb6612fng/test.esp32-idf.yaml +++ b/tests/components/grove_tb6612fng/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.esp8266-ard.yaml b/tests/components/grove_tb6612fng/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/grove_tb6612fng/test.esp8266-ard.yaml +++ b/tests/components/grove_tb6612fng/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.rp2040-ard.yaml b/tests/components/grove_tb6612fng/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/grove_tb6612fng/test.rp2040-ard.yaml +++ b/tests/components/grove_tb6612fng/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/growatt_solar/common.yaml b/tests/components/growatt_solar/common.yaml index 7cc6b2a139..f304e84fcf 100644 --- a/tests/components/growatt_solar/common.yaml +++ b/tests/components/growatt_solar/common.yaml @@ -1,14 +1,6 @@ -uart: - - id: uart_growatt_solar - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - -modbus: - flow_control_pin: ${flow_control_pin} - sensor: - platform: growatt_solar + modbus_id: modbus_bus update_interval: 10s protocol_version: RTU inverter_status: diff --git a/tests/components/growatt_solar/test.esp32-c3-idf.yaml b/tests/components/growatt_solar/test.esp32-c3-idf.yaml index 452031a5aa..17940aafcf 100644 --- a/tests/components/growatt_solar/test.esp32-c3-idf.yaml +++ b/tests/components/growatt_solar/test.esp32-c3-idf.yaml @@ -1,6 +1,6 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 flow_control_pin: GPIO3 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/growatt_solar/test.esp32-idf.yaml b/tests/components/growatt_solar/test.esp32-idf.yaml index bd767a8ece..a755bfa66a 100644 --- a/tests/components/growatt_solar/test.esp32-idf.yaml +++ b/tests/components/growatt_solar/test.esp32-idf.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO14 flow_control_pin: GPIO13 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/growatt_solar/test.esp8266-ard.yaml b/tests/components/growatt_solar/test.esp8266-ard.yaml index 29c98d0957..6daa08c22b 100644 --- a/tests/components/growatt_solar/test.esp8266-ard.yaml +++ b/tests/components/growatt_solar/test.esp8266-ard.yaml @@ -1,6 +1,9 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - flow_control_pin: GPIO13 + tx_pin: GPIO0 + rx_pin: GPIO2 + flow_control_pin: GPIO15 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/growatt_solar/test.rp2040-ard.yaml b/tests/components/growatt_solar/test.rp2040-ard.yaml index 452031a5aa..a00283a9e0 100644 --- a/tests/components/growatt_solar/test.rp2040-ard.yaml +++ b/tests/components/growatt_solar/test.rp2040-ard.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO5 flow_control_pin: GPIO3 +packages: + modbus: !include ../../test_build_components/common/modbus/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/gt911/common.yaml b/tests/components/gt911/common.yaml index 7bb88108da..5f9748afb6 100644 --- a/tests/components/gt911/common.yaml +++ b/tests/components/gt911/common.yaml @@ -1,23 +1,21 @@ -i2c: - - id: i2c_gt911 - scl: 5 - sda: 4 - display: - platform: ssd1306_i2c + i2c_id: i2c_bus id: ssd1306_display model: SSD1306_128X64 - reset_pin: 10 + reset_pin: ${display_reset_pin} pages: - - id: page1 + - id: gt911_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); touchscreen: - platform: gt911 + i2c_id: i2c_bus + id: gt911_touchscreen display: ssd1306_display - interrupt_pin: 20 - reset_pin: 21 + interrupt_pin: ${interrupt_pin} + reset_pin: ${reset_pin} binary_sensor: - platform: gt911 diff --git a/tests/components/gt911/test.esp32-c3-idf.yaml b/tests/components/gt911/test.esp32-c3-idf.yaml index dade44d145..5e15963b7e 100644 --- a/tests/components/gt911/test.esp32-c3-idf.yaml +++ b/tests/components/gt911/test.esp32-c3-idf.yaml @@ -1 +1,9 @@ +substitutions: + display_reset_pin: "18" + interrupt_pin: "20" + reset_pin: "21" + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/gt911/test.esp32-idf.yaml b/tests/components/gt911/test.esp32-idf.yaml index dade44d145..3bce86d9a3 100644 --- a/tests/components/gt911/test.esp32-idf.yaml +++ b/tests/components/gt911/test.esp32-idf.yaml @@ -1 +1,9 @@ +substitutions: + display_reset_pin: "10" + interrupt_pin: "20" + reset_pin: "21" + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/gt911/test.esp8266-ard.yaml b/tests/components/gt911/test.esp8266-ard.yaml index 8b76eff29e..c3bc159b5b 100644 --- a/tests/components/gt911/test.esp8266-ard.yaml +++ b/tests/components/gt911/test.esp8266-ard.yaml @@ -1,24 +1,9 @@ -i2c: - - id: i2c_gt911 - scl: 5 - sda: 4 +substitutions: + display_reset_pin: "10" + interrupt_pin: "12" + reset_pin: "13" -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml -touchscreen: - - platform: gt911 - display: ssd1306_display - interrupt_pin: 12 - -binary_sensor: - - platform: gt911 - id: touch_key_911 - index: 0 +<<: !include common.yaml diff --git a/tests/components/gt911/test.rp2040-ard.yaml b/tests/components/gt911/test.rp2040-ard.yaml index dade44d145..0c7f0bc504 100644 --- a/tests/components/gt911/test.rp2040-ard.yaml +++ b/tests/components/gt911/test.rp2040-ard.yaml @@ -1 +1,9 @@ +substitutions: + display_reset_pin: "10" + interrupt_pin: "20" + reset_pin: "21" + +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/haier/common.yaml b/tests/components/haier/common.yaml index 368b88b69c..926a1d1b7a 100644 --- a/tests/components/haier/common.yaml +++ b/tests/components/haier/common.yaml @@ -2,16 +2,9 @@ wifi: ssid: MySSID password: password1 -uart: - - id: uart_haier - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - climate: - platform: haier id: haier_ac - uart_id: uart_haier protocol: hOn name: Haier AC wifi_signal: true diff --git a/tests/components/haier/test.esp32-c3-idf.yaml b/tests/components/haier/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/haier/test.esp32-c3-idf.yaml +++ b/tests/components/haier/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/haier/test.esp32-idf.yaml b/tests/components/haier/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/haier/test.esp32-idf.yaml +++ b/tests/components/haier/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/haier/test.esp8266-ard.yaml b/tests/components/haier/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/haier/test.esp8266-ard.yaml +++ b/tests/components/haier/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/haier/test.rp2040-ard.yaml b/tests/components/haier/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/haier/test.rp2040-ard.yaml +++ b/tests/components/haier/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/havells_solar/common.yaml b/tests/components/havells_solar/common.yaml index 370b0d357d..2f00f61f14 100644 --- a/tests/components/havells_solar/common.yaml +++ b/tests/components/havells_solar/common.yaml @@ -1,14 +1,6 @@ -uart: - - id: uart_havells_solar - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - -modbus: - flow_control_pin: ${flow_control_pin} - sensor: - platform: havells_solar + modbus_id: modbus_bus update_interval: 60s phase_a: voltage: diff --git a/tests/components/havells_solar/test.esp32-c3-idf.yaml b/tests/components/havells_solar/test.esp32-c3-idf.yaml index 452031a5aa..17940aafcf 100644 --- a/tests/components/havells_solar/test.esp32-c3-idf.yaml +++ b/tests/components/havells_solar/test.esp32-c3-idf.yaml @@ -1,6 +1,6 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 flow_control_pin: GPIO3 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/havells_solar/test.esp32-idf.yaml b/tests/components/havells_solar/test.esp32-idf.yaml index bd767a8ece..a755bfa66a 100644 --- a/tests/components/havells_solar/test.esp32-idf.yaml +++ b/tests/components/havells_solar/test.esp32-idf.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO14 flow_control_pin: GPIO13 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/havells_solar/test.esp8266-ard.yaml b/tests/components/havells_solar/test.esp8266-ard.yaml index 29c98d0957..6daa08c22b 100644 --- a/tests/components/havells_solar/test.esp8266-ard.yaml +++ b/tests/components/havells_solar/test.esp8266-ard.yaml @@ -1,6 +1,9 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - flow_control_pin: GPIO13 + tx_pin: GPIO0 + rx_pin: GPIO2 + flow_control_pin: GPIO15 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/havells_solar/test.rp2040-ard.yaml b/tests/components/havells_solar/test.rp2040-ard.yaml index 452031a5aa..a00283a9e0 100644 --- a/tests/components/havells_solar/test.rp2040-ard.yaml +++ b/tests/components/havells_solar/test.rp2040-ard.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO5 flow_control_pin: GPIO3 +packages: + modbus: !include ../../test_build_components/common/modbus/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/hbridge/common.yaml b/tests/components/hbridge/common.yaml index 0504cdea03..ca619d6851 100644 --- a/tests/components/hbridge/common.yaml +++ b/tests/components/hbridge/common.yaml @@ -31,9 +31,3 @@ fan: on_preset_set: then: - logger.log: Preset mode was changed! - -switch: - - platform: hbridge - id: switch_hbridge - on_pin: ${hbridge_on_pin} - off_pin: ${hbridge_off_pin} diff --git a/tests/components/hbridge/test.esp32-c3-idf.yaml b/tests/components/hbridge/test.esp32-c3-idf.yaml index c73f08b6de..93a6cb5818 100644 --- a/tests/components/hbridge/test.esp32-c3-idf.yaml +++ b/tests/components/hbridge/test.esp32-c3-idf.yaml @@ -7,9 +7,11 @@ substitutions: hbridge_on_pin: "2" hbridge_off_pin: "3" -packages: - common: !include common.yaml +<<: !include common.yaml switch: - - id: !extend switch_hbridge + - platform: hbridge + id: switch_hbridge + on_pin: ${hbridge_on_pin} + off_pin: ${hbridge_off_pin} pulse_length: 60ms diff --git a/tests/components/hbridge/test.esp32-idf.yaml b/tests/components/hbridge/test.esp32-idf.yaml index dbbfa738c7..7f3d9b928a 100644 --- a/tests/components/hbridge/test.esp32-idf.yaml +++ b/tests/components/hbridge/test.esp32-idf.yaml @@ -7,10 +7,12 @@ substitutions: hbridge_on_pin: "4" hbridge_off_pin: "5" -packages: - common: !include common.yaml +<<: !include common.yaml switch: - - id: !extend switch_hbridge + - platform: hbridge + id: switch_hbridge + on_pin: ${hbridge_on_pin} + off_pin: ${hbridge_off_pin} pulse_length: 60ms wait_time: 10ms diff --git a/tests/components/hbridge/test.esp8266-ard.yaml b/tests/components/hbridge/test.esp8266-ard.yaml index f560da5d38..0346c8801d 100644 --- a/tests/components/hbridge/test.esp8266-ard.yaml +++ b/tests/components/hbridge/test.esp8266-ard.yaml @@ -7,10 +7,12 @@ substitutions: hbridge_on_pin: "14" hbridge_off_pin: "15" -packages: - common: !include common.yaml +<<: !include common.yaml switch: - - id: !extend switch_hbridge + - platform: hbridge + id: switch_hbridge + on_pin: ${hbridge_on_pin} + off_pin: ${hbridge_off_pin} pulse_length: 60ms wait_time: 10ms diff --git a/tests/components/hbridge/test.rp2040-ard.yaml b/tests/components/hbridge/test.rp2040-ard.yaml index aa6e290cab..f9cb2b5618 100644 --- a/tests/components/hbridge/test.rp2040-ard.yaml +++ b/tests/components/hbridge/test.rp2040-ard.yaml @@ -7,10 +7,12 @@ substitutions: hbridge_on_pin: "2" hbridge_off_pin: "3" -packages: - common: !include common.yaml +<<: !include common.yaml switch: - - id: !extend switch_hbridge + - platform: hbridge + id: switch_hbridge + on_pin: ${hbridge_on_pin} + off_pin: ${hbridge_off_pin} wait_time: 10ms optimistic: true diff --git a/tests/components/hdc1080/common.yaml b/tests/components/hdc1080/common.yaml index d260d4737c..a559392cb1 100644 --- a/tests/components/hdc1080/common.yaml +++ b/tests/components/hdc1080/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_hdc1080 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: hdc1080 + i2c_id: i2c_bus temperature: name: Temperature humidity: diff --git a/tests/components/hdc1080/test.esp32-c3-idf.yaml b/tests/components/hdc1080/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/hdc1080/test.esp32-c3-idf.yaml +++ b/tests/components/hdc1080/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/hdc1080/test.esp32-idf.yaml b/tests/components/hdc1080/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/hdc1080/test.esp32-idf.yaml +++ b/tests/components/hdc1080/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/hdc1080/test.esp8266-ard.yaml b/tests/components/hdc1080/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/hdc1080/test.esp8266-ard.yaml +++ b/tests/components/hdc1080/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/hdc1080/test.rp2040-ard.yaml b/tests/components/hdc1080/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/hdc1080/test.rp2040-ard.yaml +++ b/tests/components/hdc1080/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/he60r/common.yaml b/tests/components/he60r/common.yaml index e10e745f57..ab5ec67f0e 100644 --- a/tests/components/he60r/common.yaml +++ b/tests/components/he60r/common.yaml @@ -1,10 +1,3 @@ -uart: - - id: uart_he60r - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 1200 - parity: EVEN - cover: - platform: he60r id: garage_door diff --git a/tests/components/he60r/test.esp32-c3-idf.yaml b/tests/components/he60r/test.esp32-c3-idf.yaml index b516342f3b..b0c8c5de3d 100644 --- a/tests/components/he60r/test.esp32-c3-idf.yaml +++ b/tests/components/he60r/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart_1200_even: !include ../../test_build_components/common/uart_1200_even/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/he60r/test.esp32-idf.yaml b/tests/components/he60r/test.esp32-idf.yaml index f486544afa..f52c2a75f8 100644 --- a/tests/components/he60r/test.esp32-idf.yaml +++ b/tests/components/he60r/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 +packages: + uart_1200_even: !include ../../test_build_components/common/uart_1200_even/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/he60r/test.esp8266-ard.yaml b/tests/components/he60r/test.esp8266-ard.yaml index b516342f3b..e28024fa4d 100644 --- a/tests/components/he60r/test.esp8266-ard.yaml +++ b/tests/components/he60r/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart_1200_even: !include ../../test_build_components/common/uart_1200_even/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/he60r/test.rp2040-ard.yaml b/tests/components/he60r/test.rp2040-ard.yaml index b516342f3b..a576b03d63 100644 --- a/tests/components/he60r/test.rp2040-ard.yaml +++ b/tests/components/he60r/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart_1200_even: !include ../../test_build_components/common/uart_1200_even/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/hlw8012/test.esp8266-ard.yaml b/tests/components/hlw8012/test.esp8266-ard.yaml index 8b42b21b54..ec9c0e43dc 100644 --- a/tests/components/hlw8012/test.esp8266-ard.yaml +++ b/tests/components/hlw8012/test.esp8266-ard.yaml @@ -1,6 +1,6 @@ substitutions: - sel_pin: GPIO12 - cf_pin: GPIO13 - cf1_pin: GPIO14 + sel_pin: GPIO0 + cf_pin: GPIO2 + cf1_pin: GPIO15 <<: !include common.yaml diff --git a/tests/components/hm3301/common.yaml b/tests/components/hm3301/common.yaml index b533130569..a56ac7bc65 100644 --- a/tests/components/hm3301/common.yaml +++ b/tests/components/hm3301/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_hm3301 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: hm3301 + i2c_id: i2c_bus pm_1_0: name: PM1.0 pm_2_5: diff --git a/tests/components/hm3301/test.esp32-c3-idf.yaml b/tests/components/hm3301/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/hm3301/test.esp32-c3-idf.yaml +++ b/tests/components/hm3301/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/hm3301/test.esp32-idf.yaml b/tests/components/hm3301/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/hm3301/test.esp32-idf.yaml +++ b/tests/components/hm3301/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/hm3301/test.esp8266-ard.yaml b/tests/components/hm3301/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/hm3301/test.esp8266-ard.yaml +++ b/tests/components/hm3301/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/hm3301/test.rp2040-ard.yaml b/tests/components/hm3301/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/hm3301/test.rp2040-ard.yaml +++ b/tests/components/hm3301/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/hmc5883l/common.yaml b/tests/components/hmc5883l/common.yaml index 1c90f5f1c6..19e4ba4c45 100644 --- a/tests/components/hmc5883l/common.yaml +++ b/tests/components/hmc5883l/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_hmc5883l - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: hmc5883l + i2c_id: i2c_bus address: 0x68 field_strength_x: name: HMC5883L Field Strength X diff --git a/tests/components/hmc5883l/test.esp32-c3-idf.yaml b/tests/components/hmc5883l/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/hmc5883l/test.esp32-c3-idf.yaml +++ b/tests/components/hmc5883l/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/hmc5883l/test.esp32-idf.yaml b/tests/components/hmc5883l/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/hmc5883l/test.esp32-idf.yaml +++ b/tests/components/hmc5883l/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/hmc5883l/test.esp8266-ard.yaml b/tests/components/hmc5883l/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/hmc5883l/test.esp8266-ard.yaml +++ b/tests/components/hmc5883l/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/hmc5883l/test.rp2040-ard.yaml b/tests/components/hmc5883l/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/hmc5883l/test.rp2040-ard.yaml +++ b/tests/components/hmc5883l/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/common.yaml b/tests/components/honeywell_hih_i2c/common.yaml index a5f3eef187..de588a30bd 100644 --- a/tests/components/honeywell_hih_i2c/common.yaml +++ b/tests/components/honeywell_hih_i2c/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_honeywell_hih - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: honeywell_hih_i2c + i2c_id: i2c_bus temperature: name: Temperature humidity: diff --git a/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml b/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml b/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml +++ b/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.esp8266-ard.yaml b/tests/components/honeywell_hih_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/honeywell_hih_i2c/test.esp8266-ard.yaml +++ b/tests/components/honeywell_hih_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.rp2040-ard.yaml b/tests/components/honeywell_hih_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/honeywell_hih_i2c/test.rp2040-ard.yaml +++ b/tests/components/honeywell_hih_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/honeywellabp/common.yaml b/tests/components/honeywellabp/common.yaml index 21a3ef6ee3..f82d289ace 100644 --- a/tests/components/honeywellabp/common.yaml +++ b/tests/components/honeywellabp/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_bme280 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: honeywellabp cs_pin: ${cs_pin} diff --git a/tests/components/honeywellabp/test.esp32-c3-idf.yaml b/tests/components/honeywellabp/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/honeywellabp/test.esp32-c3-idf.yaml +++ b/tests/components/honeywellabp/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/honeywellabp/test.esp32-idf.yaml b/tests/components/honeywellabp/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/honeywellabp/test.esp32-idf.yaml +++ b/tests/components/honeywellabp/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/honeywellabp/test.esp8266-ard.yaml b/tests/components/honeywellabp/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/honeywellabp/test.esp8266-ard.yaml +++ b/tests/components/honeywellabp/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/honeywellabp/test.rp2040-ard.yaml b/tests/components/honeywellabp/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/honeywellabp/test.rp2040-ard.yaml +++ b/tests/components/honeywellabp/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/common.yaml b/tests/components/honeywellabp2_i2c/common.yaml index 6752a69866..e1b060edcf 100644 --- a/tests/components/honeywellabp2_i2c/common.yaml +++ b/tests/components/honeywellabp2_i2c/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_honeywellabp2 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: honeywellabp2_i2c + i2c_id: i2c_bus address: 0x28 pressure: name: Honeywell2 pressure diff --git a/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml b/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml b/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml +++ b/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.esp8266-ard.yaml b/tests/components/honeywellabp2_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/honeywellabp2_i2c/test.esp8266-ard.yaml +++ b/tests/components/honeywellabp2_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.rp2040-ard.yaml b/tests/components/honeywellabp2_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/honeywellabp2_i2c/test.rp2040-ard.yaml +++ b/tests/components/honeywellabp2_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/common.yaml b/tests/components/hrxl_maxsonar_wr/common.yaml index d74ada716d..84376aa5ba 100644 --- a/tests/components/hrxl_maxsonar_wr/common.yaml +++ b/tests/components/hrxl_maxsonar_wr/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_hrxl_maxsonar_wr - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 115200 - sensor: - platform: hrxl_maxsonar_wr id: hrxl_maxsonar_wr_sensor diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml +++ b/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml index 811f6b72a6..64baa4ec9d 100644 --- a/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml +++ b/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO12 rx_pin: GPIO14 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.esp8266-ard.yaml b/tests/components/hrxl_maxsonar_wr/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/hrxl_maxsonar_wr/test.esp8266-ard.yaml +++ b/tests/components/hrxl_maxsonar_wr/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.rp2040-ard.yaml b/tests/components/hrxl_maxsonar_wr/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/hrxl_maxsonar_wr/test.rp2040-ard.yaml +++ b/tests/components/hrxl_maxsonar_wr/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/hte501/common.yaml b/tests/components/hte501/common.yaml index 7c57b5bc6a..e0641de193 100644 --- a/tests/components/hte501/common.yaml +++ b/tests/components/hte501/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_hte501 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: hte501 + i2c_id: i2c_bus address: 0x40 temperature: name: Temperature diff --git a/tests/components/hte501/test.esp32-c3-idf.yaml b/tests/components/hte501/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/hte501/test.esp32-c3-idf.yaml +++ b/tests/components/hte501/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/hte501/test.esp32-idf.yaml b/tests/components/hte501/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/hte501/test.esp32-idf.yaml +++ b/tests/components/hte501/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/hte501/test.esp8266-ard.yaml b/tests/components/hte501/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/hte501/test.esp8266-ard.yaml +++ b/tests/components/hte501/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/hte501/test.rp2040-ard.yaml b/tests/components/hte501/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/hte501/test.rp2040-ard.yaml +++ b/tests/components/hte501/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/http_request/common.yaml b/tests/components/http_request/common.yaml index 97961007e2..9ff9f9fb67 100644 --- a/tests/components/http_request/common.yaml +++ b/tests/components/http_request/common.yaml @@ -51,6 +51,7 @@ script: ota: - platform: http_request + id: http_request_ota on_begin: then: - logger.log: "OTA start" @@ -77,10 +78,12 @@ button: on_press: then: - ota.http_request.flash: + id: http_request_ota md5_url: http://my.ha.net:8123/local/esphome/firmware.md5 url: http://my.ha.net:8123/local/esphome/firmware.bin - ota.http_request.flash: + id: http_request_ota md5: 0123456789abcdef0123456789abcdef url: http://my.ha.net:8123/local/esphome/firmware.bin @@ -90,6 +93,7 @@ update: - platform: http_request name: OTA Update id: ota_update + ota_id: http_request_ota source: http://my.ha.net:8123/local/esphome/manifest.json on_update_available: - logger.log: "A new update is available" diff --git a/tests/components/htu21d/common.yaml b/tests/components/htu21d/common.yaml index f12c1ca46e..ad4b23d460 100644 --- a/tests/components/htu21d/common.yaml +++ b/tests/components/htu21d/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_htu21d - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: htu21d + i2c_id: i2c_bus model: htu21d temperature: name: Temperature diff --git a/tests/components/htu21d/test.esp32-c3-idf.yaml b/tests/components/htu21d/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/htu21d/test.esp32-c3-idf.yaml +++ b/tests/components/htu21d/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/htu21d/test.esp32-idf.yaml b/tests/components/htu21d/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/htu21d/test.esp32-idf.yaml +++ b/tests/components/htu21d/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/htu21d/test.esp8266-ard.yaml b/tests/components/htu21d/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/htu21d/test.esp8266-ard.yaml +++ b/tests/components/htu21d/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/htu21d/test.rp2040-ard.yaml b/tests/components/htu21d/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/htu21d/test.rp2040-ard.yaml +++ b/tests/components/htu21d/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/htu31d/common.yaml b/tests/components/htu31d/common.yaml index 735cdc7cbf..41d718ea69 100644 --- a/tests/components/htu31d/common.yaml +++ b/tests/components/htu31d/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_htu31d - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: htu31d + i2c_id: i2c_bus temperature: name: Living Room Temperature humidity: diff --git a/tests/components/htu31d/test.esp32-c3-idf.yaml b/tests/components/htu31d/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/htu31d/test.esp32-c3-idf.yaml +++ b/tests/components/htu31d/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/htu31d/test.esp32-idf.yaml b/tests/components/htu31d/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/htu31d/test.esp32-idf.yaml +++ b/tests/components/htu31d/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/htu31d/test.esp8266-ard.yaml b/tests/components/htu31d/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/htu31d/test.esp8266-ard.yaml +++ b/tests/components/htu31d/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/htu31d/test.rp2040-ard.yaml b/tests/components/htu31d/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/htu31d/test.rp2040-ard.yaml +++ b/tests/components/htu31d/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/hx711/test.esp32-c3-idf.yaml b/tests/components/hx711/test.esp32-c3-idf.yaml index 08a6e705c0..defef165e3 100644 --- a/tests/components/hx711/test.esp32-c3-idf.yaml +++ b/tests/components/hx711/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - clk_pin: GPIO5 - dout_pin: GPIO4 + clk_pin: GPIO4 + dout_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/hx711/test.esp32-idf.yaml b/tests/components/hx711/test.esp32-idf.yaml index 6423867395..defef165e3 100644 --- a/tests/components/hx711/test.esp32-idf.yaml +++ b/tests/components/hx711/test.esp32-idf.yaml @@ -1,5 +1,5 @@ substitutions: - clk_pin: GPIO16 - dout_pin: GPIO17 + clk_pin: GPIO4 + dout_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/hx711/test.esp8266-ard.yaml b/tests/components/hx711/test.esp8266-ard.yaml index 08a6e705c0..defef165e3 100644 --- a/tests/components/hx711/test.esp8266-ard.yaml +++ b/tests/components/hx711/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clk_pin: GPIO5 - dout_pin: GPIO4 + clk_pin: GPIO4 + dout_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/hx711/test.rp2040-ard.yaml b/tests/components/hx711/test.rp2040-ard.yaml index 08a6e705c0..defef165e3 100644 --- a/tests/components/hx711/test.rp2040-ard.yaml +++ b/tests/components/hx711/test.rp2040-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clk_pin: GPIO5 - dout_pin: GPIO4 + clk_pin: GPIO4 + dout_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/common.yaml b/tests/components/hydreon_rgxx/common.yaml index e11c6c91c9..e96879fdec 100644 --- a/tests/components/hydreon_rgxx/common.yaml +++ b/tests/components/hydreon_rgxx/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_hydreon_rgxx - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 115200 - binary_sensor: - platform: hydreon_rgxx hydreon_rgxx_id: hydreon_rg9 diff --git a/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml b/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml +++ b/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.esp32-idf.yaml b/tests/components/hydreon_rgxx/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/hydreon_rgxx/test.esp32-idf.yaml +++ b/tests/components/hydreon_rgxx/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.esp8266-ard.yaml b/tests/components/hydreon_rgxx/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/hydreon_rgxx/test.esp8266-ard.yaml +++ b/tests/components/hydreon_rgxx/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.rp2040-ard.yaml b/tests/components/hydreon_rgxx/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/hydreon_rgxx/test.rp2040-ard.yaml +++ b/tests/components/hydreon_rgxx/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/hyt271/common.yaml b/tests/components/hyt271/common.yaml index 7a4371173f..5771d882da 100644 --- a/tests/components/hyt271/common.yaml +++ b/tests/components/hyt271/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_hyt271 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: hyt271 + i2c_id: i2c_bus temperature: name: Temperature humidity: diff --git a/tests/components/hyt271/test.esp32-c3-idf.yaml b/tests/components/hyt271/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/hyt271/test.esp32-c3-idf.yaml +++ b/tests/components/hyt271/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/hyt271/test.esp32-idf.yaml b/tests/components/hyt271/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/hyt271/test.esp32-idf.yaml +++ b/tests/components/hyt271/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/hyt271/test.esp8266-ard.yaml b/tests/components/hyt271/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/hyt271/test.esp8266-ard.yaml +++ b/tests/components/hyt271/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/hyt271/test.rp2040-ard.yaml b/tests/components/hyt271/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/hyt271/test.rp2040-ard.yaml +++ b/tests/components/hyt271/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/i2c/common.yaml b/tests/components/i2c/common.yaml index d70cd595ad..e69de29bb2 100644 --- a/tests/components/i2c/common.yaml +++ b/tests/components/i2c/common.yaml @@ -1,4 +0,0 @@ -i2c: - - id: i2c_i2c - scl: ${scl_pin} - sda: ${sda_pin} diff --git a/tests/components/i2c/test.esp32-ard.yaml b/tests/components/i2c/test.esp32-ard.yaml index 63c3bd6afd..7c503b0ccb 100644 --- a/tests/components/i2c/test.esp32-ard.yaml +++ b/tests/components/i2c/test.esp32-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml <<: !include common.yaml diff --git a/tests/components/i2c/test.esp32-c3-idf.yaml b/tests/components/i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/i2c/test.esp32-c3-idf.yaml +++ b/tests/components/i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/i2c/test.esp32-idf.yaml b/tests/components/i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/i2c/test.esp32-idf.yaml +++ b/tests/components/i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/i2c/test.esp8266-ard.yaml b/tests/components/i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/i2c/test.esp8266-ard.yaml +++ b/tests/components/i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/i2c/test.rp2040-ard.yaml b/tests/components/i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/i2c/test.rp2040-ard.yaml +++ b/tests/components/i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/i2c_device/common.yaml b/tests/components/i2c_device/common.yaml index 35a6f8f7f0..fed399eb8c 100644 --- a/tests/components/i2c_device/common.yaml +++ b/tests/components/i2c_device/common.yaml @@ -1,8 +1,4 @@ -i2c: - - id: i2c_i2c_device - scl: ${scl_pin} - sda: ${sda_pin} - i2c_device: id: i2cdev + i2c_id: i2c_bus address: 0x2C diff --git a/tests/components/i2c_device/test.esp32-c3-idf.yaml b/tests/components/i2c_device/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/i2c_device/test.esp32-c3-idf.yaml +++ b/tests/components/i2c_device/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/i2c_device/test.esp32-idf.yaml b/tests/components/i2c_device/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/i2c_device/test.esp32-idf.yaml +++ b/tests/components/i2c_device/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/i2c_device/test.esp8266-ard.yaml b/tests/components/i2c_device/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/i2c_device/test.esp8266-ard.yaml +++ b/tests/components/i2c_device/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/i2c_device/test.rp2040-ard.yaml b/tests/components/i2c_device/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/i2c_device/test.rp2040-ard.yaml +++ b/tests/components/i2c_device/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/i2s_audio/test.esp32-idf.yaml b/tests/components/i2s_audio/test.esp32-idf.yaml index ce751d7d4a..ead1eaa1e9 100644 --- a/tests/components/i2s_audio/test.esp32-idf.yaml +++ b/tests/components/i2s_audio/test.esp32-idf.yaml @@ -1,6 +1,9 @@ substitutions: i2s_bclk_pin: GPIO15 - i2s_lrclk_pin: GPIO16 - i2s_mclk_pin: GPIO17 + i2s_lrclk_pin: GPIO4 + i2s_mclk_pin: GPIO5 + +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/iaqcore/common.yaml b/tests/components/iaqcore/common.yaml index 927b836c52..e0f9d09a00 100644 --- a/tests/components/iaqcore/common.yaml +++ b/tests/components/iaqcore/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_iaqcore - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: iaqcore + i2c_id: i2c_bus co2: name: iAQ Core CO2 Sensor tvoc: diff --git a/tests/components/iaqcore/test.esp32-c3-idf.yaml b/tests/components/iaqcore/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/iaqcore/test.esp32-c3-idf.yaml +++ b/tests/components/iaqcore/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/iaqcore/test.esp32-idf.yaml b/tests/components/iaqcore/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/iaqcore/test.esp32-idf.yaml +++ b/tests/components/iaqcore/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/iaqcore/test.esp8266-ard.yaml b/tests/components/iaqcore/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/iaqcore/test.esp8266-ard.yaml +++ b/tests/components/iaqcore/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/iaqcore/test.rp2040-ard.yaml b/tests/components/iaqcore/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/iaqcore/test.rp2040-ard.yaml +++ b/tests/components/iaqcore/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ili9xxx/common.yaml b/tests/components/ili9xxx/common.yaml index b182d110cd..47384b1398 100644 --- a/tests/components/ili9xxx/common.yaml +++ b/tests/components/ili9xxx/common.yaml @@ -1,8 +1,3 @@ -spi: - - id: spi_main_lcd - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: ili9xxx invert_colors: true diff --git a/tests/components/ili9xxx/test.esp32-c3-idf.yaml b/tests/components/ili9xxx/test.esp32-c3-idf.yaml index 3037785e81..1eea1e85f7 100644 --- a/tests/components/ili9xxx/test.esp32-c3-idf.yaml +++ b/tests/components/ili9xxx/test.esp32-c3-idf.yaml @@ -1,12 +1,11 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin1: GPIO8 dc_pin1: GPIO9 reset_pin1: GPIO10 cs_pin2: GPIO2 dc_pin2: GPIO3 - reset_pin2: GPIO4 + reset_pin2: GPIO7 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ili9xxx/test.esp32-idf.yaml b/tests/components/ili9xxx/test.esp32-idf.yaml index 2e006d2521..866e57573b 100644 --- a/tests/components/ili9xxx/test.esp32-idf.yaml +++ b/tests/components/ili9xxx/test.esp32-idf.yaml @@ -1,6 +1,4 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin1: GPIO12 dc_pin1: GPIO13 reset_pin1: GPIO14 @@ -8,4 +6,7 @@ substitutions: dc_pin2: GPIO26 reset_pin2: GPIO27 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ili9xxx/test.esp8266-ard.yaml b/tests/components/ili9xxx/test.esp8266-ard.yaml index 5dee055fd4..feb7773794 100644 --- a/tests/components/ili9xxx/test.esp8266-ard.yaml +++ b/tests/components/ili9xxx/test.esp8266-ard.yaml @@ -9,4 +9,7 @@ substitutions: dc_pin2: GPIO4 reset_pin2: GPIO0 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ili9xxx/test.rp2040-ard.yaml b/tests/components/ili9xxx/test.rp2040-ard.yaml index 74c9b906e9..2acde3e629 100644 --- a/tests/components/ili9xxx/test.rp2040-ard.yaml +++ b/tests/components/ili9xxx/test.rp2040-ard.yaml @@ -9,4 +9,7 @@ substitutions: dc_pin2: GPIO21 reset_pin2: GPIO22 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/image/test.esp32-idf.yaml b/tests/components/image/test.esp32-idf.yaml index 814f16c36c..aea2b4bbb0 100644 --- a/tests/components/image/test.esp32-idf.yaml +++ b/tests/components/image/test.esp32-idf.yaml @@ -1,14 +1,12 @@ -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 - miso_pin: 18 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml display: - platform: ili9xxx id: main_lcd + spi_id: spi_bus model: ili9342 - cs_pin: 19 + cs_pin: 15 dc_pin: 13 reset_pin: 21 invert_colors: true diff --git a/tests/components/image/test.esp8266-ard.yaml b/tests/components/image/test.esp8266-ard.yaml index 626076d44e..2e7bfc5ae5 100644 --- a/tests/components/image/test.esp8266-ard.yaml +++ b/tests/components/image/test.esp8266-ard.yaml @@ -1,12 +1,10 @@ -spi: - - id: spi_main_lcd - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml display: - platform: ili9xxx id: main_lcd + spi_id: spi_bus model: ili9342 cs_pin: 5 dc_pin: 15 diff --git a/tests/components/image/test.rp2040-ard.yaml b/tests/components/image/test.rp2040-ard.yaml index 5167c99a7d..40f17d57fe 100644 --- a/tests/components/image/test.rp2040-ard.yaml +++ b/tests/components/image/test.rp2040-ard.yaml @@ -1,12 +1,10 @@ -spi: - - id: spi_main_lcd - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml display: - platform: ili9xxx id: main_lcd + spi_id: spi_bus model: ili9342 cs_pin: 20 dc_pin: 21 diff --git a/tests/components/ina219/common.yaml b/tests/components/ina219/common.yaml index 4ca4c9ed8f..71291a082d 100644 --- a/tests/components/ina219/common.yaml +++ b/tests/components/ina219/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ina219 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ina219 + i2c_id: i2c_bus address: 0x40 shunt_resistance: 0.1 ohm current: diff --git a/tests/components/ina219/test.esp32-c3-idf.yaml b/tests/components/ina219/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ina219/test.esp32-c3-idf.yaml +++ b/tests/components/ina219/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina219/test.esp32-idf.yaml b/tests/components/ina219/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ina219/test.esp32-idf.yaml +++ b/tests/components/ina219/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina219/test.esp8266-ard.yaml b/tests/components/ina219/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ina219/test.esp8266-ard.yaml +++ b/tests/components/ina219/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ina219/test.rp2040-ard.yaml b/tests/components/ina219/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ina219/test.rp2040-ard.yaml +++ b/tests/components/ina219/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ina226/common.yaml b/tests/components/ina226/common.yaml index 8bcd038e50..7cbaf8cb2f 100644 --- a/tests/components/ina226/common.yaml +++ b/tests/components/ina226/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ina226 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ina226 + i2c_id: i2c_bus address: 0x40 shunt_resistance: 0.1 ohm current: diff --git a/tests/components/ina226/test.esp32-c3-idf.yaml b/tests/components/ina226/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ina226/test.esp32-c3-idf.yaml +++ b/tests/components/ina226/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina226/test.esp32-idf.yaml b/tests/components/ina226/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ina226/test.esp32-idf.yaml +++ b/tests/components/ina226/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina226/test.esp8266-ard.yaml b/tests/components/ina226/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ina226/test.esp8266-ard.yaml +++ b/tests/components/ina226/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ina226/test.rp2040-ard.yaml b/tests/components/ina226/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ina226/test.rp2040-ard.yaml +++ b/tests/components/ina226/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ina260/common.yaml b/tests/components/ina260/common.yaml index ab94b2e509..f630d0bb47 100644 --- a/tests/components/ina260/common.yaml +++ b/tests/components/ina260/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ina260 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ina260 + i2c_id: i2c_bus address: 0x40 current: name: INA260 Current diff --git a/tests/components/ina260/test.esp32-c3-idf.yaml b/tests/components/ina260/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ina260/test.esp32-c3-idf.yaml +++ b/tests/components/ina260/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina260/test.esp32-idf.yaml b/tests/components/ina260/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ina260/test.esp32-idf.yaml +++ b/tests/components/ina260/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina260/test.esp8266-ard.yaml b/tests/components/ina260/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ina260/test.esp8266-ard.yaml +++ b/tests/components/ina260/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ina260/test.rp2040-ard.yaml b/tests/components/ina260/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ina260/test.rp2040-ard.yaml +++ b/tests/components/ina260/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ina2xx_i2c/common.yaml b/tests/components/ina2xx_i2c/common.yaml index 320b680b6b..7d586f136e 100644 --- a/tests/components/ina2xx_i2c/common.yaml +++ b/tests/components/ina2xx_i2c/common.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_ina2xx - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ina2xx_i2c - i2c_id: i2c_ina2xx + i2c_id: i2c_bus address: 0x40 model: INA228 shunt_resistance: 0.001130 ohm diff --git a/tests/components/ina2xx_i2c/test.esp32-c3-idf.yaml b/tests/components/ina2xx_i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ina2xx_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/ina2xx_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina2xx_i2c/test.esp32-idf.yaml b/tests/components/ina2xx_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ina2xx_i2c/test.esp32-idf.yaml +++ b/tests/components/ina2xx_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina2xx_i2c/test.esp8266-ard.yaml b/tests/components/ina2xx_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ina2xx_i2c/test.esp8266-ard.yaml +++ b/tests/components/ina2xx_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ina2xx_i2c/test.rp2040-ard.yaml b/tests/components/ina2xx_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ina2xx_i2c/test.rp2040-ard.yaml +++ b/tests/components/ina2xx_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ina2xx_spi/common.yaml b/tests/components/ina2xx_spi/common.yaml index 3eab7e6f0a..d9b2300e26 100644 --- a/tests/components/ina2xx_spi/common.yaml +++ b/tests/components/ina2xx_spi/common.yaml @@ -1,12 +1,5 @@ -spi: - - id: spi_ina2xx - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: ina2xx_spi - spi_id: spi_ina2xx cs_pin: ${cs_pin} model: INA229 shunt_resistance: 0.001130 ohm diff --git a/tests/components/ina2xx_spi/test.esp32-c3-idf.yaml b/tests/components/ina2xx_spi/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/ina2xx_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ina2xx_spi/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina2xx_spi/test.esp32-idf.yaml b/tests/components/ina2xx_spi/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/ina2xx_spi/test.esp32-idf.yaml +++ b/tests/components/ina2xx_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ina2xx_spi/test.esp8266-ard.yaml b/tests/components/ina2xx_spi/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/ina2xx_spi/test.esp8266-ard.yaml +++ b/tests/components/ina2xx_spi/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ina2xx_spi/test.rp2040-ard.yaml b/tests/components/ina2xx_spi/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/ina2xx_spi/test.rp2040-ard.yaml +++ b/tests/components/ina2xx_spi/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ina3221/common.yaml b/tests/components/ina3221/common.yaml index ba1abdfe3a..570d1b0a12 100644 --- a/tests/components/ina3221/common.yaml +++ b/tests/components/ina3221/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ina3221 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ina3221 + i2c_id: i2c_bus address: 0x40 channel_1: shunt_resistance: 0.1 ohm diff --git a/tests/components/ina3221/test.esp32-c3-idf.yaml b/tests/components/ina3221/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ina3221/test.esp32-c3-idf.yaml +++ b/tests/components/ina3221/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina3221/test.esp32-idf.yaml b/tests/components/ina3221/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ina3221/test.esp32-idf.yaml +++ b/tests/components/ina3221/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ina3221/test.esp8266-ard.yaml b/tests/components/ina3221/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ina3221/test.esp8266-ard.yaml +++ b/tests/components/ina3221/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ina3221/test.rp2040-ard.yaml b/tests/components/ina3221/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ina3221/test.rp2040-ard.yaml +++ b/tests/components/ina3221/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/inkbird_ibsth1_mini/test.esp32-c3-idf.yaml b/tests/components/inkbird_ibsth1_mini/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/inkbird_ibsth1_mini/test.esp32-c3-idf.yaml +++ b/tests/components/inkbird_ibsth1_mini/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/inkbird_ibsth1_mini/test.esp32-idf.yaml b/tests/components/inkbird_ibsth1_mini/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/inkbird_ibsth1_mini/test.esp32-idf.yaml +++ b/tests/components/inkbird_ibsth1_mini/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/inkplate/common.yaml b/tests/components/inkplate/common.yaml index 7050b1739f..bb18ff4f7e 100644 --- a/tests/components/inkplate/common.yaml +++ b/tests/components/inkplate/common.yaml @@ -1,13 +1,9 @@ -i2c: - - id: i2c_inkplate - scl: 16 - sda: 17 - esp32: cpu_frequency: 240MHz display: - platform: inkplate + i2c_id: i2c_bus id: inkplate_display greyscale: false partial_updating: false diff --git a/tests/components/inkplate/test.esp32-idf.yaml b/tests/components/inkplate/test.esp32-idf.yaml index dade44d145..b47e39c389 100644 --- a/tests/components/inkplate/test.esp32-idf.yaml +++ b/tests/components/inkplate/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/jsn_sr04t/common.yaml b/tests/components/jsn_sr04t/common.yaml index d6871d5e91..bc2c301c2e 100644 --- a/tests/components/jsn_sr04t/common.yaml +++ b/tests/components/jsn_sr04t/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_jsn_sr04t - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: jsn_sr04t id: jsn_sr04t_sensor diff --git a/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml b/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml +++ b/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.esp32-idf.yaml b/tests/components/jsn_sr04t/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/jsn_sr04t/test.esp32-idf.yaml +++ b/tests/components/jsn_sr04t/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.esp8266-ard.yaml b/tests/components/jsn_sr04t/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/jsn_sr04t/test.esp8266-ard.yaml +++ b/tests/components/jsn_sr04t/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.rp2040-ard.yaml b/tests/components/jsn_sr04t/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/jsn_sr04t/test.rp2040-ard.yaml +++ b/tests/components/jsn_sr04t/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/kamstrup_kmp/common.yaml b/tests/components/kamstrup_kmp/common.yaml index b348d03c72..b3ebea523c 100644 --- a/tests/components/kamstrup_kmp/common.yaml +++ b/tests/components/kamstrup_kmp/common.yaml @@ -1,9 +1,3 @@ -uart: - tx_pin: ${uart_tx_pin} - rx_pin: ${uart_rx_pin} - baud_rate: 1200 - stop_bits: 2 - sensor: - platform: kamstrup_kmp heat_energy: diff --git a/tests/components/kamstrup_kmp/test.esp32-idf.yaml b/tests/components/kamstrup_kmp/test.esp32-idf.yaml index adc2c4d24a..1016905720 100644 --- a/tests/components/kamstrup_kmp/test.esp32-idf.yaml +++ b/tests/components/kamstrup_kmp/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - uart_tx_pin: GPIO1 - uart_rx_pin: GPIO3 +packages: + uart_1200: !include ../../test_build_components/common/uart_1200/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/kamstrup_kmp/test.esp8266-ard.yaml b/tests/components/kamstrup_kmp/test.esp8266-ard.yaml index adc2c4d24a..f55c18eb76 100644 --- a/tests/components/kamstrup_kmp/test.esp8266-ard.yaml +++ b/tests/components/kamstrup_kmp/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: uart_tx_pin: GPIO1 uart_rx_pin: GPIO3 +packages: + uart_1200: !include ../../test_build_components/common/uart_1200/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/kmeteriso/common.yaml b/tests/components/kmeteriso/common.yaml index 6b68175904..8542bb6a06 100644 --- a/tests/components/kmeteriso/common.yaml +++ b/tests/components/kmeteriso/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_kmeteriso - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: kmeteriso + i2c_id: i2c_bus temperature: name: Outside Temperature internal_temperature: diff --git a/tests/components/kmeteriso/test.esp32-c3-idf.yaml b/tests/components/kmeteriso/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/kmeteriso/test.esp32-c3-idf.yaml +++ b/tests/components/kmeteriso/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/kmeteriso/test.esp32-idf.yaml b/tests/components/kmeteriso/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/kmeteriso/test.esp32-idf.yaml +++ b/tests/components/kmeteriso/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/kmeteriso/test.esp8266-ard.yaml b/tests/components/kmeteriso/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/kmeteriso/test.esp8266-ard.yaml +++ b/tests/components/kmeteriso/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/kmeteriso/test.rp2040-ard.yaml b/tests/components/kmeteriso/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/kmeteriso/test.rp2040-ard.yaml +++ b/tests/components/kmeteriso/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/kuntze/common.yaml b/tests/components/kuntze/common.yaml index 4daecea242..32c6fa3809 100644 --- a/tests/components/kuntze/common.yaml +++ b/tests/components/kuntze/common.yaml @@ -1,14 +1,6 @@ -uart: - - id: uart_kuntze - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - -modbus: - flow_control_pin: ${flow_control_pin} - sensor: - platform: kuntze + modbus_id: modbus_bus ph: name: Kuntze pH temperature: diff --git a/tests/components/kuntze/test.esp32-c3-idf.yaml b/tests/components/kuntze/test.esp32-c3-idf.yaml index 452031a5aa..17940aafcf 100644 --- a/tests/components/kuntze/test.esp32-c3-idf.yaml +++ b/tests/components/kuntze/test.esp32-c3-idf.yaml @@ -1,6 +1,6 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 flow_control_pin: GPIO3 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/kuntze/test.esp32-idf.yaml b/tests/components/kuntze/test.esp32-idf.yaml index bd767a8ece..a755bfa66a 100644 --- a/tests/components/kuntze/test.esp32-idf.yaml +++ b/tests/components/kuntze/test.esp32-idf.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO14 flow_control_pin: GPIO13 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/kuntze/test.esp8266-ard.yaml b/tests/components/kuntze/test.esp8266-ard.yaml index 29c98d0957..6daa08c22b 100644 --- a/tests/components/kuntze/test.esp8266-ard.yaml +++ b/tests/components/kuntze/test.esp8266-ard.yaml @@ -1,6 +1,9 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - flow_control_pin: GPIO13 + tx_pin: GPIO0 + rx_pin: GPIO2 + flow_control_pin: GPIO15 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/kuntze/test.rp2040-ard.yaml b/tests/components/kuntze/test.rp2040-ard.yaml index 452031a5aa..a00283a9e0 100644 --- a/tests/components/kuntze/test.rp2040-ard.yaml +++ b/tests/components/kuntze/test.rp2040-ard.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO5 flow_control_pin: GPIO3 +packages: + modbus: !include ../../test_build_components/common/modbus/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/lc709203f/common.yaml b/tests/components/lc709203f/common.yaml index 53177c0d4a..3711e5372c 100644 --- a/tests/components/lc709203f/common.yaml +++ b/tests/components/lc709203f/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_lc709203f - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: lc709203f + i2c_id: i2c_bus size: 2000 voltage: 3.7 battery_voltage: diff --git a/tests/components/lc709203f/test.esp32-c3-idf.yaml b/tests/components/lc709203f/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/lc709203f/test.esp32-c3-idf.yaml +++ b/tests/components/lc709203f/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp32-idf.yaml b/tests/components/lc709203f/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/lc709203f/test.esp32-idf.yaml +++ b/tests/components/lc709203f/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp8266-ard.yaml b/tests/components/lc709203f/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/lc709203f/test.esp8266-ard.yaml +++ b/tests/components/lc709203f/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/lc709203f/test.rp2040-ard.yaml b/tests/components/lc709203f/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/lc709203f/test.rp2040-ard.yaml +++ b/tests/components/lc709203f/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/lcd_gpio/test.esp32-idf.yaml b/tests/components/lcd_gpio/test.esp32-idf.yaml index 9c2af456b5..93abad5e38 100644 --- a/tests/components/lcd_gpio/test.esp32-idf.yaml +++ b/tests/components/lcd_gpio/test.esp32-idf.yaml @@ -3,7 +3,7 @@ substitutions: d1_pin: GPIO13 d2_pin: GPIO14 d3_pin: GPIO15 - enable_pin: GPIO16 + enable_pin: GPIO4 rs_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/lcd_gpio/test.esp8266-ard.yaml b/tests/components/lcd_gpio/test.esp8266-ard.yaml index 9c2af456b5..50471a16f4 100644 --- a/tests/components/lcd_gpio/test.esp8266-ard.yaml +++ b/tests/components/lcd_gpio/test.esp8266-ard.yaml @@ -1,6 +1,6 @@ substitutions: - d0_pin: GPIO12 - d1_pin: GPIO13 + d0_pin: GPIO0 + d1_pin: GPIO2 d2_pin: GPIO14 d3_pin: GPIO15 enable_pin: GPIO16 diff --git a/tests/components/lcd_menu/test.esp32-idf.yaml b/tests/components/lcd_menu/test.esp32-idf.yaml index 9c2af456b5..93abad5e38 100644 --- a/tests/components/lcd_menu/test.esp32-idf.yaml +++ b/tests/components/lcd_menu/test.esp32-idf.yaml @@ -3,7 +3,7 @@ substitutions: d1_pin: GPIO13 d2_pin: GPIO14 d3_pin: GPIO15 - enable_pin: GPIO16 + enable_pin: GPIO4 rs_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/lcd_menu/test.esp8266-ard.yaml b/tests/components/lcd_menu/test.esp8266-ard.yaml index 9c2af456b5..50471a16f4 100644 --- a/tests/components/lcd_menu/test.esp8266-ard.yaml +++ b/tests/components/lcd_menu/test.esp8266-ard.yaml @@ -1,6 +1,6 @@ substitutions: - d0_pin: GPIO12 - d1_pin: GPIO13 + d0_pin: GPIO0 + d1_pin: GPIO2 d2_pin: GPIO14 d3_pin: GPIO15 enable_pin: GPIO16 diff --git a/tests/components/lcd_pcf8574/common.yaml b/tests/components/lcd_pcf8574/common.yaml index 8b03cf5497..1ec4400332 100644 --- a/tests/components/lcd_pcf8574/common.yaml +++ b/tests/components/lcd_pcf8574/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_lcd_pcf8574 - scl: ${scl_pin} - sda: ${sda_pin} - display: - platform: lcd_pcf8574 + i2c_id: i2c_bus dimensions: 18x4 address: 0x3F user_characters: diff --git a/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml b/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml +++ b/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.esp32-idf.yaml b/tests/components/lcd_pcf8574/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/lcd_pcf8574/test.esp32-idf.yaml +++ b/tests/components/lcd_pcf8574/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.esp8266-ard.yaml b/tests/components/lcd_pcf8574/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/lcd_pcf8574/test.esp8266-ard.yaml +++ b/tests/components/lcd_pcf8574/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.rp2040-ard.yaml b/tests/components/lcd_pcf8574/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/lcd_pcf8574/test.rp2040-ard.yaml +++ b/tests/components/lcd_pcf8574/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ld2410/common.yaml b/tests/components/ld2410/common.yaml index e975fcf757..7d168bf7ec 100644 --- a/tests/components/ld2410/common.yaml +++ b/tests/components/ld2410/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_ld2410 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - ld2410: id: my_ld2410 diff --git a/tests/components/ld2410/test.esp32-c3-idf.yaml b/tests/components/ld2410/test.esp32-c3-idf.yaml index b516342f3b..7a8f790ed8 100644 --- a/tests/components/ld2410/test.esp32-c3-idf.yaml +++ b/tests/components/ld2410/test.esp32-c3-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ld2410/test.esp32-idf.yaml b/tests/components/ld2410/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/ld2410/test.esp32-idf.yaml +++ b/tests/components/ld2410/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ld2410/test.esp8266-ard.yaml b/tests/components/ld2410/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/ld2410/test.esp8266-ard.yaml +++ b/tests/components/ld2410/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ld2410/test.rp2040-ard.yaml b/tests/components/ld2410/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/ld2410/test.rp2040-ard.yaml +++ b/tests/components/ld2410/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ld2412/common.yaml b/tests/components/ld2412/common.yaml index 9176c61fd5..18c4612ffe 100644 --- a/tests/components/ld2412/common.yaml +++ b/tests/components/ld2412/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_ld2412 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - ld2412: id: my_ld2412 @@ -123,13 +117,13 @@ select: on_value: - delay: 3s - lambda: |- - id(uart_ld2412).flush(); + id(uart_bus).flush(); uint32_t new_baud_rate = stoi(x); - ESP_LOGD("change_baud_rate", "Changing baud rate from %i to %i",id(uart_ld2412).get_baud_rate(), new_baud_rate); - if (id(uart_ld2412).get_baud_rate() != new_baud_rate) { - id(uart_ld2412).set_baud_rate(new_baud_rate); + ESP_LOGD("change_baud_rate", "Changing baud rate from %i to %i",id(uart_bus).get_baud_rate(), new_baud_rate); + if (id(uart_bus).get_baud_rate() != new_baud_rate) { + id(uart_bus).set_baud_rate(new_baud_rate); #if defined(USE_ESP8266) || defined(USE_ESP32) - id(uart_ld2412).load_settings(); + id(uart_bus).load_settings(); #endif } diff --git a/tests/components/ld2412/test.esp32-c3-idf.yaml b/tests/components/ld2412/test.esp32-c3-idf.yaml index b516342f3b..7a8f790ed8 100644 --- a/tests/components/ld2412/test.esp32-c3-idf.yaml +++ b/tests/components/ld2412/test.esp32-c3-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ld2412/test.esp32-idf.yaml b/tests/components/ld2412/test.esp32-idf.yaml index f486544afa..2d29656c94 100644 --- a/tests/components/ld2412/test.esp32-idf.yaml +++ b/tests/components/ld2412/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ld2412/test.esp8266-ard.yaml b/tests/components/ld2412/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/ld2412/test.esp8266-ard.yaml +++ b/tests/components/ld2412/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ld2412/test.rp2040-ard.yaml b/tests/components/ld2412/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/ld2412/test.rp2040-ard.yaml +++ b/tests/components/ld2412/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ld2420/common.yaml b/tests/components/ld2420/common.yaml index 76748aa8f0..1539df4d1b 100644 --- a/tests/components/ld2420/common.yaml +++ b/tests/components/ld2420/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_ld2420 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - ld2420: id: my_ld2420 diff --git a/tests/components/ld2420/test.esp32-c3-idf.yaml b/tests/components/ld2420/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/ld2420/test.esp32-c3-idf.yaml +++ b/tests/components/ld2420/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ld2420/test.esp32-idf.yaml b/tests/components/ld2420/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/ld2420/test.esp32-idf.yaml +++ b/tests/components/ld2420/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ld2420/test.esp8266-ard.yaml b/tests/components/ld2420/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/ld2420/test.esp8266-ard.yaml +++ b/tests/components/ld2420/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ld2420/test.rp2040-ard.yaml b/tests/components/ld2420/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/ld2420/test.rp2040-ard.yaml +++ b/tests/components/ld2420/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ld2450/common.yaml b/tests/components/ld2450/common.yaml index c18bed46b0..9dcefffd09 100644 --- a/tests/components/ld2450/common.yaml +++ b/tests/components/ld2450/common.yaml @@ -1,14 +1,5 @@ -uart: - - id: ld2450_uart - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 256000 - parity: NONE - stop_bits: 1 - ld2450: - id: ld2450_radar - uart_id: ld2450_uart button: - platform: ld2450 diff --git a/tests/components/ld2450/test.esp32-c3-idf.yaml b/tests/components/ld2450/test.esp32-c3-idf.yaml index b516342f3b..7a8f790ed8 100644 --- a/tests/components/ld2450/test.esp32-c3-idf.yaml +++ b/tests/components/ld2450/test.esp32-c3-idf.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ld2450/test.esp32-idf.yaml b/tests/components/ld2450/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/ld2450/test.esp32-idf.yaml +++ b/tests/components/ld2450/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ld2450/test.esp8266-ard.yaml b/tests/components/ld2450/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/ld2450/test.esp8266-ard.yaml +++ b/tests/components/ld2450/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ld2450/test.rp2040-ard.yaml b/tests/components/ld2450/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/ld2450/test.rp2040-ard.yaml +++ b/tests/components/ld2450/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/common.yaml b/tests/components/lilygo_t5_47/common.yaml index f539c58d74..7079139ec7 100644 --- a/tests/components/lilygo_t5_47/common.yaml +++ b/tests/components/lilygo_t5_47/common.yaml @@ -1,20 +1,17 @@ -i2c: - - id: i2c_lilygo_t5_47 - scl: ${scl_pin} - sda: ${sda_pin} - display: - platform: ssd1306_i2c + i2c_id: i2c_bus id: ssd1306_display model: SSD1306_128X64 reset_pin: ${reset_pin} pages: - - id: page1 + - id: lilygo_t5_47_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); touchscreen: - platform: lilygo_t5_47 + i2c_id: i2c_bus id: lilygo_touchscreen interrupt_pin: ${interrupt_pin} display: ssd1306_display diff --git a/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml b/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml index 061a98ce24..febf38d8e1 100644 --- a/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml +++ b/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO0 - sda_pin: GPIO1 interrupt_pin: GPIO2 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.esp32-idf.yaml b/tests/components/lilygo_t5_47/test.esp32-idf.yaml index 342f0b6d8b..590f6a919c 100644 --- a/tests/components/lilygo_t5_47/test.esp32-idf.yaml +++ b/tests/components/lilygo_t5_47/test.esp32-idf.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO13 - sda_pin: GPIO14 interrupt_pin: GPIO15 - reset_pin: GPIO16 + reset_pin: GPIO4 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.esp8266-ard.yaml b/tests/components/lilygo_t5_47/test.esp8266-ard.yaml index b446a75f13..3684d5e77b 100644 --- a/tests/components/lilygo_t5_47/test.esp8266-ard.yaml +++ b/tests/components/lilygo_t5_47/test.esp8266-ard.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - interrupt_pin: GPIO12 + interrupt_pin: GPIO15 reset_pin: GPIO16 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.rp2040-ard.yaml b/tests/components/lilygo_t5_47/test.rp2040-ard.yaml index 061a98ce24..2dd70ff33a 100644 --- a/tests/components/lilygo_t5_47/test.rp2040-ard.yaml +++ b/tests/components/lilygo_t5_47/test.rp2040-ard.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO0 - sda_pin: GPIO1 interrupt_pin: GPIO2 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/lm75b/common.yaml b/tests/components/lm75b/common.yaml index e451c2f679..39c39ed8dc 100644 --- a/tests/components/lm75b/common.yaml +++ b/tests/components/lm75b/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_lm75b - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: lm75b + i2c_id: i2c_bus name: LM75B Temperature update_interval: 30s diff --git a/tests/components/lm75b/test.esp32-c3-idf.yaml b/tests/components/lm75b/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/lm75b/test.esp32-c3-idf.yaml +++ b/tests/components/lm75b/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/lm75b/test.esp32-idf.yaml b/tests/components/lm75b/test.esp32-idf.yaml index 43264df633..b47e39c389 100644 --- a/tests/components/lm75b/test.esp32-idf.yaml +++ b/tests/components/lm75b/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO15 - sda_pin: GPIO13 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/lm75b/test.esp8266-ard.yaml b/tests/components/lm75b/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/lm75b/test.esp8266-ard.yaml +++ b/tests/components/lm75b/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/lm75b/test.rp2040-ard.yaml b/tests/components/lm75b/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/lm75b/test.rp2040-ard.yaml +++ b/tests/components/lm75b/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/lock/common.yaml b/tests/components/lock/common.yaml index 67da46653c..1ee88a239a 100644 --- a/tests/components/lock/common.yaml +++ b/tests/components/lock/common.yaml @@ -27,7 +27,9 @@ lock: id: test_lock1 state: !lambda "return LOCK_STATE_UNLOCKED;" on_lock: - - lock.template.publish: LOCKED + - lock.template.publish: + id: test_lock1 + state: LOCKED - platform: output name: Generic Output Lock id: test_lock2 diff --git a/tests/components/lps22/common.yaml b/tests/components/lps22/common.yaml index e6de4752ba..026b3620cd 100644 --- a/tests/components/lps22/common.yaml +++ b/tests/components/lps22/common.yaml @@ -1,5 +1,6 @@ sensor: - platform: lps22 + i2c_id: i2c_bus address: 0x5d update_interval: 10s temperature: diff --git a/tests/components/lps22/test.esp32-c3-idf.yaml b/tests/components/lps22/test.esp32-c3-idf.yaml index 6091393d31..9990d96d29 100644 --- a/tests/components/lps22/test.esp32-c3-idf.yaml +++ b/tests/components/lps22/test.esp32-c3-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_lps22 - scl: 5 - sda: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/lps22/test.esp32-idf.yaml b/tests/components/lps22/test.esp32-idf.yaml index 0da6a9577e..b47e39c389 100644 --- a/tests/components/lps22/test.esp32-idf.yaml +++ b/tests/components/lps22/test.esp32-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_lps22 - scl: 16 - sda: 17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/lps22/test.esp8266-ard.yaml b/tests/components/lps22/test.esp8266-ard.yaml index 6091393d31..4a98b9388a 100644 --- a/tests/components/lps22/test.esp8266-ard.yaml +++ b/tests/components/lps22/test.esp8266-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_lps22 - scl: 5 - sda: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/lps22/test.rp2040-ard.yaml b/tests/components/lps22/test.rp2040-ard.yaml index 6091393d31..319a7c71a6 100644 --- a/tests/components/lps22/test.rp2040-ard.yaml +++ b/tests/components/lps22/test.rp2040-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_lps22 - scl: 5 - sda: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ltr390/common.yaml b/tests/components/ltr390/common.yaml index e5e331e7ba..c168da557b 100644 --- a/tests/components/ltr390/common.yaml +++ b/tests/components/ltr390/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ltr390 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: ltr390 + i2c_id: i2c_bus uv: name: LTR390 UV 1 uv_index: @@ -19,6 +15,7 @@ sensor: address: 0x53 update_interval: 60s - platform: ltr390 + i2c_id: i2c_bus uv: name: LTR390 UV 2 uv_index: diff --git a/tests/components/ltr390/test.esp32-c3-idf.yaml b/tests/components/ltr390/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ltr390/test.esp32-c3-idf.yaml +++ b/tests/components/ltr390/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ltr390/test.esp32-idf.yaml b/tests/components/ltr390/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ltr390/test.esp32-idf.yaml +++ b/tests/components/ltr390/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ltr390/test.esp8266-ard.yaml b/tests/components/ltr390/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ltr390/test.esp8266-ard.yaml +++ b/tests/components/ltr390/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ltr390/test.rp2040-ard.yaml b/tests/components/ltr390/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ltr390/test.rp2040-ard.yaml +++ b/tests/components/ltr390/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ltr501/common.yaml b/tests/components/ltr501/common.yaml index b7074f52f2..77c6f13739 100644 --- a/tests/components/ltr501/common.yaml +++ b/tests/components/ltr501/common.yaml @@ -1,7 +1,6 @@ sensor: - platform: ltr501 address: 0x23 - i2c_id: i2c_ltr501 type: ALS_PS gain: 1X integration_time: 100ms diff --git a/tests/components/ltr501/test.esp32-c3-idf.yaml b/tests/components/ltr501/test.esp32-c3-idf.yaml index 9e7de2768d..72703301a1 100644 --- a/tests/components/ltr501/test.esp32-c3-idf.yaml +++ b/tests/components/ltr501/test.esp32-c3-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_ltr501 - scl: 5 - sda: 4 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ltr501/test.esp32-idf.yaml b/tests/components/ltr501/test.esp32-idf.yaml index 4c710c74fe..7a5d01898a 100644 --- a/tests/components/ltr501/test.esp32-idf.yaml +++ b/tests/components/ltr501/test.esp32-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_ltr501 - scl: 16 - sda: 17 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ltr501/test.esp8266-ard.yaml b/tests/components/ltr501/test.esp8266-ard.yaml index 9e7de2768d..9e23bb3778 100644 --- a/tests/components/ltr501/test.esp8266-ard.yaml +++ b/tests/components/ltr501/test.esp8266-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_ltr501 - scl: 5 - sda: 4 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ltr501/test.rp2040-ard.yaml b/tests/components/ltr501/test.rp2040-ard.yaml index 9e7de2768d..a7eb30036f 100644 --- a/tests/components/ltr501/test.rp2040-ard.yaml +++ b/tests/components/ltr501/test.rp2040-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_ltr501 - scl: 5 - sda: 4 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ltr_als_ps/common.yaml b/tests/components/ltr_als_ps/common.yaml index aa5c8abed7..edad4b2e4f 100644 --- a/tests/components/ltr_als_ps/common.yaml +++ b/tests/components/ltr_als_ps/common.yaml @@ -1,7 +1,6 @@ sensor: - platform: ltr_als_ps address: 0x23 - i2c_id: i2c_als_ps gain: 1x integration_time: 100ms ps_cooldown: 5 s diff --git a/tests/components/ltr_als_ps/test.esp32-c3-idf.yaml b/tests/components/ltr_als_ps/test.esp32-c3-idf.yaml index d64d70f018..72703301a1 100644 --- a/tests/components/ltr_als_ps/test.esp32-c3-idf.yaml +++ b/tests/components/ltr_als_ps/test.esp32-c3-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_als_ps - scl: 5 - sda: 4 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ltr_als_ps/test.esp32-idf.yaml b/tests/components/ltr_als_ps/test.esp32-idf.yaml index 2349292a64..7a5d01898a 100644 --- a/tests/components/ltr_als_ps/test.esp32-idf.yaml +++ b/tests/components/ltr_als_ps/test.esp32-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_als_ps - scl: 16 - sda: 17 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ltr_als_ps/test.esp8266-ard.yaml b/tests/components/ltr_als_ps/test.esp8266-ard.yaml index d64d70f018..9e23bb3778 100644 --- a/tests/components/ltr_als_ps/test.esp8266-ard.yaml +++ b/tests/components/ltr_als_ps/test.esp8266-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_als_ps - scl: 5 - sda: 4 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ltr_als_ps/test.rp2040-ard.yaml b/tests/components/ltr_als_ps/test.rp2040-ard.yaml index d64d70f018..a7eb30036f 100644 --- a/tests/components/ltr_als_ps/test.rp2040-ard.yaml +++ b/tests/components/ltr_als_ps/test.rp2040-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_als_ps - scl: 5 - sda: 4 +packages: + i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index a035900386..d9b7013a1e 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -1,5 +1,6 @@ touchscreen: - platform: ft63x6 + i2c_id: i2c_bus id: tft_touch display: tft_display update_interval: 50ms diff --git a/tests/components/lvgl/test.esp32-idf.yaml b/tests/components/lvgl/test.esp32-idf.yaml index eacace1d4b..6170b0f4fb 100644 --- a/tests/components/lvgl/test.esp32-idf.yaml +++ b/tests/components/lvgl/test.esp32-idf.yaml @@ -1,10 +1,7 @@ -spi: - clk_pin: 14 - mosi_pin: 13 - -i2c: - sda: GPIO18 - scl: GPIO19 +packages: + lvgl: !include lvgl-package.yaml + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml sensor: - platform: rotary_encoder @@ -25,6 +22,7 @@ binary_sensor: display: - platform: ili9xxx + spi_id: spi_bus model: st7789v id: second_display dimensions: @@ -44,6 +42,7 @@ display: update_interval: never - platform: ili9xxx + spi_id: spi_bus model: st7789v id: tft_display dimensions: @@ -60,9 +59,6 @@ display: invert_colors: false update_interval: never -packages: - lvgl: !include lvgl-package.yaml - lvgl: displays: - tft_display diff --git a/tests/components/m5stack_8angle/common.yaml b/tests/components/m5stack_8angle/common.yaml index d7f988ed3a..d0af24116c 100644 --- a/tests/components/m5stack_8angle/common.yaml +++ b/tests/components/m5stack_8angle/common.yaml @@ -1,10 +1,5 @@ -i2c: - sda: 0 - scl: 1 - id: bus_external - m5stack_8angle: - i2c_id: bus_external + i2c_id: i2c_bus id: m5stack_8angle_base light: diff --git a/tests/components/m5stack_8angle/test.esp32-c3-idf.yaml b/tests/components/m5stack_8angle/test.esp32-c3-idf.yaml index dade44d145..9990d96d29 100644 --- a/tests/components/m5stack_8angle/test.esp32-c3-idf.yaml +++ b/tests/components/m5stack_8angle/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.esp32-idf.yaml b/tests/components/m5stack_8angle/test.esp32-idf.yaml index dade44d145..b47e39c389 100644 --- a/tests/components/m5stack_8angle/test.esp32-idf.yaml +++ b/tests/components/m5stack_8angle/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.esp8266-ard.yaml b/tests/components/m5stack_8angle/test.esp8266-ard.yaml index dade44d145..4a98b9388a 100644 --- a/tests/components/m5stack_8angle/test.esp8266-ard.yaml +++ b/tests/components/m5stack_8angle/test.esp8266-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.rp2040-ard.yaml b/tests/components/m5stack_8angle/test.rp2040-ard.yaml index dade44d145..319a7c71a6 100644 --- a/tests/components/m5stack_8angle/test.rp2040-ard.yaml +++ b/tests/components/m5stack_8angle/test.rp2040-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mapping/test.esp32-c3-idf.yaml b/tests/components/mapping/test.esp32-c3-idf.yaml index f95dd4f30d..7911eb7edc 100644 --- a/tests/components/mapping/test.esp32-c3-idf.yaml +++ b/tests/components/mapping/test.esp32-c3-idf.yaml @@ -1,10 +1,9 @@ -spi: - - id: spi_main_lcd - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml + map: !include common.yaml display: + spi_id: spi_bus platform: ili9xxx id: main_lcd model: ili9342 @@ -12,6 +11,3 @@ display: dc_pin: 9 reset_pin: 10 invert_colors: false - -packages: - map: !include common.yaml diff --git a/tests/components/mapping/test.esp32-idf.yaml b/tests/components/mapping/test.esp32-idf.yaml index 231fdae797..a35b6940c7 100644 --- a/tests/components/mapping/test.esp32-idf.yaml +++ b/tests/components/mapping/test.esp32-idf.yaml @@ -1,10 +1,9 @@ -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + map: !include common.yaml display: + spi_id: spi_bus platform: ili9xxx id: main_lcd model: ili9342 @@ -12,6 +11,3 @@ display: dc_pin: 13 reset_pin: 21 invert_colors: false - -packages: - map: !include common.yaml diff --git a/tests/components/mapping/test.esp8266-ard.yaml b/tests/components/mapping/test.esp8266-ard.yaml index a5b45d391a..c59821a211 100644 --- a/tests/components/mapping/test.esp8266-ard.yaml +++ b/tests/components/mapping/test.esp8266-ard.yaml @@ -1,10 +1,9 @@ -spi: - - id: spi_main_lcd - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + map: !include common.yaml display: + spi_id: spi_bus platform: ili9xxx id: main_lcd model: ili9342 @@ -12,6 +11,3 @@ display: dc_pin: 15 reset_pin: 16 invert_colors: false - -packages: - map: !include common.yaml diff --git a/tests/components/mapping/test.rp2040-ard.yaml b/tests/components/mapping/test.rp2040-ard.yaml index f092686553..acc305a701 100644 --- a/tests/components/mapping/test.rp2040-ard.yaml +++ b/tests/components/mapping/test.rp2040-ard.yaml @@ -1,10 +1,9 @@ -spi: - - id: spi_main_lcd - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + map: !include common.yaml display: + spi_id: spi_bus platform: ili9xxx id: main_lcd model: ili9342 @@ -12,6 +11,3 @@ display: dc_pin: 21 reset_pin: 22 invert_colors: false - -packages: - map: !include common.yaml diff --git a/tests/components/max17043/common.yaml b/tests/components/max17043/common.yaml index c2f324212e..f58006c460 100644 --- a/tests/components/max17043/common.yaml +++ b/tests/components/max17043/common.yaml @@ -3,15 +3,10 @@ esphome: then: - max17043.sleep_mode: max17043_id -i2c: - - id: i2c_id - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: max17043 + i2c_id: i2c_bus id: max17043_id - i2c_id: i2c_id battery_voltage: name: "Battery Voltage" battery_level: diff --git a/tests/components/max17043/test.esp32-c3-idf.yaml b/tests/components/max17043/test.esp32-c3-idf.yaml index 9a1477d4b9..9990d96d29 100644 --- a/tests/components/max17043/test.esp32-c3-idf.yaml +++ b/tests/components/max17043/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - sda_pin: GPIO8 - scl_pin: GPIO10 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/max17043/test.esp32-idf.yaml b/tests/components/max17043/test.esp32-idf.yaml index c6615f51cd..b47e39c389 100644 --- a/tests/components/max17043/test.esp32-idf.yaml +++ b/tests/components/max17043/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - sda_pin: GPIO21 - scl_pin: GPIO22 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/max17043/test.esp8266-ard.yaml b/tests/components/max17043/test.esp8266-ard.yaml index a87353b78b..4a98b9388a 100644 --- a/tests/components/max17043/test.esp8266-ard.yaml +++ b/tests/components/max17043/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - sda_pin: GPIO4 - scl_pin: GPIO5 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/max17043/test.rp2040-ard.yaml b/tests/components/max17043/test.rp2040-ard.yaml index c6615f51cd..319a7c71a6 100644 --- a/tests/components/max17043/test.rp2040-ard.yaml +++ b/tests/components/max17043/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - sda_pin: GPIO21 - scl_pin: GPIO22 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/max31855/common.yaml b/tests/components/max31855/common.yaml index 7136c597d5..905b111d71 100644 --- a/tests/components/max31855/common.yaml +++ b/tests/components/max31855/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_max31855 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: max31855 name: MAX31855 Temperature diff --git a/tests/components/max31855/test.esp32-c3-idf.yaml b/tests/components/max31855/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/max31855/test.esp32-c3-idf.yaml +++ b/tests/components/max31855/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/max31855/test.esp32-idf.yaml b/tests/components/max31855/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/max31855/test.esp32-idf.yaml +++ b/tests/components/max31855/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/max31855/test.esp8266-ard.yaml b/tests/components/max31855/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/max31855/test.esp8266-ard.yaml +++ b/tests/components/max31855/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max31855/test.rp2040-ard.yaml b/tests/components/max31855/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/max31855/test.rp2040-ard.yaml +++ b/tests/components/max31855/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max31856/common.yaml b/tests/components/max31856/common.yaml index 4f7c3ad408..9d420662d7 100644 --- a/tests/components/max31856/common.yaml +++ b/tests/components/max31856/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_max31856 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: max31856 name: MAX31856 Temperature diff --git a/tests/components/max31856/test.esp32-c3-idf.yaml b/tests/components/max31856/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/max31856/test.esp32-c3-idf.yaml +++ b/tests/components/max31856/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/max31856/test.esp32-idf.yaml b/tests/components/max31856/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/max31856/test.esp32-idf.yaml +++ b/tests/components/max31856/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/max31856/test.esp8266-ard.yaml b/tests/components/max31856/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/max31856/test.esp8266-ard.yaml +++ b/tests/components/max31856/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max31856/test.rp2040-ard.yaml b/tests/components/max31856/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/max31856/test.rp2040-ard.yaml +++ b/tests/components/max31856/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max31865/common.yaml b/tests/components/max31865/common.yaml index 5bb7bda5aa..6e71f17efc 100644 --- a/tests/components/max31865/common.yaml +++ b/tests/components/max31865/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_max31865 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: max31865 name: MAX31865 Temperature diff --git a/tests/components/max31865/test.esp32-c3-idf.yaml b/tests/components/max31865/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/max31865/test.esp32-c3-idf.yaml +++ b/tests/components/max31865/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/max31865/test.esp32-idf.yaml b/tests/components/max31865/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/max31865/test.esp32-idf.yaml +++ b/tests/components/max31865/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/max31865/test.esp8266-ard.yaml b/tests/components/max31865/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/max31865/test.esp8266-ard.yaml +++ b/tests/components/max31865/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max31865/test.rp2040-ard.yaml b/tests/components/max31865/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/max31865/test.rp2040-ard.yaml +++ b/tests/components/max31865/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max44009/common.yaml b/tests/components/max44009/common.yaml index ef51740895..523387e1cc 100644 --- a/tests/components/max44009/common.yaml +++ b/tests/components/max44009/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_max44009 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: max44009 + i2c_id: i2c_bus name: MAX44009 Brightness internal: true mode: low_power diff --git a/tests/components/max44009/test.esp32-c3-idf.yaml b/tests/components/max44009/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/max44009/test.esp32-c3-idf.yaml +++ b/tests/components/max44009/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/max44009/test.esp32-idf.yaml b/tests/components/max44009/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/max44009/test.esp32-idf.yaml +++ b/tests/components/max44009/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/max44009/test.esp8266-ard.yaml b/tests/components/max44009/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/max44009/test.esp8266-ard.yaml +++ b/tests/components/max44009/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/max44009/test.rp2040-ard.yaml b/tests/components/max44009/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/max44009/test.rp2040-ard.yaml +++ b/tests/components/max44009/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/max6675/common.yaml b/tests/components/max6675/common.yaml index 5b4e04b317..31a4e7035a 100644 --- a/tests/components/max6675/common.yaml +++ b/tests/components/max6675/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_max6675 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sensor: - platform: max6675 name: Temperature diff --git a/tests/components/max6675/test.esp32-c3-idf.yaml b/tests/components/max6675/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/max6675/test.esp32-c3-idf.yaml +++ b/tests/components/max6675/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/max6675/test.esp32-idf.yaml b/tests/components/max6675/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/max6675/test.esp32-idf.yaml +++ b/tests/components/max6675/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/max6675/test.esp8266-ard.yaml b/tests/components/max6675/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/max6675/test.esp8266-ard.yaml +++ b/tests/components/max6675/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max6675/test.rp2040-ard.yaml b/tests/components/max6675/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/max6675/test.rp2040-ard.yaml +++ b/tests/components/max6675/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max6956/common.yaml b/tests/components/max6956/common.yaml index e44e3464f8..665a606027 100644 --- a/tests/components/max6956/common.yaml +++ b/tests/components/max6956/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_max6956 - scl: ${scl_pin} - sda: ${sda_pin} - max6956: - id: max6956_1 + i2c_id: i2c_bus address: 0x40 binary_sensor: diff --git a/tests/components/max6956/test.esp32-c3-idf.yaml b/tests/components/max6956/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/max6956/test.esp32-c3-idf.yaml +++ b/tests/components/max6956/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/max6956/test.esp32-idf.yaml b/tests/components/max6956/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/max6956/test.esp32-idf.yaml +++ b/tests/components/max6956/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/max6956/test.esp8266-ard.yaml b/tests/components/max6956/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/max6956/test.esp8266-ard.yaml +++ b/tests/components/max6956/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/max6956/test.rp2040-ard.yaml b/tests/components/max6956/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/max6956/test.rp2040-ard.yaml +++ b/tests/components/max6956/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/max7219/common.yaml b/tests/components/max7219/common.yaml index 0060db191e..5d7f54af17 100644 --- a/tests/components/max7219/common.yaml +++ b/tests/components/max7219/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_max6675 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - display: - platform: max7219 cs_pin: ${cs_pin} diff --git a/tests/components/max7219/test.esp32-c3-idf.yaml b/tests/components/max7219/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/max7219/test.esp32-c3-idf.yaml +++ b/tests/components/max7219/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/max7219/test.esp32-idf.yaml b/tests/components/max7219/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/max7219/test.esp32-idf.yaml +++ b/tests/components/max7219/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/max7219/test.esp8266-ard.yaml b/tests/components/max7219/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/max7219/test.esp8266-ard.yaml +++ b/tests/components/max7219/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max7219/test.rp2040-ard.yaml b/tests/components/max7219/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/max7219/test.rp2040-ard.yaml +++ b/tests/components/max7219/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max7219digit/common.yaml b/tests/components/max7219digit/common.yaml index 84edc7eb3d..525b7b8d3e 100644 --- a/tests/components/max7219digit/common.yaml +++ b/tests/components/max7219digit/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_max7219digit - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - display: - platform: max7219digit cs_pin: ${cs_pin} diff --git a/tests/components/max7219digit/test.esp32-c3-idf.yaml b/tests/components/max7219digit/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/max7219digit/test.esp32-c3-idf.yaml +++ b/tests/components/max7219digit/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/max7219digit/test.esp32-idf.yaml b/tests/components/max7219digit/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/max7219digit/test.esp32-idf.yaml +++ b/tests/components/max7219digit/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/max7219digit/test.esp8266-ard.yaml b/tests/components/max7219digit/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/max7219digit/test.esp8266-ard.yaml +++ b/tests/components/max7219digit/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max7219digit/test.rp2040-ard.yaml b/tests/components/max7219digit/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/max7219digit/test.rp2040-ard.yaml +++ b/tests/components/max7219digit/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/max9611/common.yaml b/tests/components/max9611/common.yaml index c3c00fdf85..ca9ee59038 100644 --- a/tests/components/max9611/common.yaml +++ b/tests/components/max9611/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_max9611 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: max9611 + i2c_id: i2c_bus shunt_resistance: 0.2 ohm gain: 1X voltage: diff --git a/tests/components/max9611/test.esp32-c3-idf.yaml b/tests/components/max9611/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/max9611/test.esp32-c3-idf.yaml +++ b/tests/components/max9611/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/max9611/test.esp32-idf.yaml b/tests/components/max9611/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/max9611/test.esp32-idf.yaml +++ b/tests/components/max9611/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/max9611/test.esp8266-ard.yaml b/tests/components/max9611/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/max9611/test.esp8266-ard.yaml +++ b/tests/components/max9611/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/max9611/test.rp2040-ard.yaml b/tests/components/max9611/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/max9611/test.rp2040-ard.yaml +++ b/tests/components/max9611/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp23008/common.yaml b/tests/components/mcp23008/common.yaml index 1954766d25..4a407adfd8 100644 --- a/tests/components/mcp23008/common.yaml +++ b/tests/components/mcp23008/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_mcp23008 - scl: ${scl_pin} - sda: ${sda_pin} - mcp23008: + i2c_id: i2c_bus id: mcp23008_hub binary_sensor: diff --git a/tests/components/mcp23008/test.esp32-c3-idf.yaml b/tests/components/mcp23008/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mcp23008/test.esp32-c3-idf.yaml +++ b/tests/components/mcp23008/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp23008/test.esp32-idf.yaml b/tests/components/mcp23008/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mcp23008/test.esp32-idf.yaml +++ b/tests/components/mcp23008/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp23008/test.esp8266-ard.yaml b/tests/components/mcp23008/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mcp23008/test.esp8266-ard.yaml +++ b/tests/components/mcp23008/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp23008/test.rp2040-ard.yaml b/tests/components/mcp23008/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mcp23008/test.rp2040-ard.yaml +++ b/tests/components/mcp23008/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp23016/common.yaml b/tests/components/mcp23016/common.yaml index 109cb34b21..e8e3ad9d08 100644 --- a/tests/components/mcp23016/common.yaml +++ b/tests/components/mcp23016/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_mcp23016 - scl: ${scl_pin} - sda: ${sda_pin} - mcp23016: + i2c_id: i2c_bus id: mcp23016_hub binary_sensor: diff --git a/tests/components/mcp23016/test.esp32-c3-idf.yaml b/tests/components/mcp23016/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mcp23016/test.esp32-c3-idf.yaml +++ b/tests/components/mcp23016/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp23016/test.esp32-idf.yaml b/tests/components/mcp23016/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mcp23016/test.esp32-idf.yaml +++ b/tests/components/mcp23016/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp23016/test.esp8266-ard.yaml b/tests/components/mcp23016/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mcp23016/test.esp8266-ard.yaml +++ b/tests/components/mcp23016/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp23016/test.rp2040-ard.yaml b/tests/components/mcp23016/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mcp23016/test.rp2040-ard.yaml +++ b/tests/components/mcp23016/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp23017/common.yaml b/tests/components/mcp23017/common.yaml index 74949bba76..54a97e911f 100644 --- a/tests/components/mcp23017/common.yaml +++ b/tests/components/mcp23017/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_mcp23017 - scl: ${scl_pin} - sda: ${sda_pin} - mcp23017: + i2c_id: i2c_bus id: mcp23017_hub binary_sensor: diff --git a/tests/components/mcp23017/test.esp32-c3-idf.yaml b/tests/components/mcp23017/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mcp23017/test.esp32-c3-idf.yaml +++ b/tests/components/mcp23017/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp23017/test.esp32-idf.yaml b/tests/components/mcp23017/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mcp23017/test.esp32-idf.yaml +++ b/tests/components/mcp23017/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp23017/test.esp8266-ard.yaml b/tests/components/mcp23017/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mcp23017/test.esp8266-ard.yaml +++ b/tests/components/mcp23017/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp23017/test.rp2040-ard.yaml b/tests/components/mcp23017/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mcp23017/test.rp2040-ard.yaml +++ b/tests/components/mcp23017/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp23s08/common.yaml b/tests/components/mcp23s08/common.yaml index b89088fe15..2170ae0459 100644 --- a/tests/components/mcp23s08/common.yaml +++ b/tests/components/mcp23s08/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_mcp23s08 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - mcp23s08: - id: mcp23s08_hub cs_pin: ${cs_pin} diff --git a/tests/components/mcp23s08/test.esp32-c3-idf.yaml b/tests/components/mcp23s08/test.esp32-c3-idf.yaml index 2415ba5dc6..b11ec9cdc6 100644 --- a/tests/components/mcp23s08/test.esp32-c3-idf.yaml +++ b/tests/components/mcp23s08/test.esp32-c3-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mcp23s08/test.esp32-idf.yaml b/tests/components/mcp23s08/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/mcp23s08/test.esp32-idf.yaml +++ b/tests/components/mcp23s08/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mcp23s08/test.esp8266-ard.yaml b/tests/components/mcp23s08/test.esp8266-ard.yaml index dbd158d030..595f31046a 100644 --- a/tests/components/mcp23s08/test.esp8266-ard.yaml +++ b/tests/components/mcp23s08/test.esp8266-ard.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mcp23s08/test.rp2040-ard.yaml b/tests/components/mcp23s08/test.rp2040-ard.yaml index f6c3f1eeca..79ea6ce90b 100644 --- a/tests/components/mcp23s08/test.rp2040-ard.yaml +++ b/tests/components/mcp23s08/test.rp2040-ard.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO2 - mosi_pin: GPIO3 - miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mcp23s17/common.yaml b/tests/components/mcp23s17/common.yaml index 3fb27ef625..a89beeb16b 100644 --- a/tests/components/mcp23s17/common.yaml +++ b/tests/components/mcp23s17/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_mcp23s17 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - mcp23s17: - id: mcp23s17_hub cs_pin: ${cs_pin} diff --git a/tests/components/mcp23s17/test.esp32-c3-idf.yaml b/tests/components/mcp23s17/test.esp32-c3-idf.yaml index 2415ba5dc6..b11ec9cdc6 100644 --- a/tests/components/mcp23s17/test.esp32-c3-idf.yaml +++ b/tests/components/mcp23s17/test.esp32-c3-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mcp23s17/test.esp32-idf.yaml b/tests/components/mcp23s17/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/mcp23s17/test.esp32-idf.yaml +++ b/tests/components/mcp23s17/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mcp23s17/test.esp8266-ard.yaml b/tests/components/mcp23s17/test.esp8266-ard.yaml index dbd158d030..595f31046a 100644 --- a/tests/components/mcp23s17/test.esp8266-ard.yaml +++ b/tests/components/mcp23s17/test.esp8266-ard.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mcp23s17/test.rp2040-ard.yaml b/tests/components/mcp23s17/test.rp2040-ard.yaml index f6c3f1eeca..79ea6ce90b 100644 --- a/tests/components/mcp23s17/test.rp2040-ard.yaml +++ b/tests/components/mcp23s17/test.rp2040-ard.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO2 - mosi_pin: GPIO3 - miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mcp2515/common.yaml b/tests/components/mcp2515/common.yaml index 96a72a3ec3..15639df5fe 100644 --- a/tests/components/mcp2515/common.yaml +++ b/tests/components/mcp2515/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_mcp2515 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - canbus: - platform: mcp2515 id: mcp2515_can diff --git a/tests/components/mcp2515/test.esp32-c3-idf.yaml b/tests/components/mcp2515/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/mcp2515/test.esp32-c3-idf.yaml +++ b/tests/components/mcp2515/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp2515/test.esp32-idf.yaml b/tests/components/mcp2515/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/mcp2515/test.esp32-idf.yaml +++ b/tests/components/mcp2515/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mcp2515/test.esp8266-ard.yaml b/tests/components/mcp2515/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/mcp2515/test.esp8266-ard.yaml +++ b/tests/components/mcp2515/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mcp2515/test.rp2040-ard.yaml b/tests/components/mcp2515/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/mcp2515/test.rp2040-ard.yaml +++ b/tests/components/mcp2515/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mcp3008/common.yaml b/tests/components/mcp3008/common.yaml index 646d3a20e9..57d0155f73 100644 --- a/tests/components/mcp3008/common.yaml +++ b/tests/components/mcp3008/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_mcp3008 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - mcp3008: - id: mcp3008_hub cs_pin: ${cs_pin} diff --git a/tests/components/mcp3008/test.esp32-c3-idf.yaml b/tests/components/mcp3008/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/mcp3008/test.esp32-c3-idf.yaml +++ b/tests/components/mcp3008/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp3008/test.esp32-idf.yaml b/tests/components/mcp3008/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/mcp3008/test.esp32-idf.yaml +++ b/tests/components/mcp3008/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mcp3008/test.esp8266-ard.yaml b/tests/components/mcp3008/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/mcp3008/test.esp8266-ard.yaml +++ b/tests/components/mcp3008/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mcp3008/test.rp2040-ard.yaml b/tests/components/mcp3008/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/mcp3008/test.rp2040-ard.yaml +++ b/tests/components/mcp3008/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mcp3204/common.yaml b/tests/components/mcp3204/common.yaml index f102500c81..eca6ec44f4 100644 --- a/tests/components/mcp3204/common.yaml +++ b/tests/components/mcp3204/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_mcp3204 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - mcp3204: - id: mcp3204_hub cs_pin: ${cs_pin} diff --git a/tests/components/mcp3204/test.esp32-c3-idf.yaml b/tests/components/mcp3204/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/mcp3204/test.esp32-c3-idf.yaml +++ b/tests/components/mcp3204/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp3204/test.esp32-idf.yaml b/tests/components/mcp3204/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/mcp3204/test.esp32-idf.yaml +++ b/tests/components/mcp3204/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mcp3204/test.esp8266-ard.yaml b/tests/components/mcp3204/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/mcp3204/test.esp8266-ard.yaml +++ b/tests/components/mcp3204/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mcp3204/test.rp2040-ard.yaml b/tests/components/mcp3204/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/mcp3204/test.rp2040-ard.yaml +++ b/tests/components/mcp3204/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mcp4461/common.yaml b/tests/components/mcp4461/common.yaml index ce1866fdb8..92fd789dcb 100644 --- a/tests/components/mcp4461/common.yaml +++ b/tests/components/mcp4461/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mcp4461 - sda: ${sda_pin} - scl: ${scl_pin} - mcp4461: - id: mcp4461_digipot_01 + i2c_id: i2c_bus output: - platform: mcp4461 diff --git a/tests/components/mcp4461/test.esp32-c3-idf.yaml b/tests/components/mcp4461/test.esp32-c3-idf.yaml index a87353b78b..9990d96d29 100644 --- a/tests/components/mcp4461/test.esp32-c3-idf.yaml +++ b/tests/components/mcp4461/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - sda_pin: GPIO4 - scl_pin: GPIO5 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp4461/test.esp32-idf.yaml b/tests/components/mcp4461/test.esp32-idf.yaml index c5deb7ca0a..b47e39c389 100644 --- a/tests/components/mcp4461/test.esp32-idf.yaml +++ b/tests/components/mcp4461/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - sda_pin: GPIO16 - scl_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp4461/test.esp8266-ard.yaml b/tests/components/mcp4461/test.esp8266-ard.yaml index a87353b78b..4a98b9388a 100644 --- a/tests/components/mcp4461/test.esp8266-ard.yaml +++ b/tests/components/mcp4461/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - sda_pin: GPIO4 - scl_pin: GPIO5 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp4725/common.yaml b/tests/components/mcp4725/common.yaml index 0ccc649f2e..871c2805a3 100644 --- a/tests/components/mcp4725/common.yaml +++ b/tests/components/mcp4725/common.yaml @@ -1,8 +1,3 @@ -i2c: - - id: i2c_mcp4725 - scl: ${scl_pin} - sda: ${sda_pin} - output: - platform: mcp4725 id: mcp4725_dac_output diff --git a/tests/components/mcp4725/test.esp32-c3-idf.yaml b/tests/components/mcp4725/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mcp4725/test.esp32-c3-idf.yaml +++ b/tests/components/mcp4725/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp4725/test.esp32-idf.yaml b/tests/components/mcp4725/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mcp4725/test.esp32-idf.yaml +++ b/tests/components/mcp4725/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp4725/test.esp8266-ard.yaml b/tests/components/mcp4725/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mcp4725/test.esp8266-ard.yaml +++ b/tests/components/mcp4725/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp4725/test.rp2040-ard.yaml b/tests/components/mcp4725/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mcp4725/test.rp2040-ard.yaml +++ b/tests/components/mcp4725/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp4728/common.yaml b/tests/components/mcp4728/common.yaml index b42818e4e6..e60f4795e1 100644 --- a/tests/components/mcp4728/common.yaml +++ b/tests/components/mcp4728/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mcp4728 - scl: ${scl_pin} - sda: ${sda_pin} - mcp4728: - id: mcp4728_dac + i2c_id: i2c_bus output: - platform: mcp4728 diff --git a/tests/components/mcp4728/test.esp32-c3-idf.yaml b/tests/components/mcp4728/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mcp4728/test.esp32-c3-idf.yaml +++ b/tests/components/mcp4728/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp4728/test.esp32-idf.yaml b/tests/components/mcp4728/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mcp4728/test.esp32-idf.yaml +++ b/tests/components/mcp4728/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp4728/test.esp8266-ard.yaml b/tests/components/mcp4728/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mcp4728/test.esp8266-ard.yaml +++ b/tests/components/mcp4728/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp4728/test.rp2040-ard.yaml b/tests/components/mcp4728/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mcp4728/test.rp2040-ard.yaml +++ b/tests/components/mcp4728/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp47a1/common.yaml b/tests/components/mcp47a1/common.yaml index 59e28d37b3..3448fcfb31 100644 --- a/tests/components/mcp47a1/common.yaml +++ b/tests/components/mcp47a1/common.yaml @@ -1,8 +1,3 @@ -i2c: - - id: i2c_mcp47a1 - scl: ${scl_pin} - sda: ${sda_pin} - output: - platform: mcp47a1 id: output_mcp47a1 diff --git a/tests/components/mcp47a1/test.esp32-c3-idf.yaml b/tests/components/mcp47a1/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mcp47a1/test.esp32-c3-idf.yaml +++ b/tests/components/mcp47a1/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp47a1/test.esp32-idf.yaml b/tests/components/mcp47a1/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mcp47a1/test.esp32-idf.yaml +++ b/tests/components/mcp47a1/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp47a1/test.esp8266-ard.yaml b/tests/components/mcp47a1/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mcp47a1/test.esp8266-ard.yaml +++ b/tests/components/mcp47a1/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp47a1/test.rp2040-ard.yaml b/tests/components/mcp47a1/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mcp47a1/test.rp2040-ard.yaml +++ b/tests/components/mcp47a1/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp9600/common.yaml b/tests/components/mcp9600/common.yaml index e3c9df79e4..33e33d183e 100644 --- a/tests/components/mcp9600/common.yaml +++ b/tests/components/mcp9600/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mcp9600 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: mcp9600 + i2c_id: i2c_bus thermocouple_type: K hot_junction: name: Thermocouple Temperature diff --git a/tests/components/mcp9600/test.esp32-c3-idf.yaml b/tests/components/mcp9600/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mcp9600/test.esp32-c3-idf.yaml +++ b/tests/components/mcp9600/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp9600/test.esp32-idf.yaml b/tests/components/mcp9600/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mcp9600/test.esp32-idf.yaml +++ b/tests/components/mcp9600/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp9600/test.esp8266-ard.yaml b/tests/components/mcp9600/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mcp9600/test.esp8266-ard.yaml +++ b/tests/components/mcp9600/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp9600/test.rp2040-ard.yaml b/tests/components/mcp9600/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mcp9600/test.rp2040-ard.yaml +++ b/tests/components/mcp9600/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp9808/common.yaml b/tests/components/mcp9808/common.yaml index ccfd5d13ce..86d0661552 100644 --- a/tests/components/mcp9808/common.yaml +++ b/tests/components/mcp9808/common.yaml @@ -1,8 +1,4 @@ -i2c: - - id: i2c_mcp9808 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: mcp9808 + i2c_id: i2c_bus name: MCP9808 Temperature diff --git a/tests/components/mcp9808/test.esp32-c3-idf.yaml b/tests/components/mcp9808/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mcp9808/test.esp32-c3-idf.yaml +++ b/tests/components/mcp9808/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp9808/test.esp32-idf.yaml b/tests/components/mcp9808/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mcp9808/test.esp32-idf.yaml +++ b/tests/components/mcp9808/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mcp9808/test.esp8266-ard.yaml b/tests/components/mcp9808/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mcp9808/test.esp8266-ard.yaml +++ b/tests/components/mcp9808/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mcp9808/test.rp2040-ard.yaml b/tests/components/mcp9808/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mcp9808/test.rp2040-ard.yaml +++ b/tests/components/mcp9808/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mhz19/common.yaml b/tests/components/mhz19/common.yaml index 8b7e732068..94989fecbe 100644 --- a/tests/components/mhz19/common.yaml +++ b/tests/components/mhz19/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_mhz19 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: mhz19 co2: diff --git a/tests/components/mhz19/test.esp32-c3-idf.yaml b/tests/components/mhz19/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/mhz19/test.esp32-c3-idf.yaml +++ b/tests/components/mhz19/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mhz19/test.esp32-idf.yaml b/tests/components/mhz19/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/mhz19/test.esp32-idf.yaml +++ b/tests/components/mhz19/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mhz19/test.esp8266-ard.yaml b/tests/components/mhz19/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/mhz19/test.esp8266-ard.yaml +++ b/tests/components/mhz19/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mhz19/test.rp2040-ard.yaml b/tests/components/mhz19/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/mhz19/test.rp2040-ard.yaml +++ b/tests/components/mhz19/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/micronova/common.yaml b/tests/components/micronova/common.yaml index 661c9330c6..3cf8e36fb6 100644 --- a/tests/components/micronova/common.yaml +++ b/tests/components/micronova/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_micronova - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - micronova: enable_rx_pin: ${enable_rx_pin} diff --git a/tests/components/micronova/test.esp32-c3-idf.yaml b/tests/components/micronova/test.esp32-c3-idf.yaml index 993071999f..eed876ae74 100644 --- a/tests/components/micronova/test.esp32-c3-idf.yaml +++ b/tests/components/micronova/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 enable_rx_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/micronova/test.esp32-idf.yaml b/tests/components/micronova/test.esp32-idf.yaml index 35d041e047..5cc3a234ca 100644 --- a/tests/components/micronova/test.esp32-idf.yaml +++ b/tests/components/micronova/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 enable_rx_pin: GPIO13 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/micronova/test.esp8266-ard.yaml b/tests/components/micronova/test.esp8266-ard.yaml index 048fb82d72..ffe1e0a063 100644 --- a/tests/components/micronova/test.esp8266-ard.yaml +++ b/tests/components/micronova/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - enable_rx_pin: GPIO13 + enable_rx_pin: GPIO15 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/micronova/test.rp2040-ard.yaml b/tests/components/micronova/test.rp2040-ard.yaml index 993071999f..6dc030e6b6 100644 --- a/tests/components/micronova/test.rp2040-ard.yaml +++ b/tests/components/micronova/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 enable_rx_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/microphone/test.esp32-idf.yaml b/tests/components/microphone/test.esp32-idf.yaml index fe9feb9888..830f0156d7 100644 --- a/tests/components/microphone/test.esp32-idf.yaml +++ b/tests/components/microphone/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: i2s_bclk_pin: GPIO15 - i2s_lrclk_pin: GPIO16 - i2s_mclk_pin: GPIO17 + i2s_lrclk_pin: GPIO4 + i2s_mclk_pin: GPIO5 i2s_din_pin: GPIO33 i2s_audio: diff --git a/tests/components/mics_4514/common.yaml b/tests/components/mics_4514/common.yaml index 0bc3f3e654..3c1d264680 100644 --- a/tests/components/mics_4514/common.yaml +++ b/tests/components/mics_4514/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mics_4514 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: mics_4514 + i2c_id: i2c_bus update_interval: 60s nitrogen_dioxide: name: MICS-4514 NO2 diff --git a/tests/components/mics_4514/test.esp32-c3-idf.yaml b/tests/components/mics_4514/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mics_4514/test.esp32-c3-idf.yaml +++ b/tests/components/mics_4514/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mics_4514/test.esp32-idf.yaml b/tests/components/mics_4514/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mics_4514/test.esp32-idf.yaml +++ b/tests/components/mics_4514/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mics_4514/test.esp8266-ard.yaml b/tests/components/mics_4514/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mics_4514/test.esp8266-ard.yaml +++ b/tests/components/mics_4514/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mics_4514/test.rp2040-ard.yaml b/tests/components/mics_4514/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mics_4514/test.rp2040-ard.yaml +++ b/tests/components/mics_4514/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/midea/common.yaml b/tests/components/midea/common.yaml index 07385e29a1..a0909401ff 100644 --- a/tests/components/midea/common.yaml +++ b/tests/components/midea/common.yaml @@ -6,12 +6,6 @@ remote_transmitter: pin: ${pin} carrier_duty_percent: 50% -uart: - - id: uart_midea - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - climate: - platform: midea id: midea_unit diff --git a/tests/components/midea/test.esp32-ard.yaml b/tests/components/midea/test.esp32-ard.yaml index 7f55d6a52d..b78163199a 100644 --- a/tests/components/midea/test.esp32-ard.yaml +++ b/tests/components/midea/test.esp32-ard.yaml @@ -1,6 +1,7 @@ substitutions: - tx_pin: GPIO12 - rx_pin: GPIO14 pin: GPIO2 +packages: + uart: !include ../../test_build_components/common/uart/esp32-ard.yaml + <<: !include common.yaml diff --git a/tests/components/midea/test.esp8266-ard.yaml b/tests/components/midea/test.esp8266-ard.yaml index 4f50bd7e5e..dc276e274c 100644 --- a/tests/components/midea/test.esp8266-ard.yaml +++ b/tests/components/midea/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 pin: GPIO15 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mipi_dsi/test.esp32-p4-idf.yaml b/tests/components/mipi_dsi/test.esp32-p4-idf.yaml index 9c4eb07d9b..770b11d089 100644 --- a/tests/components/mipi_dsi/test.esp32-p4-idf.yaml +++ b/tests/components/mipi_dsi/test.esp32-p4-idf.yaml @@ -1,3 +1,6 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-p4-idf.yaml + esp_ldo: - id: ldo_id channel: 3 @@ -13,9 +16,3 @@ display: #id: backlight_id psram: - -i2c: - sda: GPIO7 - scl: GPIO8 - scan: true - frequency: 400kHz diff --git a/tests/components/mipi_rgb/test.esp32-s3-idf.yaml b/tests/components/mipi_rgb/test.esp32-s3-idf.yaml index 8d0e20d6f5..29f833c235 100644 --- a/tests/components/mipi_rgb/test.esp32-s3-idf.yaml +++ b/tests/components/mipi_rgb/test.esp32-s3-idf.yaml @@ -1,16 +1,12 @@ +packages: + spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + psram: mode: octal -spi: - - clk_pin: - number: 47 - allow_other_uses: true - mosi_pin: - number: 41 - allow_other_uses: true - display: - platform: mipi_rgb + spi_id: spi_bus model: ZX2D10GE01R-V4848 update_interval: 1s color_order: BGR diff --git a/tests/components/mipi_spi/common.yaml b/tests/components/mipi_spi/common.yaml index 2c84489ec7..03f807f53c 100644 --- a/tests/components/mipi_spi/common.yaml +++ b/tests/components/mipi_spi/common.yaml @@ -1,11 +1,3 @@ -spi: - - id: spi_single - clk_pin: - number: ${clk_pin} - allow_other_uses: true - mosi_pin: - number: ${mosi_pin} - display: - platform: mipi_spi spi_16: true @@ -30,8 +22,5 @@ display: dimensions: width: 100 height: 200 - enable_pin: - - number: ${clk_pin} - allow_other_uses: true - - number: ${enable_pin} + enable_pin: ${enable_pin} bus_mode: single diff --git a/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml index e0f65a3a6a..48f34f3449 100644 --- a/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml +++ b/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml @@ -1,16 +1,9 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - -spi: - - id: spi_single - clk_pin: - number: ${clk_pin} - mosi_pin: - number: ${mosi_pin} +packages: + spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml display: - platform: mipi_spi + spi_id: spi_bus model: t-display-s3-pro lvgl: diff --git a/tests/components/mipi_spi/test.esp32-c3-idf.yaml b/tests/components/mipi_spi/test.esp32-c3-idf.yaml index c17748c569..55e25d8318 100644 --- a/tests/components/mipi_spi/test.esp32-c3-idf.yaml +++ b/tests/components/mipi_spi/test.esp32-c3-idf.yaml @@ -1,10 +1,10 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 - dc_pin: GPIO21 - cs_pin: GPIO18 - enable_pin: GPIO19 + dc_pin: GPIO7 + cs_pin: GPIO8 + enable_pin: GPIO9 reset_pin: GPIO20 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mipi_spi/test.esp32-idf.yaml b/tests/components/mipi_spi/test.esp32-idf.yaml index 653ccb4910..b173b8de87 100644 --- a/tests/components/mipi_spi/test.esp32-idf.yaml +++ b/tests/components/mipi_spi/test.esp32-idf.yaml @@ -1,15 +1,10 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 - dc_pin: GPIO21 - cs_pin: GPIO18 - enable_pin: GPIO19 + dc_pin: GPIO14 + cs_pin: GPIO13 + enable_pin: GPIO4 reset_pin: GPIO20 packages: - display: !include common.yaml + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml -display: - - platform: mipi_spi - model: m5core +<<: !include common.yaml diff --git a/tests/components/mipi_spi/test.rp2040-ard.yaml b/tests/components/mipi_spi/test.rp2040-ard.yaml index 5d7333853b..380cebcde3 100644 --- a/tests/components/mipi_spi/test.rp2040-ard.yaml +++ b/tests/components/mipi_spi/test.rp2040-ard.yaml @@ -1,10 +1,10 @@ substitutions: - clk_pin: GPIO2 - mosi_pin: GPIO3 - miso_pin: GPIO4 dc_pin: GPIO14 cs_pin: GPIO13 - enable_pin: GPIO19 + enable_pin: GPIO16 reset_pin: GPIO20 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mixer/test.esp32-idf.yaml b/tests/components/mixer/test.esp32-idf.yaml index 96d2d37458..6712f1e468 100644 --- a/tests/components/mixer/test.esp32-idf.yaml +++ b/tests/components/mixer/test.esp32-idf.yaml @@ -1,7 +1,10 @@ substitutions: - lrclk_pin: GPIO16 - bclk_pin: GPIO17 + lrclk_pin: GPIO4 + bclk_pin: GPIO5 mclk_pin: GPIO15 dout_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mlx90393/common.yaml b/tests/components/mlx90393/common.yaml index 58f3b6ecf5..9e85e06c89 100644 --- a/tests/components/mlx90393/common.yaml +++ b/tests/components/mlx90393/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mlx90393 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: mlx90393 + i2c_id: i2c_bus oversampling: 3 gain: 1X temperature_compensation: true diff --git a/tests/components/mlx90393/test.esp32-c3-idf.yaml b/tests/components/mlx90393/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mlx90393/test.esp32-c3-idf.yaml +++ b/tests/components/mlx90393/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp32-idf.yaml b/tests/components/mlx90393/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mlx90393/test.esp32-idf.yaml +++ b/tests/components/mlx90393/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp32-s3-idf.yaml b/tests/components/mlx90393/test.esp32-s3-idf.yaml index ee2c29ca4e..0fd8684a2c 100644 --- a/tests/components/mlx90393/test.esp32-s3-idf.yaml +++ b/tests/components/mlx90393/test.esp32-s3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp8266-ard.yaml b/tests/components/mlx90393/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mlx90393/test.esp8266-ard.yaml +++ b/tests/components/mlx90393/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mlx90393/test.rp2040-ard.yaml b/tests/components/mlx90393/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mlx90393/test.rp2040-ard.yaml +++ b/tests/components/mlx90393/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mlx90614/common.yaml b/tests/components/mlx90614/common.yaml index 03279e6e14..8d408fb016 100644 --- a/tests/components/mlx90614/common.yaml +++ b/tests/components/mlx90614/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mlx90614 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: mlx90614 + i2c_id: i2c_bus ambient: name: Ambient object: diff --git a/tests/components/mlx90614/test.esp32-c3-idf.yaml b/tests/components/mlx90614/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mlx90614/test.esp32-c3-idf.yaml +++ b/tests/components/mlx90614/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mlx90614/test.esp32-idf.yaml b/tests/components/mlx90614/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mlx90614/test.esp32-idf.yaml +++ b/tests/components/mlx90614/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mlx90614/test.esp8266-ard.yaml b/tests/components/mlx90614/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mlx90614/test.esp8266-ard.yaml +++ b/tests/components/mlx90614/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mlx90614/test.rp2040-ard.yaml b/tests/components/mlx90614/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mlx90614/test.rp2040-ard.yaml +++ b/tests/components/mlx90614/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mmc5603/common.yaml b/tests/components/mmc5603/common.yaml index 83235f596e..6f6e35e9af 100644 --- a/tests/components/mmc5603/common.yaml +++ b/tests/components/mmc5603/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mmc5603 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: mmc5603 + i2c_id: i2c_bus address: 0x30 field_strength_x: name: HMC5883L Field Strength X diff --git a/tests/components/mmc5603/test.esp32-c3-idf.yaml b/tests/components/mmc5603/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mmc5603/test.esp32-c3-idf.yaml +++ b/tests/components/mmc5603/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mmc5603/test.esp32-idf.yaml b/tests/components/mmc5603/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mmc5603/test.esp32-idf.yaml +++ b/tests/components/mmc5603/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mmc5603/test.esp8266-ard.yaml b/tests/components/mmc5603/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mmc5603/test.esp8266-ard.yaml +++ b/tests/components/mmc5603/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mmc5603/test.rp2040-ard.yaml b/tests/components/mmc5603/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mmc5603/test.rp2040-ard.yaml +++ b/tests/components/mmc5603/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mmc5983/common.yaml b/tests/components/mmc5983/common.yaml index 949ff527e7..963a2527c1 100644 --- a/tests/components/mmc5983/common.yaml +++ b/tests/components/mmc5983/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mmc5983 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: mmc5983 + i2c_id: i2c_bus field_strength_x: name: "Magnet X" id: magnet_x diff --git a/tests/components/mmc5983/test.esp32-c3-idf.yaml b/tests/components/mmc5983/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mmc5983/test.esp32-c3-idf.yaml +++ b/tests/components/mmc5983/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mmc5983/test.esp32-idf.yaml b/tests/components/mmc5983/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mmc5983/test.esp32-idf.yaml +++ b/tests/components/mmc5983/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mmc5983/test.esp8266-ard.yaml b/tests/components/mmc5983/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mmc5983/test.esp8266-ard.yaml +++ b/tests/components/mmc5983/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mmc5983/test.rp2040-ard.yaml b/tests/components/mmc5983/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mmc5983/test.rp2040-ard.yaml +++ b/tests/components/mmc5983/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/modbus/common.yaml b/tests/components/modbus/common.yaml index 3ec9518585..d636143ec9 100644 --- a/tests/components/modbus/common.yaml +++ b/tests/components/modbus/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_modbus - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - modbus: id: mod_bus1 flow_control_pin: ${flow_control_pin} diff --git a/tests/components/modbus/test.esp32-c3-idf.yaml b/tests/components/modbus/test.esp32-c3-idf.yaml index 452031a5aa..430c6818cb 100644 --- a/tests/components/modbus/test.esp32-c3-idf.yaml +++ b/tests/components/modbus/test.esp32-c3-idf.yaml @@ -1,6 +1,6 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 flow_control_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/modbus/test.esp32-idf.yaml b/tests/components/modbus/test.esp32-idf.yaml index bd767a8ece..8a08f8a821 100644 --- a/tests/components/modbus/test.esp32-idf.yaml +++ b/tests/components/modbus/test.esp32-idf.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO14 flow_control_pin: GPIO13 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/modbus/test.esp8266-ard.yaml b/tests/components/modbus/test.esp8266-ard.yaml index 29c98d0957..dfea36d957 100644 --- a/tests/components/modbus/test.esp8266-ard.yaml +++ b/tests/components/modbus/test.esp8266-ard.yaml @@ -1,6 +1,9 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - flow_control_pin: GPIO13 + tx_pin: GPIO0 + rx_pin: GPIO2 + flow_control_pin: GPIO15 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/modbus/test.rp2040-ard.yaml b/tests/components/modbus/test.rp2040-ard.yaml index 452031a5aa..43f12ea28d 100644 --- a/tests/components/modbus/test.rp2040-ard.yaml +++ b/tests/components/modbus/test.rp2040-ard.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO5 flow_control_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/modbus_controller/common.yaml b/tests/components/modbus_controller/common.yaml index c2b5ab737f..ae5520e57d 100644 --- a/tests/components/modbus_controller/common.yaml +++ b/tests/components/modbus_controller/common.yaml @@ -1,25 +1,12 @@ -uart: - - id: uart_modbus_client - tx_pin: ${client_tx_pin} - rx_pin: ${client_rx_pin} - baud_rate: 9600 - - id: uart_modbus_server - tx_pin: ${server_tx_pin} - rx_pin: ${server_rx_pin} - baud_rate: 9600 - modbus: - - id: mod_bus1 - uart_id: uart_modbus_client - flow_control_pin: ${flow_control_pin} - id: mod_bus2 - uart_id: uart_modbus_server + uart_id: uart_bus role: server modbus_controller: - id: modbus_controller1 address: 0x2 - modbus_id: mod_bus1 + modbus_id: modbus_bus allow_duplicate_commands: false on_online: then: diff --git a/tests/components/modbus_controller/test.esp32-c3-idf.yaml b/tests/components/modbus_controller/test.esp32-c3-idf.yaml index f5b770ff58..db826676ee 100644 --- a/tests/components/modbus_controller/test.esp32-c3-idf.yaml +++ b/tests/components/modbus_controller/test.esp32-c3-idf.yaml @@ -1,8 +1,4 @@ -substitutions: - client_tx_pin: GPIO4 - client_rx_pin: GPIO5 - server_tx_pin: GPIO6 - server_rx_pin: GPIO7 - flow_control_pin: GPIO3 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/modbus_controller/test.esp32-idf.yaml b/tests/components/modbus_controller/test.esp32-idf.yaml index 548b8c0666..ace2d95a0b 100644 --- a/tests/components/modbus_controller/test.esp32-idf.yaml +++ b/tests/components/modbus_controller/test.esp32-idf.yaml @@ -1,8 +1,4 @@ -substitutions: - client_tx_pin: GPIO12 - client_rx_pin: GPIO14 - server_tx_pin: GPIO16 - server_rx_pin: GPIO17 - flow_control_pin: GPIO13 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/modbus_controller/test.esp8266-ard.yaml b/tests/components/modbus_controller/test.esp8266-ard.yaml index c68a57cbde..560629b0cd 100644 --- a/tests/components/modbus_controller/test.esp8266-ard.yaml +++ b/tests/components/modbus_controller/test.esp8266-ard.yaml @@ -1,8 +1,4 @@ -substitutions: - client_tx_pin: GPIO1 - client_rx_pin: GPIO3 - server_tx_pin: GPIO4 - server_rx_pin: GPIO5 - flow_control_pin: GPIO13 +packages: + modbus: !include ../../test_build_components/common/modbus/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/modbus_controller/test.rp2040-ard.yaml b/tests/components/modbus_controller/test.rp2040-ard.yaml index 80528bc12b..eeebbd2a8a 100644 --- a/tests/components/modbus_controller/test.rp2040-ard.yaml +++ b/tests/components/modbus_controller/test.rp2040-ard.yaml @@ -1,8 +1,4 @@ -substitutions: - client_tx_pin: GPIO2 - client_rx_pin: GPIO3 - server_tx_pin: GPIO4 - server_rx_pin: GPIO5 - flow_control_pin: GPIO6 +packages: + modbus: !include ../../test_build_components/common/modbus/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mopeka_ble/test.esp32-c3-idf.yaml b/tests/components/mopeka_ble/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/mopeka_ble/test.esp32-c3-idf.yaml +++ b/tests/components/mopeka_ble/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mopeka_ble/test.esp32-idf.yaml b/tests/components/mopeka_ble/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/mopeka_ble/test.esp32-idf.yaml +++ b/tests/components/mopeka_ble/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mopeka_pro_check/test.esp32-c3-idf.yaml b/tests/components/mopeka_pro_check/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/mopeka_pro_check/test.esp32-c3-idf.yaml +++ b/tests/components/mopeka_pro_check/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mopeka_pro_check/test.esp32-idf.yaml b/tests/components/mopeka_pro_check/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/mopeka_pro_check/test.esp32-idf.yaml +++ b/tests/components/mopeka_pro_check/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mopeka_std_check/test.esp32-c3-idf.yaml b/tests/components/mopeka_std_check/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/mopeka_std_check/test.esp32-c3-idf.yaml +++ b/tests/components/mopeka_std_check/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mopeka_std_check/test.esp32-idf.yaml b/tests/components/mopeka_std_check/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/mopeka_std_check/test.esp32-idf.yaml +++ b/tests/components/mopeka_std_check/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mpl3115a2/common.yaml b/tests/components/mpl3115a2/common.yaml index f2f65885b3..aa6c2b77fc 100644 --- a/tests/components/mpl3115a2/common.yaml +++ b/tests/components/mpl3115a2/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mpl3115a2 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: mpl3115a2 + i2c_id: i2c_bus temperature: name: MPL3115A2 Temperature pressure: diff --git a/tests/components/mpl3115a2/test.esp32-c3-idf.yaml b/tests/components/mpl3115a2/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mpl3115a2/test.esp32-c3-idf.yaml +++ b/tests/components/mpl3115a2/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.esp32-idf.yaml b/tests/components/mpl3115a2/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mpl3115a2/test.esp32-idf.yaml +++ b/tests/components/mpl3115a2/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.esp8266-ard.yaml b/tests/components/mpl3115a2/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mpl3115a2/test.esp8266-ard.yaml +++ b/tests/components/mpl3115a2/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.rp2040-ard.yaml b/tests/components/mpl3115a2/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mpl3115a2/test.rp2040-ard.yaml +++ b/tests/components/mpl3115a2/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mpr121/common.yaml b/tests/components/mpr121/common.yaml index fcf61b57f3..67a06cf9c1 100644 --- a/tests/components/mpr121/common.yaml +++ b/tests/components/mpr121/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_mpr121 - scl: ${i2c_scl} - sda: ${i2c_sda} - mpr121: + i2c_id: i2c_bus id: mpr121_first address: 0x5A diff --git a/tests/components/mpr121/test.esp32-c3-idf.yaml b/tests/components/mpr121/test.esp32-c3-idf.yaml index d7ae0d5161..d1abb03369 100644 --- a/tests/components/mpr121/test.esp32-c3-idf.yaml +++ b/tests/components/mpr121/test.esp32-c3-idf.yaml @@ -2,4 +2,7 @@ substitutions: i2c_scl: GPIO5 i2c_sda: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mpr121/test.esp32-idf.yaml b/tests/components/mpr121/test.esp32-idf.yaml index 1037d5d35b..4598505c3a 100644 --- a/tests/components/mpr121/test.esp32-idf.yaml +++ b/tests/components/mpr121/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: i2c_scl: GPIO16 i2c_sda: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mpr121/test.esp8266-ard.yaml b/tests/components/mpr121/test.esp8266-ard.yaml index d7ae0d5161..5565bb8c35 100644 --- a/tests/components/mpr121/test.esp8266-ard.yaml +++ b/tests/components/mpr121/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: i2c_scl: GPIO5 i2c_sda: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mpr121/test.rp2040-ard.yaml b/tests/components/mpr121/test.rp2040-ard.yaml index d7ae0d5161..888762a742 100644 --- a/tests/components/mpr121/test.rp2040-ard.yaml +++ b/tests/components/mpr121/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: i2c_scl: GPIO5 i2c_sda: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mpu6050/common.yaml b/tests/components/mpu6050/common.yaml index e9d4995534..7ac123f8c7 100644 --- a/tests/components/mpu6050/common.yaml +++ b/tests/components/mpu6050/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mpu6050 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: mpu6050 + i2c_id: i2c_bus address: 0x68 accel_x: name: MPU6050 Accel X diff --git a/tests/components/mpu6050/test.esp32-c3-idf.yaml b/tests/components/mpu6050/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mpu6050/test.esp32-c3-idf.yaml +++ b/tests/components/mpu6050/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mpu6050/test.esp32-idf.yaml b/tests/components/mpu6050/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mpu6050/test.esp32-idf.yaml +++ b/tests/components/mpu6050/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mpu6050/test.esp8266-ard.yaml b/tests/components/mpu6050/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mpu6050/test.esp8266-ard.yaml +++ b/tests/components/mpu6050/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mpu6050/test.rp2040-ard.yaml b/tests/components/mpu6050/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mpu6050/test.rp2040-ard.yaml +++ b/tests/components/mpu6050/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mpu6886/common.yaml b/tests/components/mpu6886/common.yaml index 9c3e283cc3..9f7324f384 100644 --- a/tests/components/mpu6886/common.yaml +++ b/tests/components/mpu6886/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_mpu6886 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: mpu6886 + i2c_id: i2c_bus address: 0x68 accel_x: name: MPU6886 Accel X diff --git a/tests/components/mpu6886/test.esp32-c3-idf.yaml b/tests/components/mpu6886/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/mpu6886/test.esp32-c3-idf.yaml +++ b/tests/components/mpu6886/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/mpu6886/test.esp32-idf.yaml b/tests/components/mpu6886/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/mpu6886/test.esp32-idf.yaml +++ b/tests/components/mpu6886/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/mpu6886/test.esp8266-ard.yaml b/tests/components/mpu6886/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/mpu6886/test.esp8266-ard.yaml +++ b/tests/components/mpu6886/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/mpu6886/test.rp2040-ard.yaml b/tests/components/mpu6886/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/mpu6886/test.rp2040-ard.yaml +++ b/tests/components/mpu6886/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/mqtt/common-update.yaml b/tests/components/mqtt/common-update.yaml index 25f57cfef2..2e21e9687a 100644 --- a/tests/components/mqtt/common-update.yaml +++ b/tests/components/mqtt/common-update.yaml @@ -6,8 +6,10 @@ http_request: ota: - platform: http_request + id: mqtt_http_request_ota update: - platform: http_request name: "OTA Update" + ota_id: mqtt_http_request_ota source: https://example.com/ota.json diff --git a/tests/components/ms5611/common.yaml b/tests/components/ms5611/common.yaml index d0417faa86..12644fa330 100644 --- a/tests/components/ms5611/common.yaml +++ b/tests/components/ms5611/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_ms5611 - scl: ${i2c_scl} - sda: ${i2c_sda} - sensor: - platform: ms5611 + i2c_id: i2c_bus temperature: name: Outside Temperature pressure: diff --git a/tests/components/ms5611/test.esp32-c3-idf.yaml b/tests/components/ms5611/test.esp32-c3-idf.yaml index d7ae0d5161..d1abb03369 100644 --- a/tests/components/ms5611/test.esp32-c3-idf.yaml +++ b/tests/components/ms5611/test.esp32-c3-idf.yaml @@ -2,4 +2,7 @@ substitutions: i2c_scl: GPIO5 i2c_sda: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ms5611/test.esp32-idf.yaml b/tests/components/ms5611/test.esp32-idf.yaml index 1037d5d35b..4598505c3a 100644 --- a/tests/components/ms5611/test.esp32-idf.yaml +++ b/tests/components/ms5611/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: i2c_scl: GPIO16 i2c_sda: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ms5611/test.esp8266-ard.yaml b/tests/components/ms5611/test.esp8266-ard.yaml index d7ae0d5161..5565bb8c35 100644 --- a/tests/components/ms5611/test.esp8266-ard.yaml +++ b/tests/components/ms5611/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: i2c_scl: GPIO5 i2c_sda: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ms5611/test.rp2040-ard.yaml b/tests/components/ms5611/test.rp2040-ard.yaml index d7ae0d5161..888762a742 100644 --- a/tests/components/ms5611/test.rp2040-ard.yaml +++ b/tests/components/ms5611/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: i2c_scl: GPIO5 i2c_sda: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/msa3xx/common.yaml b/tests/components/msa3xx/common.yaml index 8de6a8a89a..297ed8a741 100644 --- a/tests/components/msa3xx/common.yaml +++ b/tests/components/msa3xx/common.yaml @@ -1,5 +1,5 @@ msa3xx: - i2c_id: i2c_msa3xx + i2c_id: i2c_bus type: msa301 range: 4G resolution: 14 diff --git a/tests/components/msa3xx/test.esp32-c3-idf.yaml b/tests/components/msa3xx/test.esp32-c3-idf.yaml index b972ce8cdb..9990d96d29 100644 --- a/tests/components/msa3xx/test.esp32-c3-idf.yaml +++ b/tests/components/msa3xx/test.esp32-c3-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_msa3xx - scl: GPIO5 - sda: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/msa3xx/test.esp32-idf.yaml b/tests/components/msa3xx/test.esp32-idf.yaml index 7202e7b9bf..b47e39c389 100644 --- a/tests/components/msa3xx/test.esp32-idf.yaml +++ b/tests/components/msa3xx/test.esp32-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_msa3xx - scl: GPIO16 - sda: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/msa3xx/test.esp8266-ard.yaml b/tests/components/msa3xx/test.esp8266-ard.yaml index b972ce8cdb..4a98b9388a 100644 --- a/tests/components/msa3xx/test.esp8266-ard.yaml +++ b/tests/components/msa3xx/test.esp8266-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_msa3xx - scl: GPIO5 - sda: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/msa3xx/test.rp2040-ard.yaml b/tests/components/msa3xx/test.rp2040-ard.yaml index b972ce8cdb..319a7c71a6 100644 --- a/tests/components/msa3xx/test.rp2040-ard.yaml +++ b/tests/components/msa3xx/test.rp2040-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_msa3xx - scl: GPIO5 - sda: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/nau7802/common.yaml b/tests/components/nau7802/common.yaml index 2501911a3f..5c52c33dad 100644 --- a/tests/components/nau7802/common.yaml +++ b/tests/components/nau7802/common.yaml @@ -1,8 +1,8 @@ sensor: - platform: nau7802 + i2c_id: i2c_bus id: test_id name: weight - i2c_id: i2c_nau7802 gain: 32 ldo_voltage: "3.0v" samples_per_second: 10 diff --git a/tests/components/nau7802/test.esp32-c3-idf.yaml b/tests/components/nau7802/test.esp32-c3-idf.yaml index 769468f9ec..9990d96d29 100644 --- a/tests/components/nau7802/test.esp32-c3-idf.yaml +++ b/tests/components/nau7802/test.esp32-c3-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_nau7802 - scl: 5 - sda: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/nau7802/test.esp32-idf.yaml b/tests/components/nau7802/test.esp32-idf.yaml index 73a4aa4251..b47e39c389 100644 --- a/tests/components/nau7802/test.esp32-idf.yaml +++ b/tests/components/nau7802/test.esp32-idf.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_nau7802 - scl: 16 - sda: 17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/nau7802/test.esp8266-ard.yaml b/tests/components/nau7802/test.esp8266-ard.yaml index 769468f9ec..4a98b9388a 100644 --- a/tests/components/nau7802/test.esp8266-ard.yaml +++ b/tests/components/nau7802/test.esp8266-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_nau7802 - scl: 5 - sda: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/nau7802/test.rp2040-ard.yaml b/tests/components/nau7802/test.rp2040-ard.yaml index 769468f9ec..319a7c71a6 100644 --- a/tests/components/nau7802/test.rp2040-ard.yaml +++ b/tests/components/nau7802/test.rp2040-ard.yaml @@ -1,6 +1,4 @@ -i2c: - - id: i2c_nau7802 - scl: 5 - sda: 4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/nextion/common.yaml b/tests/components/nextion/common.yaml index 767c868d0b..f5ee12a51c 100644 --- a/tests/components/nextion/common.yaml +++ b/tests/components/nextion/common.yaml @@ -236,12 +236,6 @@ wifi: ssid: MySSID password: password1 -uart: - - id: uart_nextion - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 115200 - binary_sensor: - platform: nextion page_id: 0 diff --git a/tests/components/nextion/test.esp32-ard.yaml b/tests/components/nextion/test.esp32-ard.yaml index d5e02b8b85..7e94a9b4a5 100644 --- a/tests/components/nextion/test.esp32-ard.yaml +++ b/tests/components/nextion/test.esp32-ard.yaml @@ -1,8 +1,5 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - packages: + uart: !include ../../test_build_components/common/uart/esp32-ard.yaml base: !include common.yaml display: diff --git a/tests/components/nextion/test.esp32-c3-idf.yaml b/tests/components/nextion/test.esp32-c3-idf.yaml index 5135c7e4f4..888693f909 100644 --- a/tests/components/nextion/test.esp32-c3-idf.yaml +++ b/tests/components/nextion/test.esp32-c3-idf.yaml @@ -1,8 +1,5 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml base: !include common.yaml display: diff --git a/tests/components/nextion/test.esp32-idf.yaml b/tests/components/nextion/test.esp32-idf.yaml index d5e02b8b85..99820f0f8d 100644 --- a/tests/components/nextion/test.esp32-idf.yaml +++ b/tests/components/nextion/test.esp32-idf.yaml @@ -1,8 +1,5 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 - packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml base: !include common.yaml display: diff --git a/tests/components/nextion/test.esp8266-ard.yaml b/tests/components/nextion/test.esp8266-ard.yaml index 5135c7e4f4..49f79b2f4c 100644 --- a/tests/components/nextion/test.esp8266-ard.yaml +++ b/tests/components/nextion/test.esp8266-ard.yaml @@ -1,8 +1,5 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml base: !include common.yaml display: diff --git a/tests/components/nextion/test.rp2040-ard.yaml b/tests/components/nextion/test.rp2040-ard.yaml index 44534b97a8..23cc71ee7c 100644 --- a/tests/components/nextion/test.rp2040-ard.yaml +++ b/tests/components/nextion/test.rp2040-ard.yaml @@ -1,6 +1,3 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml base: !include common.yaml diff --git a/tests/components/npi19/common.yaml b/tests/components/npi19/common.yaml index 012e05695e..03550c269f 100644 --- a/tests/components/npi19/common.yaml +++ b/tests/components/npi19/common.yaml @@ -1,13 +1,7 @@ -i2c: - id: i2c_bus - scl: ${scl_pin} - sda: ${sda_pin} - frequency: 200kHz - sensor: - platform: npi19 - update_interval: 1s i2c_id: i2c_bus + update_interval: 1s temperature: name: water temperature diff --git a/tests/components/npi19/test.esp32-idf.yaml b/tests/components/npi19/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/npi19/test.esp32-idf.yaml +++ b/tests/components/npi19/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/npi19/test.esp32-s3-idf.yaml b/tests/components/npi19/test.esp32-s3-idf.yaml index 4942e3c2b3..0fd8684a2c 100644 --- a/tests/components/npi19/test.esp32-s3-idf.yaml +++ b/tests/components/npi19/test.esp32-s3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/components/npi19/test.esp8266-ard.yaml b/tests/components/npi19/test.esp8266-ard.yaml index 3be5e53dcb..4a98b9388a 100644 --- a/tests/components/npi19/test.esp8266-ard.yaml +++ b/tests/components/npi19/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO05 - sda_pin: GPIO04 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/online_image/common-esp32.yaml b/tests/components/online_image/common-esp32.yaml index 787a1ad368..32c909d351 100644 --- a/tests/components/online_image/common-esp32.yaml +++ b/tests/components/online_image/common-esp32.yaml @@ -1,13 +1,11 @@ -<<: !include common.yaml +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 - miso_pin: 18 +<<: !include common.yaml display: - platform: ili9xxx + spi_id: spi_bus id: main_lcd model: ili9342 cs_pin: 20 diff --git a/tests/components/online_image/common-esp8266.yaml b/tests/components/online_image/common-esp8266.yaml index ba15b5025c..d7722d171a 100644 --- a/tests/components/online_image/common-esp8266.yaml +++ b/tests/components/online_image/common-esp8266.yaml @@ -1,13 +1,11 @@ -<<: !include common.yaml +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml -spi: - - id: spi_main_lcd - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +<<: !include common.yaml display: - platform: ili9xxx + spi_id: spi_bus id: main_lcd model: ili9342 cs_pin: 15 diff --git a/tests/components/online_image/common-rp2040.yaml b/tests/components/online_image/common-rp2040.yaml index 16bb2b2c44..25891b94bc 100644 --- a/tests/components/online_image/common-rp2040.yaml +++ b/tests/components/online_image/common-rp2040.yaml @@ -1,13 +1,11 @@ -<<: !include common.yaml +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml -spi: - - id: spi_main_lcd - clk_pin: 18 - mosi_pin: 19 - miso_pin: 16 +<<: !include common.yaml display: - platform: ili9xxx + spi_id: spi_bus id: main_lcd model: ili9342 cs_pin: 20 diff --git a/tests/components/opt3001/common.yaml b/tests/components/opt3001/common.yaml index dab4f824f8..7b2cb339af 100644 --- a/tests/components/opt3001/common.yaml +++ b/tests/components/opt3001/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_opt3001 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: opt3001 + i2c_id: i2c_bus name: Living Room Brightness address: 0x44 update_interval: 30s diff --git a/tests/components/opt3001/test.esp32-c3-idf.yaml b/tests/components/opt3001/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/opt3001/test.esp32-c3-idf.yaml +++ b/tests/components/opt3001/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/opt3001/test.esp32-idf.yaml b/tests/components/opt3001/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/opt3001/test.esp32-idf.yaml +++ b/tests/components/opt3001/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/opt3001/test.esp8266-ard.yaml b/tests/components/opt3001/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/opt3001/test.esp8266-ard.yaml +++ b/tests/components/opt3001/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/opt3001/test.rp2040-ard.yaml b/tests/components/opt3001/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/opt3001/test.rp2040-ard.yaml +++ b/tests/components/opt3001/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/packet_transport/test.esp32-c3-idf.yaml b/tests/components/packet_transport/test.esp32-c3-idf.yaml index dade44d145..9990d96d29 100644 --- a/tests/components/packet_transport/test.esp32-c3-idf.yaml +++ b/tests/components/packet_transport/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/packet_transport/test.esp32-idf.yaml b/tests/components/packet_transport/test.esp32-idf.yaml index dade44d145..b47e39c389 100644 --- a/tests/components/packet_transport/test.esp32-idf.yaml +++ b/tests/components/packet_transport/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/packet_transport/test.esp8266-ard.yaml b/tests/components/packet_transport/test.esp8266-ard.yaml index dade44d145..4a98b9388a 100644 --- a/tests/components/packet_transport/test.esp8266-ard.yaml +++ b/tests/components/packet_transport/test.esp8266-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/packet_transport/test.host.yaml b/tests/components/packet_transport/test.host.yaml index e735c37e4d..49fdbbc9b2 100644 --- a/tests/components/packet_transport/test.host.yaml +++ b/tests/components/packet_transport/test.host.yaml @@ -1,4 +1,40 @@ -packages: - common: !include common.yaml +udp: + listen_address: 239.0.60.53 + addresses: ["239.0.60.53"] -wifi: !remove +packet_transport: + platform: udp + update_interval: 5s + encryption: "our key goes here" + rolling_code_enable: true + ping_pong_enable: true + binary_sensors: + - binary_sensor_id1 + - id: binary_sensor_id1 + broadcast_id: other_id + sensors: + - sensor_id1 + - id: sensor_id1 + broadcast_id: other_id + providers: + - name: some-device-name + encryption: "their key goes here" + +sensor: + - platform: template + id: sensor_id1 + - platform: packet_transport + provider: some-device-name + id: our_id + remote_id: some_sensor_id + +binary_sensor: + - platform: packet_transport + provider: unencrypted-device + id: other_binary_sensor_id + - platform: packet_transport + provider: some-device-name + type: status + name: Some-Device Status + - platform: template + id: binary_sensor_id1 diff --git a/tests/components/packet_transport/test.rp2040-ard.yaml b/tests/components/packet_transport/test.rp2040-ard.yaml index dade44d145..319a7c71a6 100644 --- a/tests/components/packet_transport/test.rp2040-ard.yaml +++ b/tests/components/packet_transport/test.rp2040-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pca6416a/common.yaml b/tests/components/pca6416a/common.yaml index ea538387e4..9ad6e2fb15 100644 --- a/tests/components/pca6416a/common.yaml +++ b/tests/components/pca6416a/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_pca6416a - scl: ${scl_pin} - sda: ${sda_pin} - pca6416a: - id: pca6416a_hub + i2c_id: i2c_bus address: 0x21 binary_sensor: diff --git a/tests/components/pca6416a/test.esp32-c3-idf.yaml b/tests/components/pca6416a/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/pca6416a/test.esp32-c3-idf.yaml +++ b/tests/components/pca6416a/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pca6416a/test.esp32-idf.yaml b/tests/components/pca6416a/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/pca6416a/test.esp32-idf.yaml +++ b/tests/components/pca6416a/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pca6416a/test.esp8266-ard.yaml b/tests/components/pca6416a/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/pca6416a/test.esp8266-ard.yaml +++ b/tests/components/pca6416a/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pca6416a/test.rp2040-ard.yaml b/tests/components/pca6416a/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/pca6416a/test.rp2040-ard.yaml +++ b/tests/components/pca6416a/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/pca9554/common.yaml b/tests/components/pca9554/common.yaml index db2ec2b826..9e5e7f3342 100644 --- a/tests/components/pca9554/common.yaml +++ b/tests/components/pca9554/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_pca9554 - scl: ${scl_pin} - sda: ${sda_pin} - pca9554: - id: pca9554_hub + i2c_id: i2c_bus pin_count: 8 address: 0x3F diff --git a/tests/components/pca9554/test.esp32-c3-idf.yaml b/tests/components/pca9554/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/pca9554/test.esp32-c3-idf.yaml +++ b/tests/components/pca9554/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pca9554/test.esp32-idf.yaml b/tests/components/pca9554/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/pca9554/test.esp32-idf.yaml +++ b/tests/components/pca9554/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pca9554/test.esp8266-ard.yaml b/tests/components/pca9554/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/pca9554/test.esp8266-ard.yaml +++ b/tests/components/pca9554/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pca9554/test.rp2040-ard.yaml b/tests/components/pca9554/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/pca9554/test.rp2040-ard.yaml +++ b/tests/components/pca9554/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/pca9685/common.yaml b/tests/components/pca9685/common.yaml index 57cdc5de9b..2e238b481c 100644 --- a/tests/components/pca9685/common.yaml +++ b/tests/components/pca9685/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_pca9685 - scl: ${scl_pin} - sda: ${sda_pin} - pca9685: + i2c_id: i2c_bus frequency: 500 address: 0x0 diff --git a/tests/components/pca9685/test.esp32-c3-idf.yaml b/tests/components/pca9685/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/pca9685/test.esp32-c3-idf.yaml +++ b/tests/components/pca9685/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pca9685/test.esp32-idf.yaml b/tests/components/pca9685/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/pca9685/test.esp32-idf.yaml +++ b/tests/components/pca9685/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pca9685/test.esp8266-ard.yaml b/tests/components/pca9685/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/pca9685/test.esp8266-ard.yaml +++ b/tests/components/pca9685/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pca9685/test.rp2040-ard.yaml b/tests/components/pca9685/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/pca9685/test.rp2040-ard.yaml +++ b/tests/components/pca9685/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/pcd8544/common.yaml b/tests/components/pcd8544/common.yaml index 0fb969eeb8..418cd97253 100644 --- a/tests/components/pcd8544/common.yaml +++ b/tests/components/pcd8544/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_pcd8544 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - display: - platform: pcd8544 cs_pin: ${cs_pin} diff --git a/tests/components/pcd8544/test.esp32-c3-idf.yaml b/tests/components/pcd8544/test.esp32-c3-idf.yaml index c5c932c92c..b112cf4c31 100644 --- a/tests/components/pcd8544/test.esp32-c3-idf.yaml +++ b/tests/components/pcd8544/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pcd8544/test.esp32-idf.yaml b/tests/components/pcd8544/test.esp32-idf.yaml index 09e9db5a38..ff174a4656 100644 --- a/tests/components/pcd8544/test.esp32-idf.yaml +++ b/tests/components/pcd8544/test.esp32-idf.yaml @@ -1,9 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO18 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pcd8544/test.esp8266-ard.yaml b/tests/components/pcd8544/test.esp8266-ard.yaml index 3f023a60eb..56cb29f29e 100644 --- a/tests/components/pcd8544/test.esp8266-ard.yaml +++ b/tests/components/pcd8544/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pcd8544/test.rp2040-ard.yaml b/tests/components/pcd8544/test.rp2040-ard.yaml index d7fd6ee294..66caa956f7 100644 --- a/tests/components/pcd8544/test.rp2040-ard.yaml +++ b/tests/components/pcd8544/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pcf85063/common.yaml b/tests/components/pcf85063/common.yaml index f3b68412c5..170029ad85 100644 --- a/tests/components/pcf85063/common.yaml +++ b/tests/components/pcf85063/common.yaml @@ -3,10 +3,6 @@ esphome: - pcf85063.read_time - pcf85063.write_time -i2c: - - id: i2c_pcf85063 - scl: ${scl_pin} - sda: ${sda_pin} - time: - platform: pcf85063 + i2c_id: i2c_bus diff --git a/tests/components/pcf85063/test.esp32-c3-idf.yaml b/tests/components/pcf85063/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/pcf85063/test.esp32-c3-idf.yaml +++ b/tests/components/pcf85063/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pcf85063/test.esp32-idf.yaml b/tests/components/pcf85063/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/pcf85063/test.esp32-idf.yaml +++ b/tests/components/pcf85063/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pcf85063/test.esp8266-ard.yaml b/tests/components/pcf85063/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/pcf85063/test.esp8266-ard.yaml +++ b/tests/components/pcf85063/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pcf85063/test.rp2040-ard.yaml b/tests/components/pcf85063/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/pcf85063/test.rp2040-ard.yaml +++ b/tests/components/pcf85063/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/pcf8563/common.yaml b/tests/components/pcf8563/common.yaml index 30be6b893c..ac5f3afed1 100644 --- a/tests/components/pcf8563/common.yaml +++ b/tests/components/pcf8563/common.yaml @@ -3,10 +3,6 @@ esphome: - pcf8563.read_time - pcf8563.write_time -i2c: - - id: i2c_pcf8563 - scl: ${scl_pin} - sda: ${sda_pin} - time: - platform: pcf8563 + i2c_id: i2c_bus diff --git a/tests/components/pcf8563/test.esp32-c3-idf.yaml b/tests/components/pcf8563/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/pcf8563/test.esp32-c3-idf.yaml +++ b/tests/components/pcf8563/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pcf8563/test.esp32-idf.yaml b/tests/components/pcf8563/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/pcf8563/test.esp32-idf.yaml +++ b/tests/components/pcf8563/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pcf8563/test.esp8266-ard.yaml b/tests/components/pcf8563/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/pcf8563/test.esp8266-ard.yaml +++ b/tests/components/pcf8563/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pcf8563/test.rp2040-ard.yaml b/tests/components/pcf8563/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/pcf8563/test.rp2040-ard.yaml +++ b/tests/components/pcf8563/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/pcf8574/common.yaml b/tests/components/pcf8574/common.yaml index f5fcfad64a..09fa33164e 100644 --- a/tests/components/pcf8574/common.yaml +++ b/tests/components/pcf8574/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_pcf8574 - scl: ${scl_pin} - sda: ${sda_pin} - pcf8574: - id: pcf8574_hub + i2c_id: i2c_bus address: 0x21 pcf8575: false diff --git a/tests/components/pcf8574/test.esp32-c3-idf.yaml b/tests/components/pcf8574/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/pcf8574/test.esp32-c3-idf.yaml +++ b/tests/components/pcf8574/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pcf8574/test.esp32-idf.yaml b/tests/components/pcf8574/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/pcf8574/test.esp32-idf.yaml +++ b/tests/components/pcf8574/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pcf8574/test.esp8266-ard.yaml b/tests/components/pcf8574/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/pcf8574/test.esp8266-ard.yaml +++ b/tests/components/pcf8574/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pcf8574/test.rp2040-ard.yaml b/tests/components/pcf8574/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/pcf8574/test.rp2040-ard.yaml +++ b/tests/components/pcf8574/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/pi4ioe5v6408/common.yaml b/tests/components/pi4ioe5v6408/common.yaml index 4130dc2652..2344622081 100644 --- a/tests/components/pi4ioe5v6408/common.yaml +++ b/tests/components/pi4ioe5v6408/common.yaml @@ -1,9 +1,5 @@ -i2c: - id: i2c_pi4ioe5v6408 - sda: ${i2c_sda} - scl: ${i2c_scl} - pi4ioe5v6408: + i2c_id: i2c_bus id: pi4ioe1 address: 0x44 diff --git a/tests/components/pi4ioe5v6408/test.esp32-idf.yaml b/tests/components/pi4ioe5v6408/test.esp32-idf.yaml index 55e6edfbf3..9a4779d822 100644 --- a/tests/components/pi4ioe5v6408/test.esp32-idf.yaml +++ b/tests/components/pi4ioe5v6408/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: i2c_sda: GPIO21 i2c_scl: GPIO22 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pi4ioe5v6408/test.rp2040-ard.yaml b/tests/components/pi4ioe5v6408/test.rp2040-ard.yaml index b7b6b13bfe..3429a2952a 100644 --- a/tests/components/pi4ioe5v6408/test.rp2040-ard.yaml +++ b/tests/components/pi4ioe5v6408/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: i2c_sda: GPIO4 i2c_scl: GPIO5 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pipsolar/common.yaml b/tests/components/pipsolar/common.yaml index 64e2c0476d..819764a7ea 100644 --- a/tests/components/pipsolar/common.yaml +++ b/tests/components/pipsolar/common.yaml @@ -5,12 +5,6 @@ esphome: id: inverter0_battery_recharge_voltage_out value: 48.0 -uart: - - id: uart_pipsolar - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 115200 - pipsolar: id: inverter0 diff --git a/tests/components/pipsolar/test.esp32-c3-idf.yaml b/tests/components/pipsolar/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/pipsolar/test.esp32-c3-idf.yaml +++ b/tests/components/pipsolar/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pipsolar/test.esp32-idf.yaml b/tests/components/pipsolar/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/pipsolar/test.esp32-idf.yaml +++ b/tests/components/pipsolar/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pipsolar/test.esp32-s2-idf.yaml b/tests/components/pipsolar/test.esp32-s2-idf.yaml index b516342f3b..2d29656c94 100644 --- a/tests/components/pipsolar/test.esp32-s2-idf.yaml +++ b/tests/components/pipsolar/test.esp32-s2-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pipsolar/test.esp8266-ard.yaml b/tests/components/pipsolar/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/pipsolar/test.esp8266-ard.yaml +++ b/tests/components/pipsolar/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pipsolar/test.rp2040-ard.yaml b/tests/components/pipsolar/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/pipsolar/test.rp2040-ard.yaml +++ b/tests/components/pipsolar/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pm1006/common.yaml b/tests/components/pm1006/common.yaml index 4e3e880f4e..43955bb099 100644 --- a/tests/components/pm1006/common.yaml +++ b/tests/components/pm1006/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_pm1006 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: pm1006 pm_2_5: diff --git a/tests/components/pm1006/test.esp32-c3-idf.yaml b/tests/components/pm1006/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/pm1006/test.esp32-c3-idf.yaml +++ b/tests/components/pm1006/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pm1006/test.esp32-idf.yaml b/tests/components/pm1006/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/pm1006/test.esp32-idf.yaml +++ b/tests/components/pm1006/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pm1006/test.esp8266-ard.yaml b/tests/components/pm1006/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/pm1006/test.esp8266-ard.yaml +++ b/tests/components/pm1006/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pm1006/test.rp2040-ard.yaml b/tests/components/pm1006/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/pm1006/test.rp2040-ard.yaml +++ b/tests/components/pm1006/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pm2005/common.yaml b/tests/components/pm2005/common.yaml index b8f6683b22..034752d0b9 100644 --- a/tests/components/pm2005/common.yaml +++ b/tests/components/pm2005/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_pm2005 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: pm2005 + i2c_id: i2c_bus pm_1_0: name: PM1.0 pm_2_5: diff --git a/tests/components/pm2005/test.esp32-c3-idf.yaml b/tests/components/pm2005/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/pm2005/test.esp32-c3-idf.yaml +++ b/tests/components/pm2005/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pm2005/test.esp32-idf.yaml b/tests/components/pm2005/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/pm2005/test.esp32-idf.yaml +++ b/tests/components/pm2005/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pm2005/test.esp8266-ard.yaml b/tests/components/pm2005/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/pm2005/test.esp8266-ard.yaml +++ b/tests/components/pm2005/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pm2005/test.rp2040-ard.yaml b/tests/components/pm2005/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/pm2005/test.rp2040-ard.yaml +++ b/tests/components/pm2005/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/pmsa003i/common.yaml b/tests/components/pmsa003i/common.yaml index 95e62da694..7267bd58f3 100644 --- a/tests/components/pmsa003i/common.yaml +++ b/tests/components/pmsa003i/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_pmsa003i - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: pmsa003i + i2c_id: i2c_bus pm_1_0: name: PMSA003i PM1.0 pm_2_5: diff --git a/tests/components/pmsa003i/test.esp32-c3-idf.yaml b/tests/components/pmsa003i/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/pmsa003i/test.esp32-c3-idf.yaml +++ b/tests/components/pmsa003i/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pmsa003i/test.esp32-idf.yaml b/tests/components/pmsa003i/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/pmsa003i/test.esp32-idf.yaml +++ b/tests/components/pmsa003i/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pmsa003i/test.esp8266-ard.yaml b/tests/components/pmsa003i/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/pmsa003i/test.esp8266-ard.yaml +++ b/tests/components/pmsa003i/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pmsa003i/test.rp2040-ard.yaml b/tests/components/pmsa003i/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/pmsa003i/test.rp2040-ard.yaml +++ b/tests/components/pmsa003i/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/pmsx003/common.yaml b/tests/components/pmsx003/common.yaml index 7b9ca5b091..3c60995804 100644 --- a/tests/components/pmsx003/common.yaml +++ b/tests/components/pmsx003/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_pmsx003 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: pmsx003 type: PMSX003 diff --git a/tests/components/pmsx003/test.esp32-c3-idf.yaml b/tests/components/pmsx003/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/pmsx003/test.esp32-c3-idf.yaml +++ b/tests/components/pmsx003/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pmsx003/test.esp32-idf.yaml b/tests/components/pmsx003/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/pmsx003/test.esp32-idf.yaml +++ b/tests/components/pmsx003/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pmsx003/test.esp8266-ard.yaml b/tests/components/pmsx003/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/pmsx003/test.esp8266-ard.yaml +++ b/tests/components/pmsx003/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pmsx003/test.rp2040-ard.yaml b/tests/components/pmsx003/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/pmsx003/test.rp2040-ard.yaml +++ b/tests/components/pmsx003/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pmwcs3/common.yaml b/tests/components/pmwcs3/common.yaml index dcf59d0b6e..e06400d4d4 100644 --- a/tests/components/pmwcs3/common.yaml +++ b/tests/components/pmwcs3/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_pmwcs3 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: pmwcs3 + i2c_id: i2c_bus e25: name: pmwcs3_e25 ec: diff --git a/tests/components/pmwcs3/test.esp32-c3-idf.yaml b/tests/components/pmwcs3/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/pmwcs3/test.esp32-c3-idf.yaml +++ b/tests/components/pmwcs3/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pmwcs3/test.esp32-idf.yaml b/tests/components/pmwcs3/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/pmwcs3/test.esp32-idf.yaml +++ b/tests/components/pmwcs3/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pmwcs3/test.esp8266-ard.yaml b/tests/components/pmwcs3/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/pmwcs3/test.esp8266-ard.yaml +++ b/tests/components/pmwcs3/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pmwcs3/test.rp2040-ard.yaml b/tests/components/pmwcs3/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/pmwcs3/test.rp2040-ard.yaml +++ b/tests/components/pmwcs3/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/pn532_i2c/common.yaml b/tests/components/pn532_i2c/common.yaml index db9abd4b13..f328cd40ee 100644 --- a/tests/components/pn532_i2c/common.yaml +++ b/tests/components/pn532_i2c/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_pn532 - scl: ${scl_pin} - sda: ${sda_pin} - pn532_i2c: + i2c_id: i2c_bus id: pn532_nfcc binary_sensor: diff --git a/tests/components/pn532_i2c/test.esp32-c3-idf.yaml b/tests/components/pn532_i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/pn532_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/pn532_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.esp32-idf.yaml b/tests/components/pn532_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/pn532_i2c/test.esp32-idf.yaml +++ b/tests/components/pn532_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.esp8266-ard.yaml b/tests/components/pn532_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/pn532_i2c/test.esp8266-ard.yaml +++ b/tests/components/pn532_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.rp2040-ard.yaml b/tests/components/pn532_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/pn532_i2c/test.rp2040-ard.yaml +++ b/tests/components/pn532_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/pn532_spi/common.yaml b/tests/components/pn532_spi/common.yaml index d5b8bc405e..e749a9896a 100644 --- a/tests/components/pn532_spi/common.yaml +++ b/tests/components/pn532_spi/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_pn532 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - pn532_spi: id: pn532_nfcc cs_pin: ${cs_pin} diff --git a/tests/components/pn532_spi/test.esp32-c3-idf.yaml b/tests/components/pn532_spi/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/pn532_spi/test.esp32-c3-idf.yaml +++ b/tests/components/pn532_spi/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pn532_spi/test.esp32-idf.yaml b/tests/components/pn532_spi/test.esp32-idf.yaml index bce56f398a..9bb524aa65 100644 --- a/tests/components/pn532_spi/test.esp32-idf.yaml +++ b/tests/components/pn532_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO18 cs_pin: GPIO12 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pn532_spi/test.esp8266-ard.yaml b/tests/components/pn532_spi/test.esp8266-ard.yaml index bd5c203e35..1aac800592 100644 --- a/tests/components/pn532_spi/test.esp8266-ard.yaml +++ b/tests/components/pn532_spi/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 - cs_pin: GPIO5 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO15 + cs_pin: GPIO16 + +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pn532_spi/test.rp2040-ard.yaml b/tests/components/pn532_spi/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/pn532_spi/test.rp2040-ard.yaml +++ b/tests/components/pn532_spi/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pn7150_i2c/common.yaml b/tests/components/pn7150_i2c/common.yaml index ef852b7a78..a317b72b9c 100644 --- a/tests/components/pn7150_i2c/common.yaml +++ b/tests/components/pn7150_i2c/common.yaml @@ -16,13 +16,9 @@ esphome: - tag.polling_off: nfcc_pn7150 - tag.polling_on: nfcc_pn7150 -i2c: - - id: i2c_pn7150 - scl: ${scl_pin} - sda: ${sda_pin} - pn7150_i2c: id: nfcc_pn7150 + i2c_id: i2c_bus irq_pin: ${irq_pin} ven_pin: ${ven_pin} emulation_message: https://www.home-assistant.io/tag/pulse_ce diff --git a/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml b/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml index 2067143411..cdf8445263 100644 --- a/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO6 ven_pin: GPIO7 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.esp32-idf.yaml b/tests/components/pn7150_i2c/test.esp32-idf.yaml index 1643bec317..9a4cc6669e 100644 --- a/tests/components/pn7150_i2c/test.esp32-idf.yaml +++ b/tests/components/pn7150_i2c/test.esp32-idf.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 irq_pin: GPIO14 ven_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.esp8266-ard.yaml b/tests/components/pn7150_i2c/test.esp8266-ard.yaml index 7111fc9e00..b907e56f80 100644 --- a/tests/components/pn7150_i2c/test.esp8266-ard.yaml +++ b/tests/components/pn7150_i2c/test.esp8266-ard.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - irq_pin: GPIO12 - ven_pin: GPIO13 + irq_pin: GPIO15 + ven_pin: GPIO16 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.rp2040-ard.yaml b/tests/components/pn7150_i2c/test.rp2040-ard.yaml index 2067143411..b320b947e3 100644 --- a/tests/components/pn7150_i2c/test.rp2040-ard.yaml +++ b/tests/components/pn7150_i2c/test.rp2040-ard.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO6 ven_pin: GPIO7 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pn7160_i2c/common.yaml b/tests/components/pn7160_i2c/common.yaml index 0a7c9bd6bb..9807bff0f0 100644 --- a/tests/components/pn7160_i2c/common.yaml +++ b/tests/components/pn7160_i2c/common.yaml @@ -16,13 +16,9 @@ esphome: - tag.polling_off: nfcc_pn7160 - tag.polling_on: nfcc_pn7160 -i2c: - - id: i2c_pn7160 - scl: ${scl_pin} - sda: ${sda_pin} - pn7150_i2c: id: nfcc_pn7160 + i2c_id: i2c_bus irq_pin: ${irq_pin} ven_pin: ${ven_pin} emulation_message: https://www.home-assistant.io/tag/pulse_ce diff --git a/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml b/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml index 2067143411..cdf8445263 100644 --- a/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO6 ven_pin: GPIO7 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.esp32-idf.yaml b/tests/components/pn7160_i2c/test.esp32-idf.yaml index 1643bec317..9a4cc6669e 100644 --- a/tests/components/pn7160_i2c/test.esp32-idf.yaml +++ b/tests/components/pn7160_i2c/test.esp32-idf.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 irq_pin: GPIO14 ven_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.esp8266-ard.yaml b/tests/components/pn7160_i2c/test.esp8266-ard.yaml index 7111fc9e00..b907e56f80 100644 --- a/tests/components/pn7160_i2c/test.esp8266-ard.yaml +++ b/tests/components/pn7160_i2c/test.esp8266-ard.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - irq_pin: GPIO12 - ven_pin: GPIO13 + irq_pin: GPIO15 + ven_pin: GPIO16 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.rp2040-ard.yaml b/tests/components/pn7160_i2c/test.rp2040-ard.yaml index 2067143411..b320b947e3 100644 --- a/tests/components/pn7160_i2c/test.rp2040-ard.yaml +++ b/tests/components/pn7160_i2c/test.rp2040-ard.yaml @@ -1,7 +1,8 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 irq_pin: GPIO6 ven_pin: GPIO7 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pn7160_spi/common.yaml b/tests/components/pn7160_spi/common.yaml index 9e8d22f835..d467eb093f 100644 --- a/tests/components/pn7160_spi/common.yaml +++ b/tests/components/pn7160_spi/common.yaml @@ -16,12 +16,6 @@ esphome: - tag.polling_off: nfcc_pn7160 - tag.polling_on: nfcc_pn7160 -spi: - - id: spi_pn7160 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - pn7160_spi: id: nfcc_pn7160 cs_pin: ${cs_pin} diff --git a/tests/components/pn7160_spi/test.esp32-c3-idf.yaml b/tests/components/pn7160_spi/test.esp32-c3-idf.yaml index f8a07fad2f..ac18bfff5c 100644 --- a/tests/components/pn7160_spi/test.esp32-c3-idf.yaml +++ b/tests/components/pn7160_spi/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 irq_pin: GPIO9 ven_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.esp32-idf.yaml b/tests/components/pn7160_spi/test.esp32-idf.yaml index f6073d0416..f903e4b7be 100644 --- a/tests/components/pn7160_spi/test.esp32-idf.yaml +++ b/tests/components/pn7160_spi/test.esp32-idf.yaml @@ -1,9 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO18 cs_pin: GPIO12 irq_pin: GPIO13 ven_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.esp8266-ard.yaml b/tests/components/pn7160_spi/test.esp8266-ard.yaml index cbe27533a7..7ec89dc012 100644 --- a/tests/components/pn7160_spi/test.esp8266-ard.yaml +++ b/tests/components/pn7160_spi/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 irq_pin: GPIO15 ven_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.rp2040-ard.yaml b/tests/components/pn7160_spi/test.rp2040-ard.yaml index 70cd2425fa..b4a4b436cd 100644 --- a/tests/components/pn7160_spi/test.rp2040-ard.yaml +++ b/tests/components/pn7160_spi/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: irq_pin: GPIO15 ven_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/prometheus/common.yaml b/tests/components/prometheus/common.yaml index 131d135f8b..9a16088ba0 100644 --- a/tests/components/prometheus/common.yaml +++ b/tests/components/prometheus/common.yaml @@ -21,10 +21,12 @@ http_request: ota: - platform: http_request + id: prometheus_http_request_ota update: - platform: http_request name: Firmware Update + ota_id: prometheus_http_request_ota source: http://example.com/manifest.json sensor: diff --git a/tests/components/prometheus/test.esp32-idf.yaml b/tests/components/prometheus/test.esp32-idf.yaml index f00bca5947..d60caadb05 100644 --- a/tests/components/prometheus/test.esp32-idf.yaml +++ b/tests/components/prometheus/test.esp32-idf.yaml @@ -2,4 +2,7 @@ substitutions: verify_ssl: "false" pin: GPIO2 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pvvx_mithermometer/test.esp32-c3-idf.yaml b/tests/components/pvvx_mithermometer/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/pvvx_mithermometer/test.esp32-c3-idf.yaml +++ b/tests/components/pvvx_mithermometer/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pvvx_mithermometer/test.esp32-idf.yaml b/tests/components/pvvx_mithermometer/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/pvvx_mithermometer/test.esp32-idf.yaml +++ b/tests/components/pvvx_mithermometer/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/pylontech/common.yaml b/tests/components/pylontech/common.yaml index 6852685be7..537450a3b6 100644 --- a/tests/components/pylontech/common.yaml +++ b/tests/components/pylontech/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_pylontech0 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 115200 - pylontech: - id: pylontech0 - id: pylontech1 diff --git a/tests/components/pylontech/test.esp32-c3-idf.yaml b/tests/components/pylontech/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/pylontech/test.esp32-c3-idf.yaml +++ b/tests/components/pylontech/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pylontech/test.esp32-idf.yaml b/tests/components/pylontech/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/pylontech/test.esp32-idf.yaml +++ b/tests/components/pylontech/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pylontech/test.esp8266-ard.yaml b/tests/components/pylontech/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/pylontech/test.esp8266-ard.yaml +++ b/tests/components/pylontech/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pylontech/test.rp2040-ard.yaml b/tests/components/pylontech/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/pylontech/test.rp2040-ard.yaml +++ b/tests/components/pylontech/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pzem004t/common.yaml b/tests/components/pzem004t/common.yaml index 75f7f30fc9..4584f9c273 100644 --- a/tests/components/pzem004t/common.yaml +++ b/tests/components/pzem004t/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_pzem004t - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 115200 - sensor: - platform: pzem004t voltage: diff --git a/tests/components/pzem004t/test.esp32-c3-idf.yaml b/tests/components/pzem004t/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/pzem004t/test.esp32-c3-idf.yaml +++ b/tests/components/pzem004t/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pzem004t/test.esp32-idf.yaml b/tests/components/pzem004t/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/pzem004t/test.esp32-idf.yaml +++ b/tests/components/pzem004t/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pzem004t/test.esp8266-ard.yaml b/tests/components/pzem004t/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/pzem004t/test.esp8266-ard.yaml +++ b/tests/components/pzem004t/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pzem004t/test.rp2040-ard.yaml b/tests/components/pzem004t/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/pzem004t/test.rp2040-ard.yaml +++ b/tests/components/pzem004t/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pzemac/common.yaml b/tests/components/pzemac/common.yaml index e50f4ad2f2..2566051baa 100644 --- a/tests/components/pzemac/common.yaml +++ b/tests/components/pzemac/common.yaml @@ -3,16 +3,9 @@ esphome: then: - pzemac.reset_energy: pzemac1 -uart: - - id: uart_pzemac - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - -modbus: - sensor: - platform: pzemac + modbus_id: modbus_bus id: pzemac1 voltage: name: PZEMAC Voltage diff --git a/tests/components/pzemac/test.esp32-c3-idf.yaml b/tests/components/pzemac/test.esp32-c3-idf.yaml index b516342f3b..6c6e95488f 100644 --- a/tests/components/pzemac/test.esp32-c3-idf.yaml +++ b/tests/components/pzemac/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pzemac/test.esp32-idf.yaml b/tests/components/pzemac/test.esp32-idf.yaml index f486544afa..37d98696cc 100644 --- a/tests/components/pzemac/test.esp32-idf.yaml +++ b/tests/components/pzemac/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pzemac/test.esp8266-ard.yaml b/tests/components/pzemac/test.esp8266-ard.yaml index b516342f3b..421389ae97 100644 --- a/tests/components/pzemac/test.esp8266-ard.yaml +++ b/tests/components/pzemac/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pzemac/test.rp2040-ard.yaml b/tests/components/pzemac/test.rp2040-ard.yaml index b516342f3b..d78d84c983 100644 --- a/tests/components/pzemac/test.rp2040-ard.yaml +++ b/tests/components/pzemac/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + modbus: !include ../../test_build_components/common/modbus/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/pzemdc/common.yaml b/tests/components/pzemdc/common.yaml index db1868d682..78a0ab0d07 100644 --- a/tests/components/pzemdc/common.yaml +++ b/tests/components/pzemdc/common.yaml @@ -3,15 +3,9 @@ esphome: then: - pzemdc.reset_energy: pzemdc1 -uart: - - id: uart_pzemdc - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - stop_bits: 2 - sensor: - platform: pzemdc + modbus_id: modbus_bus id: pzemdc1 voltage: name: PZEMDC Voltage diff --git a/tests/components/pzemdc/test.esp32-c3-idf.yaml b/tests/components/pzemdc/test.esp32-c3-idf.yaml index b516342f3b..6c6e95488f 100644 --- a/tests/components/pzemdc/test.esp32-c3-idf.yaml +++ b/tests/components/pzemdc/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/pzemdc/test.esp32-idf.yaml b/tests/components/pzemdc/test.esp32-idf.yaml index f486544afa..37d98696cc 100644 --- a/tests/components/pzemdc/test.esp32-idf.yaml +++ b/tests/components/pzemdc/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/pzemdc/test.esp8266-ard.yaml b/tests/components/pzemdc/test.esp8266-ard.yaml index b516342f3b..421389ae97 100644 --- a/tests/components/pzemdc/test.esp8266-ard.yaml +++ b/tests/components/pzemdc/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/pzemdc/test.rp2040-ard.yaml b/tests/components/pzemdc/test.rp2040-ard.yaml index b516342f3b..d78d84c983 100644 --- a/tests/components/pzemdc/test.rp2040-ard.yaml +++ b/tests/components/pzemdc/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + modbus: !include ../../test_build_components/common/modbus/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/qmc5883l/common.yaml b/tests/components/qmc5883l/common.yaml index c8ad4ba006..98d0350a60 100644 --- a/tests/components/qmc5883l/common.yaml +++ b/tests/components/qmc5883l/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_qmc5883l - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: qmc5883l + i2c_id: i2c_bus address: 0x0D field_strength_x: name: QMC5883L Field Strength X diff --git a/tests/components/qmc5883l/test.esp32-c3-idf.yaml b/tests/components/qmc5883l/test.esp32-c3-idf.yaml index 677501d15a..854ddc25e7 100644 --- a/tests/components/qmc5883l/test.esp32-c3-idf.yaml +++ b/tests/components/qmc5883l/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 drdy_pin: GPIO6 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/qmc5883l/test.esp32-idf.yaml b/tests/components/qmc5883l/test.esp32-idf.yaml index 2cf2041501..07bf2b14e2 100644 --- a/tests/components/qmc5883l/test.esp32-idf.yaml +++ b/tests/components/qmc5883l/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 - drdy_pin: GPIO18 + drdy_pin: GPIO12 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/qmc5883l/test.esp8266-ard.yaml b/tests/components/qmc5883l/test.esp8266-ard.yaml index 65b0fd75d9..0c02b3b96a 100644 --- a/tests/components/qmc5883l/test.esp8266-ard.yaml +++ b/tests/components/qmc5883l/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 drdy_pin: GPIO2 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/qmc5883l/test.rp2040-ard.yaml b/tests/components/qmc5883l/test.rp2040-ard.yaml index 65b0fd75d9..3b10540b1f 100644 --- a/tests/components/qmc5883l/test.rp2040-ard.yaml +++ b/tests/components/qmc5883l/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 drdy_pin: GPIO2 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/qmp6988/common.yaml b/tests/components/qmp6988/common.yaml index cb4b221df0..2aea228a0d 100644 --- a/tests/components/qmp6988/common.yaml +++ b/tests/components/qmp6988/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_qmp6988 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: qmp6988 + i2c_id: i2c_bus temperature: name: QMP6988 Temperature oversampling: 32x diff --git a/tests/components/qmp6988/test.esp32-c3-idf.yaml b/tests/components/qmp6988/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/qmp6988/test.esp32-c3-idf.yaml +++ b/tests/components/qmp6988/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/qmp6988/test.esp32-idf.yaml b/tests/components/qmp6988/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/qmp6988/test.esp32-idf.yaml +++ b/tests/components/qmp6988/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/qmp6988/test.esp8266-ard.yaml b/tests/components/qmp6988/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/qmp6988/test.esp8266-ard.yaml +++ b/tests/components/qmp6988/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/qmp6988/test.rp2040-ard.yaml b/tests/components/qmp6988/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/qmp6988/test.rp2040-ard.yaml +++ b/tests/components/qmp6988/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/qr_code/common.yaml b/tests/components/qr_code/common.yaml index 85c2ee076b..5fec26c1cc 100644 --- a/tests/components/qr_code/common.yaml +++ b/tests/components/qr_code/common.yaml @@ -1,11 +1,6 @@ -spi: - - id: spi_main_lcd - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: ili9xxx - id: main_lcd + id: qr_code_main_lcd model: ili9342 cs_pin: ${cs_pin} dc_pin: ${dc_pin} @@ -14,11 +9,11 @@ display: lambda: |- // Draw a QR code in the center of the screen auto scale = 2; - auto size = id(homepage_qr).get_size() * scale; + auto size = id(qr_code_homepage_qr).get_size() * scale; auto x = (it.get_width() / 2) - (size / 2); auto y = (it.get_height() / 2) - (size / 2); - it.qr_code(x, y, id(homepage_qr), Color(255,255,255), scale); + it.qr_code(x, y, id(qr_code_homepage_qr), Color(255,255,255), scale); qr_code: - - id: homepage_qr + - id: qr_code_homepage_qr value: https://esphome.io/index.html diff --git a/tests/components/qr_code/test.esp32-c3-idf.yaml b/tests/components/qr_code/test.esp32-c3-idf.yaml index c5c932c92c..b112cf4c31 100644 --- a/tests/components/qr_code/test.esp32-c3-idf.yaml +++ b/tests/components/qr_code/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/qr_code/test.esp32-idf.yaml b/tests/components/qr_code/test.esp32-idf.yaml index bad5241f79..ff174a4656 100644 --- a/tests/components/qr_code/test.esp32-idf.yaml +++ b/tests/components/qr_code/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/qr_code/test.esp8266-ard.yaml b/tests/components/qr_code/test.esp8266-ard.yaml index 3f023a60eb..56cb29f29e 100644 --- a/tests/components/qr_code/test.esp8266-ard.yaml +++ b/tests/components/qr_code/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/qr_code/test.rp2040-ard.yaml b/tests/components/qr_code/test.rp2040-ard.yaml index d7fd6ee294..66caa956f7 100644 --- a/tests/components/qr_code/test.rp2040-ard.yaml +++ b/tests/components/qr_code/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/qspi_dbi/common.yaml b/tests/components/qspi_dbi/common.yaml index f4b15f2b90..109db65b63 100644 --- a/tests/components/qspi_dbi/common.yaml +++ b/tests/components/qspi_dbi/common.yaml @@ -1,9 +1,3 @@ -spi: - id: quad_spi - clk_pin: 15 - type: quad - data_pins: [14, 10, 16, 12] - display: - platform: qspi_dbi model: RM690B0 diff --git a/tests/components/qspi_dbi/test.esp32-s3-idf.yaml b/tests/components/qspi_dbi/test.esp32-s3-idf.yaml index dade44d145..c335dee1f3 100644 --- a/tests/components/qspi_dbi/test.esp32-s3-idf.yaml +++ b/tests/components/qspi_dbi/test.esp32-s3-idf.yaml @@ -1 +1,4 @@ +packages: + qspi: !include ../../test_build_components/common/qspi/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/qwiic_pir/common.yaml b/tests/components/qwiic_pir/common.yaml index d4b733405d..30418ff6b9 100644 --- a/tests/components/qwiic_pir/common.yaml +++ b/tests/components/qwiic_pir/common.yaml @@ -1,8 +1,4 @@ -i2c: - - id: i2c_qwiic_pir - scl: ${scl_pin} - sda: ${sda_pin} - binary_sensor: - platform: qwiic_pir + i2c_id: i2c_bus name: Qwiic PIR Motion Sensor diff --git a/tests/components/qwiic_pir/test.esp32-c3-idf.yaml b/tests/components/qwiic_pir/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/qwiic_pir/test.esp32-c3-idf.yaml +++ b/tests/components/qwiic_pir/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.esp32-idf.yaml b/tests/components/qwiic_pir/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/qwiic_pir/test.esp32-idf.yaml +++ b/tests/components/qwiic_pir/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.esp8266-ard.yaml b/tests/components/qwiic_pir/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/qwiic_pir/test.esp8266-ard.yaml +++ b/tests/components/qwiic_pir/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.rp2040-ard.yaml b/tests/components/qwiic_pir/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/qwiic_pir/test.rp2040-ard.yaml +++ b/tests/components/qwiic_pir/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/radon_eye_ble/test.esp32-c3-idf.yaml b/tests/components/radon_eye_ble/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/radon_eye_ble/test.esp32-c3-idf.yaml +++ b/tests/components/radon_eye_ble/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/radon_eye_ble/test.esp32-idf.yaml b/tests/components/radon_eye_ble/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/radon_eye_ble/test.esp32-idf.yaml +++ b/tests/components/radon_eye_ble/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/radon_eye_rd200/test.esp32-c3-idf.yaml b/tests/components/radon_eye_rd200/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/radon_eye_rd200/test.esp32-c3-idf.yaml +++ b/tests/components/radon_eye_rd200/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/radon_eye_rd200/test.esp32-idf.yaml b/tests/components/radon_eye_rd200/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/radon_eye_rd200/test.esp32-idf.yaml +++ b/tests/components/radon_eye_rd200/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/rc522_i2c/common.yaml b/tests/components/rc522_i2c/common.yaml index b8b7a41bc7..65b92a3e78 100644 --- a/tests/components/rc522_i2c/common.yaml +++ b/tests/components/rc522_i2c/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_rc522 - scl: ${scl_pin} - sda: ${sda_pin} - rc522_i2c: - id: rc522_nfcc + i2c_id: i2c_bus update_interval: 1s on_tag: - lambda: |- diff --git a/tests/components/rc522_i2c/test.esp32-c3-idf.yaml b/tests/components/rc522_i2c/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/rc522_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/rc522_i2c/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.esp32-idf.yaml b/tests/components/rc522_i2c/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/rc522_i2c/test.esp32-idf.yaml +++ b/tests/components/rc522_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.esp8266-ard.yaml b/tests/components/rc522_i2c/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/rc522_i2c/test.esp8266-ard.yaml +++ b/tests/components/rc522_i2c/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.rp2040-ard.yaml b/tests/components/rc522_i2c/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/rc522_i2c/test.rp2040-ard.yaml +++ b/tests/components/rc522_i2c/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/rc522_spi/common.yaml b/tests/components/rc522_spi/common.yaml index 5c42858993..4ce1d6584b 100644 --- a/tests/components/rc522_spi/common.yaml +++ b/tests/components/rc522_spi/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_rc522 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - rc522_spi: id: rc522_nfcc cs_pin: ${cs_pin} diff --git a/tests/components/rc522_spi/test.esp32-c3-idf.yaml b/tests/components/rc522_spi/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/rc522_spi/test.esp32-c3-idf.yaml +++ b/tests/components/rc522_spi/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/rc522_spi/test.esp32-idf.yaml b/tests/components/rc522_spi/test.esp32-idf.yaml index 54e027a614..a3352cf880 100644 --- a/tests/components/rc522_spi/test.esp32-idf.yaml +++ b/tests/components/rc522_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/rc522_spi/test.esp8266-ard.yaml b/tests/components/rc522_spi/test.esp8266-ard.yaml index dbd158d030..b4673ba8b7 100644 --- a/tests/components/rc522_spi/test.esp8266-ard.yaml +++ b/tests/components/rc522_spi/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO16 cs_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/rc522_spi/test.rp2040-ard.yaml b/tests/components/rc522_spi/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/rc522_spi/test.rp2040-ard.yaml +++ b/tests/components/rc522_spi/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/rdm6300/common.yaml b/tests/components/rdm6300/common.yaml index 118a295471..f1a5305013 100644 --- a/tests/components/rdm6300/common.yaml +++ b/tests/components/rdm6300/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_rdm6300 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 115200 - rdm6300: binary_sensor: diff --git a/tests/components/rdm6300/test.esp32-c3-idf.yaml b/tests/components/rdm6300/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/rdm6300/test.esp32-c3-idf.yaml +++ b/tests/components/rdm6300/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/rdm6300/test.esp32-idf.yaml b/tests/components/rdm6300/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/rdm6300/test.esp32-idf.yaml +++ b/tests/components/rdm6300/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/rdm6300/test.esp8266-ard.yaml b/tests/components/rdm6300/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/rdm6300/test.esp8266-ard.yaml +++ b/tests/components/rdm6300/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/rdm6300/test.rp2040-ard.yaml b/tests/components/rdm6300/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/rdm6300/test.rp2040-ard.yaml +++ b/tests/components/rdm6300/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/remote_receiver/test.esp32-s3-idf.yaml b/tests/components/remote_receiver/test.esp32-s3-idf.yaml index cdae8b1e4e..0d7a20e0c0 100644 --- a/tests/components/remote_receiver/test.esp32-s3-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-s3-idf.yaml @@ -5,9 +5,22 @@ substitutions: receive_symbols: "4" rmt_symbols: "64" -packages: - common: !include esp32-common.yaml - +# WARNING: Using !extend or !remove prevents automatic component grouping in CI, making builds slower. remote_receiver: - - id: !extend rcvr + - id: rcvr + pin: ${pin} + dump: all + tolerance: 25% + clock_resolution: ${clock_resolution} + filter_symbols: ${filter_symbols} + receive_symbols: ${receive_symbols} + rmt_symbols: ${rmt_symbols} use_dma: "true" + <<: !include common-actions.yaml + +binary_sensor: + - platform: remote_receiver + name: Panasonic Remote Input + panasonic: + address: 0x4004 + command: 0x100BCBD diff --git a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml index fe4c46d9e7..8a038beb4a 100644 --- a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml @@ -3,9 +3,14 @@ substitutions: clock_resolution: "2000000" rmt_symbols: "64" -packages: - common: !include esp32-common.yaml - +# WARNING: Using !extend or !remove prevents automatic component grouping in CI, making builds slower. remote_transmitter: - - id: !extend xmitr + - id: xmitr + pin: ${pin} + carrier_duty_percent: 50% + clock_resolution: ${clock_resolution} + rmt_symbols: ${rmt_symbols} use_dma: "true" + +packages: + buttons: !include common-buttons.yaml diff --git a/tests/components/resampler/test.esp32-idf.yaml b/tests/components/resampler/test.esp32-idf.yaml index 96d2d37458..6712f1e468 100644 --- a/tests/components/resampler/test.esp32-idf.yaml +++ b/tests/components/resampler/test.esp32-idf.yaml @@ -1,7 +1,10 @@ substitutions: - lrclk_pin: GPIO16 - bclk_pin: GPIO17 + lrclk_pin: GPIO4 + bclk_pin: GPIO5 mclk_pin: GPIO15 dout_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/rf_bridge/common.yaml b/tests/components/rf_bridge/common.yaml index eaadc4bb9c..427c3d783d 100644 --- a/tests/components/rf_bridge/common.yaml +++ b/tests/components/rf_bridge/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_rf_bridge - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 115200 - rf_bridge: on_code_received: - lambda: |- diff --git a/tests/components/rf_bridge/test.esp32-c3-idf.yaml b/tests/components/rf_bridge/test.esp32-c3-idf.yaml index b516342f3b..a19013bf54 100644 --- a/tests/components/rf_bridge/test.esp32-c3-idf.yaml +++ b/tests/components/rf_bridge/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/rf_bridge/test.esp32-idf.yaml b/tests/components/rf_bridge/test.esp32-idf.yaml index f486544afa..2d29656c94 100644 --- a/tests/components/rf_bridge/test.esp32-idf.yaml +++ b/tests/components/rf_bridge/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/rf_bridge/test.esp8266-ard.yaml b/tests/components/rf_bridge/test.esp8266-ard.yaml index b516342f3b..5a05efa259 100644 --- a/tests/components/rf_bridge/test.esp8266-ard.yaml +++ b/tests/components/rf_bridge/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/rf_bridge/test.rp2040-ard.yaml b/tests/components/rf_bridge/test.rp2040-ard.yaml index b516342f3b..f1df2daf83 100644 --- a/tests/components/rf_bridge/test.rp2040-ard.yaml +++ b/tests/components/rf_bridge/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ruuvi_ble/test.esp32-c3-idf.yaml b/tests/components/ruuvi_ble/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/ruuvi_ble/test.esp32-c3-idf.yaml +++ b/tests/components/ruuvi_ble/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ruuvi_ble/test.esp32-idf.yaml b/tests/components/ruuvi_ble/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/ruuvi_ble/test.esp32-idf.yaml +++ b/tests/components/ruuvi_ble/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ruuvitag/test.esp32-c3-idf.yaml b/tests/components/ruuvitag/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/ruuvitag/test.esp32-c3-idf.yaml +++ b/tests/components/ruuvitag/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ruuvitag/test.esp32-idf.yaml b/tests/components/ruuvitag/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/ruuvitag/test.esp32-idf.yaml +++ b/tests/components/ruuvitag/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/scd30/common.yaml b/tests/components/scd30/common.yaml index 1c45c67af0..f21d8944dc 100644 --- a/tests/components/scd30/common.yaml +++ b/tests/components/scd30/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_scd30 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: scd30 + i2c_id: i2c_bus co2: name: SCD30 CO2 temperature: diff --git a/tests/components/scd30/test.esp32-c3-idf.yaml b/tests/components/scd30/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/scd30/test.esp32-c3-idf.yaml +++ b/tests/components/scd30/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/scd30/test.esp32-idf.yaml b/tests/components/scd30/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/scd30/test.esp32-idf.yaml +++ b/tests/components/scd30/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/scd30/test.esp8266-ard.yaml b/tests/components/scd30/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/scd30/test.esp8266-ard.yaml +++ b/tests/components/scd30/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/scd30/test.rp2040-ard.yaml b/tests/components/scd30/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/scd30/test.rp2040-ard.yaml +++ b/tests/components/scd30/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/scd4x/common.yaml b/tests/components/scd4x/common.yaml index dfd35e57de..cedb88977e 100644 --- a/tests/components/scd4x/common.yaml +++ b/tests/components/scd4x/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_scd4x - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: scd4x + i2c_id: i2c_bus id: scd40 co2: name: SCD4X CO2 diff --git a/tests/components/scd4x/test.esp32-c3-idf.yaml b/tests/components/scd4x/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/scd4x/test.esp32-c3-idf.yaml +++ b/tests/components/scd4x/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/scd4x/test.esp32-idf.yaml b/tests/components/scd4x/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/scd4x/test.esp32-idf.yaml +++ b/tests/components/scd4x/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/scd4x/test.esp8266-ard.yaml b/tests/components/scd4x/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/scd4x/test.esp8266-ard.yaml +++ b/tests/components/scd4x/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/scd4x/test.rp2040-ard.yaml b/tests/components/scd4x/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/scd4x/test.rp2040-ard.yaml +++ b/tests/components/scd4x/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sdm_meter/common.yaml b/tests/components/sdm_meter/common.yaml index 60c71a796b..760f134451 100644 --- a/tests/components/sdm_meter/common.yaml +++ b/tests/components/sdm_meter/common.yaml @@ -1,11 +1,6 @@ -uart: - - id: uart_sdm_meter - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: sdm_meter + modbus_id: modbus_bus phase_a: current: name: Phase A Current diff --git a/tests/components/sdm_meter/test.esp32-c3-idf.yaml b/tests/components/sdm_meter/test.esp32-c3-idf.yaml index b516342f3b..6c6e95488f 100644 --- a/tests/components/sdm_meter/test.esp32-c3-idf.yaml +++ b/tests/components/sdm_meter/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sdm_meter/test.esp32-idf.yaml b/tests/components/sdm_meter/test.esp32-idf.yaml index f486544afa..37d98696cc 100644 --- a/tests/components/sdm_meter/test.esp32-idf.yaml +++ b/tests/components/sdm_meter/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sdm_meter/test.esp8266-ard.yaml b/tests/components/sdm_meter/test.esp8266-ard.yaml index b516342f3b..421389ae97 100644 --- a/tests/components/sdm_meter/test.esp8266-ard.yaml +++ b/tests/components/sdm_meter/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sdm_meter/test.rp2040-ard.yaml b/tests/components/sdm_meter/test.rp2040-ard.yaml index b516342f3b..d78d84c983 100644 --- a/tests/components/sdm_meter/test.rp2040-ard.yaml +++ b/tests/components/sdm_meter/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + modbus: !include ../../test_build_components/common/modbus/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sdp3x/common.yaml b/tests/components/sdp3x/common.yaml index d3c5491ca5..5d06f8eddb 100644 --- a/tests/components/sdp3x/common.yaml +++ b/tests/components/sdp3x/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sdp3x - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sdp3x + i2c_id: i2c_bus id: filter_pressure name: HVAC Filter Pressure drop accuracy_decimals: 3 diff --git a/tests/components/sdp3x/test.esp32-c3-idf.yaml b/tests/components/sdp3x/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sdp3x/test.esp32-c3-idf.yaml +++ b/tests/components/sdp3x/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sdp3x/test.esp32-idf.yaml b/tests/components/sdp3x/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sdp3x/test.esp32-idf.yaml +++ b/tests/components/sdp3x/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sdp3x/test.esp8266-ard.yaml b/tests/components/sdp3x/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sdp3x/test.esp8266-ard.yaml +++ b/tests/components/sdp3x/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sdp3x/test.rp2040-ard.yaml b/tests/components/sdp3x/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sdp3x/test.rp2040-ard.yaml +++ b/tests/components/sdp3x/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sds011/common.yaml b/tests/components/sds011/common.yaml index c7574e1d7d..abae0f9bd8 100644 --- a/tests/components/sds011/common.yaml +++ b/tests/components/sds011/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_sdm_sds011 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 115200 - sensor: - platform: sds011 pm_2_5: diff --git a/tests/components/sds011/test.esp32-c3-idf.yaml b/tests/components/sds011/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/sds011/test.esp32-c3-idf.yaml +++ b/tests/components/sds011/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sds011/test.esp32-idf.yaml b/tests/components/sds011/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/sds011/test.esp32-idf.yaml +++ b/tests/components/sds011/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sds011/test.esp8266-ard.yaml b/tests/components/sds011/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/sds011/test.esp8266-ard.yaml +++ b/tests/components/sds011/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sds011/test.rp2040-ard.yaml b/tests/components/sds011/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/sds011/test.rp2040-ard.yaml +++ b/tests/components/sds011/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/seeed_mr24hpc1/common.yaml b/tests/components/seeed_mr24hpc1/common.yaml index 38692b3e5e..3a52888c5c 100644 --- a/tests/components/seeed_mr24hpc1/common.yaml +++ b/tests/components/seeed_mr24hpc1/common.yaml @@ -1,14 +1,5 @@ -uart: - - id: seeed_mr24hpc1_uart - tx_pin: ${uart_tx_pin} - rx_pin: ${uart_rx_pin} - baud_rate: 115200 - parity: NONE - stop_bits: 1 - seeed_mr24hpc1: id: my_seeed_mr24hpc1 - uart_id: seeed_mr24hpc1_uart sensor: - platform: seeed_mr24hpc1 diff --git a/tests/components/seeed_mr24hpc1/test.esp32-c3-idf.yaml b/tests/components/seeed_mr24hpc1/test.esp32-c3-idf.yaml index 4fb884abf4..a19013bf54 100644 --- a/tests/components/seeed_mr24hpc1/test.esp32-c3-idf.yaml +++ b/tests/components/seeed_mr24hpc1/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - uart_tx_pin: GPIO5 - uart_rx_pin: GPIO4 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/seeed_mr60bha2/common.yaml b/tests/components/seeed_mr60bha2/common.yaml index 9eb0c8d527..7d1a02b3ea 100644 --- a/tests/components/seeed_mr60bha2/common.yaml +++ b/tests/components/seeed_mr60bha2/common.yaml @@ -1,11 +1,3 @@ -uart: - - id: seeed_mr60fda2_uart - tx_pin: ${uart_tx_pin} - rx_pin: ${uart_rx_pin} - baud_rate: 115200 - parity: NONE - stop_bits: 1 - seeed_mr60bha2: id: my_seeed_mr60bha2 diff --git a/tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml b/tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml index 4fb884abf4..fea89f5768 100644 --- a/tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml +++ b/tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - uart_tx_pin: GPIO5 - uart_rx_pin: GPIO4 +packages: + uart_115200: !include ../../test_build_components/common/uart_115200/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/seeed_mr60fda2/common.yaml b/tests/components/seeed_mr60fda2/common.yaml index 55a7cc1ab3..e0e4614481 100644 --- a/tests/components/seeed_mr60fda2/common.yaml +++ b/tests/components/seeed_mr60fda2/common.yaml @@ -1,14 +1,5 @@ -uart: - - id: seeed_mr60fda2_uart - tx_pin: ${uart_tx_pin} - rx_pin: ${uart_rx_pin} - baud_rate: 115200 - parity: NONE - stop_bits: 1 - seeed_mr60fda2: id: my_seeed_mr60fda2 - uart_id: seeed_mr60fda2_uart binary_sensor: - platform: seeed_mr60fda2 diff --git a/tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml b/tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml index 4fb884abf4..fea89f5768 100644 --- a/tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml +++ b/tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - uart_tx_pin: GPIO5 - uart_rx_pin: GPIO4 +packages: + uart_115200: !include ../../test_build_components/common/uart_115200/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/selec_meter/common.yaml b/tests/components/selec_meter/common.yaml index f2714ce828..2febbee540 100644 --- a/tests/components/selec_meter/common.yaml +++ b/tests/components/selec_meter/common.yaml @@ -1,11 +1,6 @@ -uart: - - id: uart_selec_meter - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: selec_meter + modbus_id: modbus_bus total_active_energy: name: SelecEM2M Total Active Energy import_active_energy: diff --git a/tests/components/selec_meter/test.esp32-c3-idf.yaml b/tests/components/selec_meter/test.esp32-c3-idf.yaml index b516342f3b..beb90e1471 100644 --- a/tests/components/selec_meter/test.esp32-c3-idf.yaml +++ b/tests/components/selec_meter/test.esp32-c3-idf.yaml @@ -1,5 +1,7 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + flow_control_pin: GPIO10 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/selec_meter/test.esp32-idf.yaml b/tests/components/selec_meter/test.esp32-idf.yaml index f486544afa..4df9f5863e 100644 --- a/tests/components/selec_meter/test.esp32-idf.yaml +++ b/tests/components/selec_meter/test.esp32-idf.yaml @@ -1,5 +1,9 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO26 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/selec_meter/test.esp8266-ard.yaml b/tests/components/selec_meter/test.esp8266-ard.yaml index b516342f3b..48a7307795 100644 --- a/tests/components/selec_meter/test.esp8266-ard.yaml +++ b/tests/components/selec_meter/test.esp8266-ard.yaml @@ -1,5 +1,9 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + flow_control_pin: GPIO4 + +packages: + modbus: !include ../../test_build_components/common/modbus/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/selec_meter/test.rp2040-ard.yaml b/tests/components/selec_meter/test.rp2040-ard.yaml index b516342f3b..a65500ccc3 100644 --- a/tests/components/selec_meter/test.rp2040-ard.yaml +++ b/tests/components/selec_meter/test.rp2040-ard.yaml @@ -1,5 +1,9 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 + flow_control_pin: GPIO6 + +packages: + modbus: !include ../../test_build_components/common/modbus/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sen0321/common.yaml b/tests/components/sen0321/common.yaml index 8b9fdff4a1..9228c7b820 100644 --- a/tests/components/sen0321/common.yaml +++ b/tests/components/sen0321/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sen0321 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sen0321 + i2c_id: i2c_bus name: Workshop Ozone Sensor id: sen0321_ozone update_interval: 10s diff --git a/tests/components/sen0321/test.esp32-c3-idf.yaml b/tests/components/sen0321/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sen0321/test.esp32-c3-idf.yaml +++ b/tests/components/sen0321/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sen0321/test.esp32-idf.yaml b/tests/components/sen0321/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sen0321/test.esp32-idf.yaml +++ b/tests/components/sen0321/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sen0321/test.esp8266-ard.yaml b/tests/components/sen0321/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sen0321/test.esp8266-ard.yaml +++ b/tests/components/sen0321/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sen0321/test.rp2040-ard.yaml b/tests/components/sen0321/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sen0321/test.rp2040-ard.yaml +++ b/tests/components/sen0321/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sen21231/common.yaml b/tests/components/sen21231/common.yaml index 6fa1d04aa2..bfe0f9d7f8 100644 --- a/tests/components/sen21231/common.yaml +++ b/tests/components/sen21231/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_sen21231 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sen21231 + i2c_id: i2c_bus id: sen21231_sensor1 name: Person Sensor diff --git a/tests/components/sen21231/test.esp32-c3-idf.yaml b/tests/components/sen21231/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sen21231/test.esp32-c3-idf.yaml +++ b/tests/components/sen21231/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sen21231/test.esp32-idf.yaml b/tests/components/sen21231/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sen21231/test.esp32-idf.yaml +++ b/tests/components/sen21231/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sen21231/test.esp8266-ard.yaml b/tests/components/sen21231/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sen21231/test.esp8266-ard.yaml +++ b/tests/components/sen21231/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sen21231/test.rp2040-ard.yaml b/tests/components/sen21231/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sen21231/test.rp2040-ard.yaml +++ b/tests/components/sen21231/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sen5x/common.yaml b/tests/components/sen5x/common.yaml index 9adf268048..a4462a16ea 100644 --- a/tests/components/sen5x/common.yaml +++ b/tests/components/sen5x/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sen5x - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sen5x + i2c_id: i2c_bus id: sen54 temperature: name: Temperature diff --git a/tests/components/sen5x/test.esp32-c3-idf.yaml b/tests/components/sen5x/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sen5x/test.esp32-c3-idf.yaml +++ b/tests/components/sen5x/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sen5x/test.esp32-idf.yaml b/tests/components/sen5x/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sen5x/test.esp32-idf.yaml +++ b/tests/components/sen5x/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sen5x/test.esp8266-ard.yaml b/tests/components/sen5x/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sen5x/test.esp8266-ard.yaml +++ b/tests/components/sen5x/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sen5x/test.rp2040-ard.yaml b/tests/components/sen5x/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sen5x/test.rp2040-ard.yaml +++ b/tests/components/sen5x/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/senseair/common.yaml b/tests/components/senseair/common.yaml index 23a933affe..c8066896d0 100644 --- a/tests/components/senseair/common.yaml +++ b/tests/components/senseair/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_senseair - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: senseair id: senseair0 diff --git a/tests/components/senseair/test.esp32-c3-idf.yaml b/tests/components/senseair/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/senseair/test.esp32-c3-idf.yaml +++ b/tests/components/senseair/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/senseair/test.esp32-idf.yaml b/tests/components/senseair/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/senseair/test.esp32-idf.yaml +++ b/tests/components/senseair/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/senseair/test.esp8266-ard.yaml b/tests/components/senseair/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/senseair/test.esp8266-ard.yaml +++ b/tests/components/senseair/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/senseair/test.rp2040-ard.yaml b/tests/components/senseair/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/senseair/test.rp2040-ard.yaml +++ b/tests/components/senseair/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sfa30/common.yaml b/tests/components/sfa30/common.yaml index e3b38aa7fb..3cd2483abc 100644 --- a/tests/components/sfa30/common.yaml +++ b/tests/components/sfa30/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sfa30 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sfa30 + i2c_id: i2c_bus formaldehyde: name: SFA30 formaldehyde temperature: diff --git a/tests/components/sfa30/test.esp32-c3-idf.yaml b/tests/components/sfa30/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sfa30/test.esp32-c3-idf.yaml +++ b/tests/components/sfa30/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sfa30/test.esp32-idf.yaml b/tests/components/sfa30/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sfa30/test.esp32-idf.yaml +++ b/tests/components/sfa30/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sfa30/test.esp8266-ard.yaml b/tests/components/sfa30/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sfa30/test.esp8266-ard.yaml +++ b/tests/components/sfa30/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sfa30/test.rp2040-ard.yaml b/tests/components/sfa30/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sfa30/test.rp2040-ard.yaml +++ b/tests/components/sfa30/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sgp30/common.yaml b/tests/components/sgp30/common.yaml index 1db5bc67d1..9d4ed46615 100644 --- a/tests/components/sgp30/common.yaml +++ b/tests/components/sgp30/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sgp30 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sgp30 + i2c_id: i2c_bus eco2: name: Workshop eCO2 accuracy_decimals: 1 diff --git a/tests/components/sgp30/test.esp32-c3-idf.yaml b/tests/components/sgp30/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sgp30/test.esp32-c3-idf.yaml +++ b/tests/components/sgp30/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sgp30/test.esp32-idf.yaml b/tests/components/sgp30/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sgp30/test.esp32-idf.yaml +++ b/tests/components/sgp30/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sgp30/test.esp8266-ard.yaml b/tests/components/sgp30/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sgp30/test.esp8266-ard.yaml +++ b/tests/components/sgp30/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sgp30/test.rp2040-ard.yaml b/tests/components/sgp30/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sgp30/test.rp2040-ard.yaml +++ b/tests/components/sgp30/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sgp4x/common.yaml b/tests/components/sgp4x/common.yaml index adb678d542..4edda8fd1b 100644 --- a/tests/components/sgp4x/common.yaml +++ b/tests/components/sgp4x/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sgp4x - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sgp4x + i2c_id: i2c_bus voc: name: VOC Index id: sgp40_voc_index diff --git a/tests/components/sgp4x/test.esp32-c3-idf.yaml b/tests/components/sgp4x/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sgp4x/test.esp32-c3-idf.yaml +++ b/tests/components/sgp4x/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sgp4x/test.esp32-idf.yaml b/tests/components/sgp4x/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sgp4x/test.esp32-idf.yaml +++ b/tests/components/sgp4x/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sgp4x/test.esp8266-ard.yaml b/tests/components/sgp4x/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sgp4x/test.esp8266-ard.yaml +++ b/tests/components/sgp4x/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sgp4x/test.rp2040-ard.yaml b/tests/components/sgp4x/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sgp4x/test.rp2040-ard.yaml +++ b/tests/components/sgp4x/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/shelly_dimmer/common.yaml b/tests/components/shelly_dimmer/common.yaml index 3acd0260d5..ba36fa995d 100644 --- a/tests/components/shelly_dimmer/common.yaml +++ b/tests/components/shelly_dimmer/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_shelly_dimmer - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - light: - platform: shelly_dimmer name: Shelly Dimmer Light diff --git a/tests/components/shelly_dimmer/test.esp8266-ard.yaml b/tests/components/shelly_dimmer/test.esp8266-ard.yaml index dade44d145..80a2cb2fc0 100644 --- a/tests/components/shelly_dimmer/test.esp8266-ard.yaml +++ b/tests/components/shelly_dimmer/test.esp8266-ard.yaml @@ -1 +1,4 @@ +packages: + uart_115200: !include ../../test_build_components/common/uart_115200/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sht3xd/common.yaml b/tests/components/sht3xd/common.yaml index 2426ebfbb9..02ce521747 100644 --- a/tests/components/sht3xd/common.yaml +++ b/tests/components/sht3xd/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sht3xd - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sht3xd + i2c_id: i2c_bus temperature: name: SHT3XD Temperature humidity: diff --git a/tests/components/sht3xd/test.esp32-c3-idf.yaml b/tests/components/sht3xd/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sht3xd/test.esp32-c3-idf.yaml +++ b/tests/components/sht3xd/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sht3xd/test.esp32-idf.yaml b/tests/components/sht3xd/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sht3xd/test.esp32-idf.yaml +++ b/tests/components/sht3xd/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sht3xd/test.esp8266-ard.yaml b/tests/components/sht3xd/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sht3xd/test.esp8266-ard.yaml +++ b/tests/components/sht3xd/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sht3xd/test.rp2040-ard.yaml b/tests/components/sht3xd/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sht3xd/test.rp2040-ard.yaml +++ b/tests/components/sht3xd/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sht4x/common.yaml b/tests/components/sht4x/common.yaml index 703a8fa32b..50d5ad8ca4 100644 --- a/tests/components/sht4x/common.yaml +++ b/tests/components/sht4x/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sht4x - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sht4x + i2c_id: i2c_bus temperature: name: SHT4X Temperature humidity: diff --git a/tests/components/sht4x/test.esp32-c3-idf.yaml b/tests/components/sht4x/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sht4x/test.esp32-c3-idf.yaml +++ b/tests/components/sht4x/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sht4x/test.esp32-idf.yaml b/tests/components/sht4x/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sht4x/test.esp32-idf.yaml +++ b/tests/components/sht4x/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sht4x/test.esp8266-ard.yaml b/tests/components/sht4x/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sht4x/test.esp8266-ard.yaml +++ b/tests/components/sht4x/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sht4x/test.rp2040-ard.yaml b/tests/components/sht4x/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sht4x/test.rp2040-ard.yaml +++ b/tests/components/sht4x/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/shtcx/common.yaml b/tests/components/shtcx/common.yaml index 0211319124..a326a1b4d6 100644 --- a/tests/components/shtcx/common.yaml +++ b/tests/components/shtcx/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_shtcx - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: shtcx + i2c_id: i2c_bus temperature: name: SHTCX Temperature humidity: diff --git a/tests/components/shtcx/test.esp32-c3-idf.yaml b/tests/components/shtcx/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/shtcx/test.esp32-c3-idf.yaml +++ b/tests/components/shtcx/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/shtcx/test.esp32-idf.yaml b/tests/components/shtcx/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/shtcx/test.esp32-idf.yaml +++ b/tests/components/shtcx/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/shtcx/test.esp8266-ard.yaml b/tests/components/shtcx/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/shtcx/test.esp8266-ard.yaml +++ b/tests/components/shtcx/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/shtcx/test.rp2040-ard.yaml b/tests/components/shtcx/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/shtcx/test.rp2040-ard.yaml +++ b/tests/components/shtcx/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sim800l/common.yaml b/tests/components/sim800l/common.yaml index 1b4e2e1af6..503637b35f 100644 --- a/tests/components/sim800l/common.yaml +++ b/tests/components/sim800l/common.yaml @@ -11,12 +11,6 @@ esphome: - sim800l.send_ussd: ussd: test_ussd -uart: - - id: uart_sim800l - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sim800l: on_sms_received: - lambda: |- diff --git a/tests/components/sim800l/test.esp32-c3-idf.yaml b/tests/components/sim800l/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/sim800l/test.esp32-c3-idf.yaml +++ b/tests/components/sim800l/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sim800l/test.esp32-idf.yaml b/tests/components/sim800l/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/sim800l/test.esp32-idf.yaml +++ b/tests/components/sim800l/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sim800l/test.esp8266-ard.yaml b/tests/components/sim800l/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/sim800l/test.esp8266-ard.yaml +++ b/tests/components/sim800l/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sim800l/test.rp2040-ard.yaml b/tests/components/sim800l/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/sim800l/test.rp2040-ard.yaml +++ b/tests/components/sim800l/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sm16716/test.esp32-idf.yaml b/tests/components/sm16716/test.esp32-idf.yaml index d295973e3f..a4ecdb6c49 100644 --- a/tests/components/sm16716/test.esp32-idf.yaml +++ b/tests/components/sm16716/test.esp32-idf.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 + clock_pin: GPIO4 + data_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/sm16716/test.esp8266-ard.yaml b/tests/components/sm16716/test.esp8266-ard.yaml index 7808481215..7c7f1e1a11 100644 --- a/tests/components/sm16716/test.esp8266-ard.yaml +++ b/tests/components/sm16716/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 + clock_pin: GPIO0 + data_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/sm2135/test.esp32-idf.yaml b/tests/components/sm2135/test.esp32-idf.yaml index d295973e3f..a4ecdb6c49 100644 --- a/tests/components/sm2135/test.esp32-idf.yaml +++ b/tests/components/sm2135/test.esp32-idf.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 + clock_pin: GPIO4 + data_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/sm2135/test.esp8266-ard.yaml b/tests/components/sm2135/test.esp8266-ard.yaml index 7808481215..7c7f1e1a11 100644 --- a/tests/components/sm2135/test.esp8266-ard.yaml +++ b/tests/components/sm2135/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 + clock_pin: GPIO0 + data_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/sm2235/test.esp32-idf.yaml b/tests/components/sm2235/test.esp32-idf.yaml index d295973e3f..a4ecdb6c49 100644 --- a/tests/components/sm2235/test.esp32-idf.yaml +++ b/tests/components/sm2235/test.esp32-idf.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 + clock_pin: GPIO4 + data_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/sm2235/test.esp8266-ard.yaml b/tests/components/sm2235/test.esp8266-ard.yaml index 7808481215..7c7f1e1a11 100644 --- a/tests/components/sm2235/test.esp8266-ard.yaml +++ b/tests/components/sm2235/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 + clock_pin: GPIO0 + data_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/sm2335/test.esp32-idf.yaml b/tests/components/sm2335/test.esp32-idf.yaml index d295973e3f..a4ecdb6c49 100644 --- a/tests/components/sm2335/test.esp32-idf.yaml +++ b/tests/components/sm2335/test.esp32-idf.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 + clock_pin: GPIO4 + data_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/sm2335/test.esp8266-ard.yaml b/tests/components/sm2335/test.esp8266-ard.yaml index 7808481215..7c7f1e1a11 100644 --- a/tests/components/sm2335/test.esp8266-ard.yaml +++ b/tests/components/sm2335/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 + clock_pin: GPIO0 + data_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/sm300d2/common.yaml b/tests/components/sm300d2/common.yaml index a231b63816..729f243a65 100644 --- a/tests/components/sm300d2/common.yaml +++ b/tests/components/sm300d2/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_sm300d2 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: sm300d2 co2: diff --git a/tests/components/sm300d2/test.esp32-c3-idf.yaml b/tests/components/sm300d2/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/sm300d2/test.esp32-c3-idf.yaml +++ b/tests/components/sm300d2/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sm300d2/test.esp32-idf.yaml b/tests/components/sm300d2/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/sm300d2/test.esp32-idf.yaml +++ b/tests/components/sm300d2/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sm300d2/test.esp8266-ard.yaml b/tests/components/sm300d2/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/sm300d2/test.esp8266-ard.yaml +++ b/tests/components/sm300d2/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sm300d2/test.rp2040-ard.yaml b/tests/components/sm300d2/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/sm300d2/test.rp2040-ard.yaml +++ b/tests/components/sm300d2/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sml/common.yaml b/tests/components/sml/common.yaml index a50d25eeee..b1bd5949b6 100644 --- a/tests/components/sml/common.yaml +++ b/tests/components/sml/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_sml - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sml: id: mysml on_data: diff --git a/tests/components/sml/test.esp32-c3-idf.yaml b/tests/components/sml/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/sml/test.esp32-c3-idf.yaml +++ b/tests/components/sml/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sml/test.esp32-idf.yaml b/tests/components/sml/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/sml/test.esp32-idf.yaml +++ b/tests/components/sml/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sml/test.esp8266-ard.yaml b/tests/components/sml/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/sml/test.esp8266-ard.yaml +++ b/tests/components/sml/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sml/test.rp2040-ard.yaml b/tests/components/sml/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/sml/test.rp2040-ard.yaml +++ b/tests/components/sml/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/smt100/common.yaml b/tests/components/smt100/common.yaml index b12d7198fd..86c5636b94 100644 --- a/tests/components/smt100/common.yaml +++ b/tests/components/smt100/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_smt100 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: smt100 counts: diff --git a/tests/components/smt100/test.esp32-c3-idf.yaml b/tests/components/smt100/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/smt100/test.esp32-c3-idf.yaml +++ b/tests/components/smt100/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/smt100/test.esp32-idf.yaml b/tests/components/smt100/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/smt100/test.esp32-idf.yaml +++ b/tests/components/smt100/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/smt100/test.esp8266-ard.yaml b/tests/components/smt100/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/smt100/test.esp8266-ard.yaml +++ b/tests/components/smt100/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/smt100/test.rp2040-ard.yaml b/tests/components/smt100/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/smt100/test.rp2040-ard.yaml +++ b/tests/components/smt100/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sn74hc165/test.esp32-idf.yaml b/tests/components/sn74hc165/test.esp32-idf.yaml index 27f963312f..8a997e16e1 100644 --- a/tests/components/sn74hc165/test.esp32-idf.yaml +++ b/tests/components/sn74hc165/test.esp32-idf.yaml @@ -2,6 +2,6 @@ substitutions: clock_pin: GPIO13 data_pin: GPIO14 load_pin: GPIO15 - clock_inhibit_pin: GPIO16 + clock_inhibit_pin: GPIO4 <<: !include common.yaml diff --git a/tests/components/sn74hc165/test.esp8266-ard.yaml b/tests/components/sn74hc165/test.esp8266-ard.yaml index 27f963312f..00ffc10a9e 100644 --- a/tests/components/sn74hc165/test.esp8266-ard.yaml +++ b/tests/components/sn74hc165/test.esp8266-ard.yaml @@ -1,6 +1,6 @@ substitutions: - clock_pin: GPIO13 - data_pin: GPIO14 + clock_pin: GPIO0 + data_pin: GPIO2 load_pin: GPIO15 clock_inhibit_pin: GPIO16 diff --git a/tests/components/sn74hc595/common.yaml b/tests/components/sn74hc595/common.yaml index fc297909f5..3892c0564b 100644 --- a/tests/components/sn74hc595/common.yaml +++ b/tests/components/sn74hc595/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_sn74hc595 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sn74hc595: - id: sn74hc595_hub clock_pin: ${clock_pin} diff --git a/tests/components/sn74hc595/test.esp32-c3-idf.yaml b/tests/components/sn74hc595/test.esp32-c3-idf.yaml index 14c928be88..74b5e855fa 100644 --- a/tests/components/sn74hc595/test.esp32-c3-idf.yaml +++ b/tests/components/sn74hc595/test.esp32-c3-idf.yaml @@ -1,9 +1,9 @@ +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml + substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO8 - clock_pin: GPIO5 - data_pin: GPIO4 + clock_pin: GPIO7 + data_pin: GPIO10 latch_pin1: GPIO1 oe_pin1: GPIO2 latch_pin2: GPIO3 diff --git a/tests/components/sn74hc595/test.esp32-idf.yaml b/tests/components/sn74hc595/test.esp32-idf.yaml index a4bab64862..4b47d2aeaa 100644 --- a/tests/components/sn74hc595/test.esp32-idf.yaml +++ b/tests/components/sn74hc595/test.esp32-idf.yaml @@ -1,12 +1,12 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO18 clock_pin: GPIO15 data_pin: GPIO14 latch_pin1: GPIO21 oe_pin1: GPIO22 - latch_pin2: GPIO23 - oe_pin2: GPIO25 + latch_pin2: GPIO25 + oe_pin2: GPIO26 + +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sn74hc595/test.esp8266-ard.yaml b/tests/components/sn74hc595/test.esp8266-ard.yaml index cad11feca8..cc011e01d4 100644 --- a/tests/components/sn74hc595/test.esp8266-ard.yaml +++ b/tests/components/sn74hc595/test.esp8266-ard.yaml @@ -1,7 +1,7 @@ +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 clock_pin: GPIO5 data_pin: GPIO4 latch_pin1: GPIO2 diff --git a/tests/components/sn74hc595/test.rp2040-ard.yaml b/tests/components/sn74hc595/test.rp2040-ard.yaml index 14c928be88..93cf5efb0c 100644 --- a/tests/components/sn74hc595/test.rp2040-ard.yaml +++ b/tests/components/sn74hc595/test.rp2040-ard.yaml @@ -1,12 +1,12 @@ +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO8 clock_pin: GPIO5 - data_pin: GPIO4 + data_pin: GPIO0 latch_pin1: GPIO1 - oe_pin1: GPIO2 - latch_pin2: GPIO3 + oe_pin1: GPIO6 + latch_pin2: GPIO7 oe_pin2: GPIO9 <<: !include common.yaml diff --git a/tests/components/sonoff_d1/common.yaml b/tests/components/sonoff_d1/common.yaml index d2d4043b95..dc08380683 100644 --- a/tests/components/sonoff_d1/common.yaml +++ b/tests/components/sonoff_d1/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_sonoff_d1 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - light: - platform: sonoff_d1 id: d1_light diff --git a/tests/components/sonoff_d1/test.esp32-c3-idf.yaml b/tests/components/sonoff_d1/test.esp32-c3-idf.yaml index b516342f3b..a19013bf54 100644 --- a/tests/components/sonoff_d1/test.esp32-c3-idf.yaml +++ b/tests/components/sonoff_d1/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.esp32-idf.yaml b/tests/components/sonoff_d1/test.esp32-idf.yaml index f486544afa..2d29656c94 100644 --- a/tests/components/sonoff_d1/test.esp32-idf.yaml +++ b/tests/components/sonoff_d1/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.esp8266-ard.yaml b/tests/components/sonoff_d1/test.esp8266-ard.yaml index b516342f3b..5a05efa259 100644 --- a/tests/components/sonoff_d1/test.esp8266-ard.yaml +++ b/tests/components/sonoff_d1/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.rp2040-ard.yaml b/tests/components/sonoff_d1/test.rp2040-ard.yaml index b516342f3b..f1df2daf83 100644 --- a/tests/components/sonoff_d1/test.rp2040-ard.yaml +++ b/tests/components/sonoff_d1/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sound_level/test.esp32-idf.yaml b/tests/components/sound_level/test.esp32-idf.yaml index c6d1bfa330..20e38e8df8 100644 --- a/tests/components/sound_level/test.esp32-idf.yaml +++ b/tests/components/sound_level/test.esp32-idf.yaml @@ -3,4 +3,7 @@ substitutions: i2s_lrclk_pin: GPIO26 i2s_dout_pin: GPIO27 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/speaker/audio_dac.esp32-ard.yaml b/tests/components/speaker/audio_dac.esp32-ard.yaml index 75d9ddf92b..3f5d1bba7c 100644 --- a/tests/components/speaker/audio_dac.esp32-ard.yaml +++ b/tests/components/speaker/audio_dac.esp32-ard.yaml @@ -1,9 +1,10 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 i2s_bclk_pin: GPIO27 i2s_lrclk_pin: GPIO26 i2s_mclk_pin: GPIO25 i2s_dout_pin: GPIO23 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml + <<: !include common-audio_dac.yaml diff --git a/tests/components/speaker/audio_dac.esp32-c3-idf.yaml b/tests/components/speaker/audio_dac.esp32-c3-idf.yaml index 1004d2143e..30900f1920 100644 --- a/tests/components/speaker/audio_dac.esp32-c3-idf.yaml +++ b/tests/components/speaker/audio_dac.esp32-c3-idf.yaml @@ -1,9 +1,10 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 i2s_bclk_pin: GPIO7 i2s_lrclk_pin: GPIO6 i2s_mclk_pin: GPIO9 i2s_dout_pin: GPIO8 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common-audio_dac.yaml diff --git a/tests/components/speaker/audio_dac.esp32-idf.yaml b/tests/components/speaker/audio_dac.esp32-idf.yaml index 75d9ddf92b..71c8b06e24 100644 --- a/tests/components/speaker/audio_dac.esp32-idf.yaml +++ b/tests/components/speaker/audio_dac.esp32-idf.yaml @@ -1,9 +1,10 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 i2s_bclk_pin: GPIO27 i2s_lrclk_pin: GPIO26 i2s_mclk_pin: GPIO25 i2s_dout_pin: GPIO23 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common-audio_dac.yaml diff --git a/tests/components/speaker/common-audio_dac.yaml b/tests/components/speaker/common-audio_dac.yaml index 41b994d4d4..67bd6c28ef 100644 --- a/tests/components/speaker/common-audio_dac.yaml +++ b/tests/components/speaker/common-audio_dac.yaml @@ -14,11 +14,6 @@ esphome: - speaker.finish: - speaker.stop: -i2c: - - id: i2c_audio_dac - scl: ${scl_pin} - sda: ${sda_pin} - i2s_audio: i2s_lrclk_pin: ${i2s_bclk_pin} i2s_bclk_pin: ${i2s_lrclk_pin} @@ -26,6 +21,7 @@ i2s_audio: audio_dac: - platform: aic3204 + i2c_id: i2c_bus id: internal_dac speaker: diff --git a/tests/components/speaker/test.esp32-ard.yaml b/tests/components/speaker/test.esp32-ard.yaml index e2439ebdf2..13350cd097 100644 --- a/tests/components/speaker/test.esp32-ard.yaml +++ b/tests/components/speaker/test.esp32-ard.yaml @@ -1,9 +1,10 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 i2s_bclk_pin: GPIO27 i2s_lrclk_pin: GPIO26 i2s_mclk_pin: GPIO25 - i2s_dout_pin: GPIO23 + i2s_dout_pin: GPIO4 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml <<: !include common.yaml diff --git a/tests/components/speaker/test.esp32-c3-idf.yaml b/tests/components/speaker/test.esp32-c3-idf.yaml index ddcf051fab..9d1a1cca3b 100644 --- a/tests/components/speaker/test.esp32-c3-idf.yaml +++ b/tests/components/speaker/test.esp32-c3-idf.yaml @@ -6,4 +6,7 @@ substitutions: i2s_mclk_pin: GPIO9 i2s_dout_pin: GPIO8 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/speaker/test.esp32-idf.yaml b/tests/components/speaker/test.esp32-idf.yaml index e2439ebdf2..27b8604656 100644 --- a/tests/components/speaker/test.esp32-idf.yaml +++ b/tests/components/speaker/test.esp32-idf.yaml @@ -1,9 +1,10 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 i2s_bclk_pin: GPIO27 i2s_lrclk_pin: GPIO26 i2s_mclk_pin: GPIO25 - i2s_dout_pin: GPIO23 + i2s_dout_pin: GPIO12 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/spi/common.yaml b/tests/components/spi/common.yaml index 04b4779957..e69de29bb2 100644 --- a/tests/components/spi/common.yaml +++ b/tests/components/spi/common.yaml @@ -1,5 +0,0 @@ -spi: - - id: spi_spi - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} diff --git a/tests/components/spi/test.esp32-c3-idf.yaml b/tests/components/spi/test.esp32-c3-idf.yaml index bfa12b1755..54463271a9 100644 --- a/tests/components/spi/test.esp32-c3-idf.yaml +++ b/tests/components/spi/test.esp32-c3-idf.yaml @@ -1,6 +1,5 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/spi/test.esp32-idf.yaml b/tests/components/spi/test.esp32-idf.yaml index 448e54fea6..a8e18ca503 100644 --- a/tests/components/spi/test.esp32-idf.yaml +++ b/tests/components/spi/test.esp32-idf.yaml @@ -1,6 +1,4 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/spi/test.esp8266-ard.yaml b/tests/components/spi/test.esp8266-ard.yaml index b9545d4f6a..4f9c816740 100644 --- a/tests/components/spi/test.esp8266-ard.yaml +++ b/tests/components/spi/test.esp8266-ard.yaml @@ -1,6 +1,6 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO15 <<: !include common.yaml diff --git a/tests/components/spi_device/common.yaml b/tests/components/spi_device/common.yaml index 0f6a5038fb..8511603bc2 100644 --- a/tests/components/spi_device/common.yaml +++ b/tests/components/spi_device/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_device1 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - spi_device: - id: spi_device_test data_rate: 2MHz diff --git a/tests/components/spi_device/test.esp32-c3-idf.yaml b/tests/components/spi_device/test.esp32-c3-idf.yaml index bfa12b1755..6d64c2b23b 100644 --- a/tests/components/spi_device/test.esp32-c3-idf.yaml +++ b/tests/components/spi_device/test.esp32-c3-idf.yaml @@ -1,6 +1,4 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/spi_device/test.esp32-idf.yaml b/tests/components/spi_device/test.esp32-idf.yaml index c4989cccbf..aace9192b1 100644 --- a/tests/components/spi_device/test.esp32-idf.yaml +++ b/tests/components/spi_device/test.esp32-idf.yaml @@ -1,7 +1,5 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - miso_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml <<: !include common.yaml spi_device: diff --git a/tests/components/spi_device/test.esp8266-ard.yaml b/tests/components/spi_device/test.esp8266-ard.yaml index b9545d4f6a..433a931284 100644 --- a/tests/components/spi_device/test.esp8266-ard.yaml +++ b/tests/components/spi_device/test.esp8266-ard.yaml @@ -1,6 +1,4 @@ -substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/spi_device/test.rp2040-ard.yaml b/tests/components/spi_device/test.rp2040-ard.yaml index 81a8acafd8..149d530753 100644 --- a/tests/components/spi_device/test.rp2040-ard.yaml +++ b/tests/components/spi_device/test.rp2040-ard.yaml @@ -1,6 +1,4 @@ -substitutions: - clk_pin: GPIO2 - mosi_pin: GPIO3 - miso_pin: GPIO4 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/spi_led_strip/common.yaml b/tests/components/spi_led_strip/common.yaml index 80b98a63a4..30e2ef22f7 100644 --- a/tests/components/spi_led_strip/common.yaml +++ b/tests/components/spi_led_strip/common.yaml @@ -1,8 +1,3 @@ -spi: - - id: spi_spi_led_strip - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - light: - platform: spi_led_strip num_leds: 4 diff --git a/tests/components/spi_led_strip/test.esp32-c3-idf.yaml b/tests/components/spi_led_strip/test.esp32-c3-idf.yaml index a85b587070..6d64c2b23b 100644 --- a/tests/components/spi_led_strip/test.esp32-c3-idf.yaml +++ b/tests/components/spi_led_strip/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.esp32-idf.yaml b/tests/components/spi_led_strip/test.esp32-idf.yaml index 8906602ef4..a8e18ca503 100644 --- a/tests/components/spi_led_strip/test.esp32-idf.yaml +++ b/tests/components/spi_led_strip/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.esp8266-ard.yaml b/tests/components/spi_led_strip/test.esp8266-ard.yaml index 7baaa62ed5..433a931284 100644 --- a/tests/components/spi_led_strip/test.esp8266-ard.yaml +++ b/tests/components/spi_led_strip/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.rp2040-ard.yaml b/tests/components/spi_led_strip/test.rp2040-ard.yaml index 411cfbe00e..149d530753 100644 --- a/tests/components/spi_led_strip/test.rp2040-ard.yaml +++ b/tests/components/spi_led_strip/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - clk_pin: GPIO2 - mosi_pin: GPIO3 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sps30/common.yaml b/tests/components/sps30/common.yaml index 2fbe2c747a..d40cd16b6d 100644 --- a/tests/components/sps30/common.yaml +++ b/tests/components/sps30/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sps30 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sps30 + i2c_id: i2c_bus pm_1_0: name: Workshop PM <1µm Weight concentration id: workshop_PM_1_0 diff --git a/tests/components/sps30/test.esp32-c3-idf.yaml b/tests/components/sps30/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sps30/test.esp32-c3-idf.yaml +++ b/tests/components/sps30/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sps30/test.esp32-idf.yaml b/tests/components/sps30/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sps30/test.esp32-idf.yaml +++ b/tests/components/sps30/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sps30/test.esp8266-ard.yaml b/tests/components/sps30/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sps30/test.esp8266-ard.yaml +++ b/tests/components/sps30/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sps30/test.rp2040-ard.yaml b/tests/components/sps30/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sps30/test.rp2040-ard.yaml +++ b/tests/components/sps30/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/common.yaml b/tests/components/ssd1306_i2c/common.yaml index d17f83f03a..e876fcb36a 100644 --- a/tests/components/ssd1306_i2c/common.yaml +++ b/tests/components/ssd1306_i2c/common.yaml @@ -1,25 +1,21 @@ -i2c: - - id: i2c_ssd1306_i2c - scl: ${scl_pin} - sda: ${sda_pin} - display: - platform: ssd1306_i2c + i2c_id: i2c_bus model: SSD1306_128X64 reset_pin: ${reset_pin} address: 0x3C id: display1 contrast: 60% pages: - - id: page1 + - id: ssd1306_i2c_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: ssd1306_i2c_page2 lambda: |- it.rectangle(0, 0, 10, 10); on_page_change: - from: page1 - to: page2 + from: ssd1306_i2c_page1 + to: ssd1306_i2c_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml b/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml index 4eaff7fa4a..f8bfab2319 100644 --- a/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.esp32-idf.yaml b/tests/components/ssd1306_i2c/test.esp32-idf.yaml index 1ca773e06c..4ff2241ec9 100644 --- a/tests/components/ssd1306_i2c/test.esp32-idf.yaml +++ b/tests/components/ssd1306_i2c/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.esp8266-ard.yaml b/tests/components/ssd1306_i2c/test.esp8266-ard.yaml index af91c21a0d..352cc8cda8 100644 --- a/tests/components/ssd1306_i2c/test.esp8266-ard.yaml +++ b/tests/components/ssd1306_i2c/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO2 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.rp2040-ard.yaml b/tests/components/ssd1306_i2c/test.rp2040-ard.yaml index 4eaff7fa4a..2972fde8a5 100644 --- a/tests/components/ssd1306_i2c/test.rp2040-ard.yaml +++ b/tests/components/ssd1306_i2c/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1306_spi/common.yaml b/tests/components/ssd1306_spi/common.yaml index 71705f32d2..2a2adb4146 100644 --- a/tests/components/ssd1306_spi/common.yaml +++ b/tests/components/ssd1306_spi/common.yaml @@ -1,8 +1,3 @@ -spi: - - id: spi_ssd1306_spi - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: ssd1306_spi model: SSD1306 128x64 @@ -10,15 +5,15 @@ display: dc_pin: ${dc_pin} reset_pin: ${reset_pin} pages: - - id: page1 + - id: ssd1306_spi_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: ssd1306_spi_page2 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); on_page_change: - from: page1 - to: page2 + from: ssd1306_spi_page1 + to: ssd1306_spi_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml index c5c932c92c..b112cf4c31 100644 --- a/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.esp32-idf.yaml b/tests/components/ssd1306_spi/test.esp32-idf.yaml index bad5241f79..ff174a4656 100644 --- a/tests/components/ssd1306_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1306_spi/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.esp8266-ard.yaml b/tests/components/ssd1306_spi/test.esp8266-ard.yaml index 3f023a60eb..56cb29f29e 100644 --- a/tests/components/ssd1306_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1306_spi/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.rp2040-ard.yaml b/tests/components/ssd1306_spi/test.rp2040-ard.yaml index d7fd6ee294..66caa956f7 100644 --- a/tests/components/ssd1306_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1306_spi/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1322_spi/common.yaml b/tests/components/ssd1322_spi/common.yaml index b67392794c..c0e16b6f09 100644 --- a/tests/components/ssd1322_spi/common.yaml +++ b/tests/components/ssd1322_spi/common.yaml @@ -1,8 +1,3 @@ -spi: - - id: spi_ssd1322_spi - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: ssd1322_spi model: SSD1322 256x64 @@ -10,15 +5,15 @@ display: dc_pin: ${dc_pin} reset_pin: ${reset_pin} pages: - - id: page1 + - id: ssd1322_spi_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: ssd1322_spi_page2 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); on_page_change: - from: page1 - to: page2 + from: ssd1322_spi_page1 + to: ssd1322_spi_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml index c5c932c92c..b112cf4c31 100644 --- a/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.esp32-idf.yaml b/tests/components/ssd1322_spi/test.esp32-idf.yaml index bad5241f79..ff174a4656 100644 --- a/tests/components/ssd1322_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1322_spi/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.esp8266-ard.yaml b/tests/components/ssd1322_spi/test.esp8266-ard.yaml index 3f023a60eb..56cb29f29e 100644 --- a/tests/components/ssd1322_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1322_spi/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.rp2040-ard.yaml b/tests/components/ssd1322_spi/test.rp2040-ard.yaml index d7fd6ee294..66caa956f7 100644 --- a/tests/components/ssd1322_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1322_spi/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1325_spi/common.yaml b/tests/components/ssd1325_spi/common.yaml index eaa8b619ca..dea8502538 100644 --- a/tests/components/ssd1325_spi/common.yaml +++ b/tests/components/ssd1325_spi/common.yaml @@ -1,8 +1,3 @@ -spi: - - id: spi_ssd1325_spi - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: ssd1325_spi model: SSD1325 128x64 @@ -10,15 +5,15 @@ display: dc_pin: ${dc_pin} reset_pin: ${reset_pin} pages: - - id: page1 + - id: ssd1325_spi_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: ssd1325_spi_page2 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); on_page_change: - from: page1 - to: page2 + from: ssd1325_spi_page1 + to: ssd1325_spi_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml index c5c932c92c..b112cf4c31 100644 --- a/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.esp32-idf.yaml b/tests/components/ssd1325_spi/test.esp32-idf.yaml index bad5241f79..ff174a4656 100644 --- a/tests/components/ssd1325_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1325_spi/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.esp8266-ard.yaml b/tests/components/ssd1325_spi/test.esp8266-ard.yaml index 3f023a60eb..56cb29f29e 100644 --- a/tests/components/ssd1325_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1325_spi/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.rp2040-ard.yaml b/tests/components/ssd1325_spi/test.rp2040-ard.yaml index d7fd6ee294..66caa956f7 100644 --- a/tests/components/ssd1325_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1325_spi/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/common.yaml b/tests/components/ssd1327_i2c/common.yaml index 72a122c3d7..c90e9678dd 100644 --- a/tests/components/ssd1327_i2c/common.yaml +++ b/tests/components/ssd1327_i2c/common.yaml @@ -1,24 +1,20 @@ -i2c: - - id: i2c_ssd1327_i2c - scl: ${scl_pin} - sda: ${sda_pin} - display: - platform: ssd1327_i2c + i2c_id: i2c_bus model: SSD1327_128x128 reset_pin: ${reset_pin} address: 0x3C id: display1 pages: - - id: page1 + - id: ssd1327_i2c_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: ssd1327_i2c_page2 lambda: |- it.rectangle(0, 0, 10, 10); on_page_change: - from: page1 - to: page2 + from: ssd1327_i2c_page1 + to: ssd1327_i2c_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml b/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml index 4eaff7fa4a..f8bfab2319 100644 --- a/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.esp32-idf.yaml b/tests/components/ssd1327_i2c/test.esp32-idf.yaml index 1ca773e06c..4ff2241ec9 100644 --- a/tests/components/ssd1327_i2c/test.esp32-idf.yaml +++ b/tests/components/ssd1327_i2c/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.esp8266-ard.yaml b/tests/components/ssd1327_i2c/test.esp8266-ard.yaml index af91c21a0d..352cc8cda8 100644 --- a/tests/components/ssd1327_i2c/test.esp8266-ard.yaml +++ b/tests/components/ssd1327_i2c/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO2 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.rp2040-ard.yaml b/tests/components/ssd1327_i2c/test.rp2040-ard.yaml index 4eaff7fa4a..2972fde8a5 100644 --- a/tests/components/ssd1327_i2c/test.rp2040-ard.yaml +++ b/tests/components/ssd1327_i2c/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1327_spi/common.yaml b/tests/components/ssd1327_spi/common.yaml index 85f4d4736b..1aa4fb5a1c 100644 --- a/tests/components/ssd1327_spi/common.yaml +++ b/tests/components/ssd1327_spi/common.yaml @@ -1,8 +1,3 @@ -spi: - - id: spi_ssd1327_spi - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: ssd1327_spi model: SSD1327 128x128 @@ -10,15 +5,15 @@ display: dc_pin: ${dc_pin} reset_pin: ${reset_pin} pages: - - id: page1 + - id: ssd1327_spi_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: ssd1327_spi_page2 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); on_page_change: - from: page1 - to: page2 + from: ssd1327_spi_page1 + to: ssd1327_spi_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml index c5c932c92c..b112cf4c31 100644 --- a/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.esp32-idf.yaml b/tests/components/ssd1327_spi/test.esp32-idf.yaml index bad5241f79..ff174a4656 100644 --- a/tests/components/ssd1327_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1327_spi/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.esp8266-ard.yaml b/tests/components/ssd1327_spi/test.esp8266-ard.yaml index 3f023a60eb..56cb29f29e 100644 --- a/tests/components/ssd1327_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1327_spi/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.rp2040-ard.yaml b/tests/components/ssd1327_spi/test.rp2040-ard.yaml index d7fd6ee294..66caa956f7 100644 --- a/tests/components/ssd1327_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1327_spi/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1331_spi/common.yaml b/tests/components/ssd1331_spi/common.yaml index 40726101e8..2b708e9bd9 100644 --- a/tests/components/ssd1331_spi/common.yaml +++ b/tests/components/ssd1331_spi/common.yaml @@ -1,23 +1,18 @@ -spi: - - id: spi_ssd1331_spi - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: ssd1331_spi cs_pin: ${cs_pin} dc_pin: ${dc_pin} reset_pin: ${reset_pin} pages: - - id: page1 + - id: ssd1331_spi_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: ssd1331_spi_page2 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); on_page_change: - from: page1 - to: page2 + from: ssd1331_spi_page1 + to: ssd1331_spi_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml index c5c932c92c..b112cf4c31 100644 --- a/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.esp32-idf.yaml b/tests/components/ssd1331_spi/test.esp32-idf.yaml index bad5241f79..ff174a4656 100644 --- a/tests/components/ssd1331_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1331_spi/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.esp8266-ard.yaml b/tests/components/ssd1331_spi/test.esp8266-ard.yaml index 3f023a60eb..56cb29f29e 100644 --- a/tests/components/ssd1331_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1331_spi/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.rp2040-ard.yaml b/tests/components/ssd1331_spi/test.rp2040-ard.yaml index d7fd6ee294..66caa956f7 100644 --- a/tests/components/ssd1331_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1331_spi/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1351_spi/common.yaml b/tests/components/ssd1351_spi/common.yaml index fa495ff0c3..e4d3d55f3f 100644 --- a/tests/components/ssd1351_spi/common.yaml +++ b/tests/components/ssd1351_spi/common.yaml @@ -1,8 +1,3 @@ -spi: - - id: spi_ssd1351_spi - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: ssd1351_spi model: "SSD1351 128x128" @@ -10,15 +5,15 @@ display: dc_pin: ${dc_pin} reset_pin: ${reset_pin} pages: - - id: page1 + - id: ssd1351_spi_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: ssd1351_spi_page2 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); on_page_change: - from: page1 - to: page2 + from: ssd1351_spi_page1 + to: ssd1351_spi_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml index c5c932c92c..b112cf4c31 100644 --- a/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.esp32-idf.yaml b/tests/components/ssd1351_spi/test.esp32-idf.yaml index bad5241f79..ff174a4656 100644 --- a/tests/components/ssd1351_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1351_spi/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.esp8266-ard.yaml b/tests/components/ssd1351_spi/test.esp8266-ard.yaml index 3f023a60eb..56cb29f29e 100644 --- a/tests/components/ssd1351_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1351_spi/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.rp2040-ard.yaml b/tests/components/ssd1351_spi/test.rp2040-ard.yaml index d7fd6ee294..66caa956f7 100644 --- a/tests/components/ssd1351_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1351_spi/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/st7567_i2c/common.yaml b/tests/components/st7567_i2c/common.yaml index 41c65e5110..9a4cd79faa 100644 --- a/tests/components/st7567_i2c/common.yaml +++ b/tests/components/st7567_i2c/common.yaml @@ -1,23 +1,19 @@ -i2c: - - id: i2c_st7567_i2c - scl: ${scl_pin} - sda: ${sda_pin} - display: - platform: st7567_i2c + i2c_id: i2c_bus reset_pin: ${reset_pin} address: 0x3C id: display1 pages: - - id: page1 + - id: st7567_i2c_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: st7567_i2c_page2 lambda: |- it.rectangle(0, 0, 10, 10); on_page_change: - from: page1 - to: page2 + from: st7567_i2c_page1 + to: st7567_i2c_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/st7567_i2c/test.esp32-c3-idf.yaml b/tests/components/st7567_i2c/test.esp32-c3-idf.yaml index 4eaff7fa4a..f8bfab2319 100644 --- a/tests/components/st7567_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/st7567_i2c/test.esp32-c3-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.esp32-idf.yaml b/tests/components/st7567_i2c/test.esp32-idf.yaml index 1ca773e06c..4ff2241ec9 100644 --- a/tests/components/st7567_i2c/test.esp32-idf.yaml +++ b/tests/components/st7567_i2c/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 reset_pin: GPIO15 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.esp8266-ard.yaml b/tests/components/st7567_i2c/test.esp8266-ard.yaml index af91c21a0d..352cc8cda8 100644 --- a/tests/components/st7567_i2c/test.esp8266-ard.yaml +++ b/tests/components/st7567_i2c/test.esp8266-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO2 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.rp2040-ard.yaml b/tests/components/st7567_i2c/test.rp2040-ard.yaml index 4eaff7fa4a..2972fde8a5 100644 --- a/tests/components/st7567_i2c/test.rp2040-ard.yaml +++ b/tests/components/st7567_i2c/test.rp2040-ard.yaml @@ -1,6 +1,7 @@ substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/st7567_spi/common.yaml b/tests/components/st7567_spi/common.yaml index 037a700239..b5a4074e13 100644 --- a/tests/components/st7567_spi/common.yaml +++ b/tests/components/st7567_spi/common.yaml @@ -1,23 +1,18 @@ -spi: - - id: spi_st7567_spi - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: st7567_spi cs_pin: ${cs_pin} dc_pin: ${dc_pin} reset_pin: ${reset_pin} pages: - - id: page1 + - id: st7567_spi_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: st7567_spi_page2 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); on_page_change: - from: page1 - to: page2 + from: st7567_spi_page1 + to: st7567_spi_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/st7567_spi/test.esp32-c3-idf.yaml b/tests/components/st7567_spi/test.esp32-c3-idf.yaml index c5c932c92c..b112cf4c31 100644 --- a/tests/components/st7567_spi/test.esp32-c3-idf.yaml +++ b/tests/components/st7567_spi/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/st7567_spi/test.esp32-idf.yaml b/tests/components/st7567_spi/test.esp32-idf.yaml index bad5241f79..ff174a4656 100644 --- a/tests/components/st7567_spi/test.esp32-idf.yaml +++ b/tests/components/st7567_spi/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/st7567_spi/test.esp8266-ard.yaml b/tests/components/st7567_spi/test.esp8266-ard.yaml index 3f023a60eb..56cb29f29e 100644 --- a/tests/components/st7567_spi/test.esp8266-ard.yaml +++ b/tests/components/st7567_spi/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/st7567_spi/test.rp2040-ard.yaml b/tests/components/st7567_spi/test.rp2040-ard.yaml index d7fd6ee294..66caa956f7 100644 --- a/tests/components/st7567_spi/test.rp2040-ard.yaml +++ b/tests/components/st7567_spi/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/st7701s/common.yaml b/tests/components/st7701s/common.yaml index b94fadfe52..751862dea5 100644 --- a/tests/components/st7701s/common.yaml +++ b/tests/components/st7701s/common.yaml @@ -1,8 +1,3 @@ -spi: - - id: spi_st7701s - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: st7701s spi_mode: MODE3 diff --git a/tests/components/st7701s/test.esp32-s3-idf.yaml b/tests/components/st7701s/test.esp32-s3-idf.yaml index cd09b31f6e..87e5248888 100644 --- a/tests/components/st7701s/test.esp32-s3-idf.yaml +++ b/tests/components/st7701s/test.esp32-s3-idf.yaml @@ -1,5 +1,7 @@ +packages: + spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + substitutions: - clk_pin: GPIO41 mosi_pin: GPIO48 cs_pin: GPIO44 de_pin: GPIO18 diff --git a/tests/components/st7735/common.yaml b/tests/components/st7735/common.yaml index c140652eda..242c4bccd6 100644 --- a/tests/components/st7735/common.yaml +++ b/tests/components/st7735/common.yaml @@ -1,8 +1,3 @@ -spi: - - id: spi_st7735 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: st7735 model: INITR_18BLACKTAB @@ -14,15 +9,15 @@ display: col_start: 0 row_start: 0 pages: - - id: page1 + - id: st7735_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: st7735_page2 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); on_page_change: - from: page1 - to: page2 + from: st7735_page1 + to: st7735_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/st7735/test.esp32-c3-idf.yaml b/tests/components/st7735/test.esp32-c3-idf.yaml index c5c932c92c..b112cf4c31 100644 --- a/tests/components/st7735/test.esp32-c3-idf.yaml +++ b/tests/components/st7735/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/st7735/test.esp32-idf.yaml b/tests/components/st7735/test.esp32-idf.yaml index bad5241f79..ff174a4656 100644 --- a/tests/components/st7735/test.esp32-idf.yaml +++ b/tests/components/st7735/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/st7735/test.esp8266-ard.yaml b/tests/components/st7735/test.esp8266-ard.yaml index 3f023a60eb..56cb29f29e 100644 --- a/tests/components/st7735/test.esp8266-ard.yaml +++ b/tests/components/st7735/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/st7735/test.rp2040-ard.yaml b/tests/components/st7735/test.rp2040-ard.yaml index d7fd6ee294..66caa956f7 100644 --- a/tests/components/st7735/test.rp2040-ard.yaml +++ b/tests/components/st7735/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: dc_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/st7789v/common.yaml b/tests/components/st7789v/common.yaml index d5f74809e0..19cef5656a 100644 --- a/tests/components/st7789v/common.yaml +++ b/tests/components/st7789v/common.yaml @@ -1,24 +1,20 @@ -spi: - - id: spi_st7789v - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: st7789v model: TTGO TDisplay 135x240 cs_pin: ${cs_pin} dc_pin: ${dc_pin} reset_pin: ${reset_pin} + backlight_pin: ${backlight_pin} pages: - - id: page1 + - id: st7789v_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: st7789v_page2 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); on_page_change: - from: page1 - to: page2 + from: st7789v_page1 + to: st7789v_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/st7789v/test.esp32-c3-idf.yaml b/tests/components/st7789v/test.esp32-c3-idf.yaml index c5c932c92c..b4d70edb31 100644 --- a/tests/components/st7789v/test.esp32-c3-idf.yaml +++ b/tests/components/st7789v/test.esp32-c3-idf.yaml @@ -1,9 +1,10 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 dc_pin: GPIO9 reset_pin: GPIO10 + backlight_pin: GPIO7 + +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/st7789v/test.esp32-idf.yaml b/tests/components/st7789v/test.esp32-idf.yaml index bad5241f79..3b6d584e5c 100644 --- a/tests/components/st7789v/test.esp32-idf.yaml +++ b/tests/components/st7789v/test.esp32-idf.yaml @@ -1,8 +1,10 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 dc_pin: GPIO13 reset_pin: GPIO14 + backlight_pin: GPIO15 + +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/st7789v/test.esp8266-ard.yaml b/tests/components/st7789v/test.esp8266-ard.yaml index 3f023a60eb..0ddca66ef4 100644 --- a/tests/components/st7789v/test.esp8266-ard.yaml +++ b/tests/components/st7789v/test.esp8266-ard.yaml @@ -1,9 +1,13 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 miso_pin: GPIO12 cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 + backlight_pin: GPIO4 + +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/st7789v/test.rp2040-ard.yaml b/tests/components/st7789v/test.rp2040-ard.yaml index d7fd6ee294..464e720549 100644 --- a/tests/components/st7789v/test.rp2040-ard.yaml +++ b/tests/components/st7789v/test.rp2040-ard.yaml @@ -5,5 +5,9 @@ substitutions: cs_pin: GPIO5 dc_pin: GPIO15 reset_pin: GPIO16 + backlight_pin: GPIO6 + +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/st7920/common.yaml b/tests/components/st7920/common.yaml index 9ede271f01..477ac14bd4 100644 --- a/tests/components/st7920/common.yaml +++ b/tests/components/st7920/common.yaml @@ -1,23 +1,18 @@ -spi: - - id: spi_st7920 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: - platform: st7920 cs_pin: ${cs_pin} height: 128 width: 64 pages: - - id: page1 + - id: st7920_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 + - id: st7920_page2 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); on_page_change: - from: page1 - to: page2 + from: st7920_page1 + to: st7920_page2 then: lambda: |- ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/st7920/test.esp32-c3-idf.yaml b/tests/components/st7920/test.esp32-c3-idf.yaml index 2415ba5dc6..15d986e157 100644 --- a/tests/components/st7920/test.esp32-c3-idf.yaml +++ b/tests/components/st7920/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - miso_pin: GPIO5 cs_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/st7920/test.esp32-idf.yaml b/tests/components/st7920/test.esp32-idf.yaml index 04d2633d2b..9bb524aa65 100644 --- a/tests/components/st7920/test.esp32-idf.yaml +++ b/tests/components/st7920/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO12 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/st7920/test.esp8266-ard.yaml b/tests/components/st7920/test.esp8266-ard.yaml index bd5c203e35..1aac800592 100644 --- a/tests/components/st7920/test.esp8266-ard.yaml +++ b/tests/components/st7920/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 - miso_pin: GPIO12 - cs_pin: GPIO5 + clk_pin: GPIO0 + mosi_pin: GPIO2 + miso_pin: GPIO15 + cs_pin: GPIO16 + +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/st7920/test.rp2040-ard.yaml b/tests/components/st7920/test.rp2040-ard.yaml index f6c3f1eeca..1ded24de1c 100644 --- a/tests/components/st7920/test.rp2040-ard.yaml +++ b/tests/components/st7920/test.rp2040-ard.yaml @@ -4,4 +4,7 @@ substitutions: miso_pin: GPIO4 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sts3x/common.yaml b/tests/components/sts3x/common.yaml index 1feac4bc3f..1a61fa9212 100644 --- a/tests/components/sts3x/common.yaml +++ b/tests/components/sts3x/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sts3x - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: sts3x + i2c_id: i2c_bus id: sts3x_sensor name: STS3X Temperature address: 0x4A diff --git a/tests/components/sts3x/test.esp32-c3-idf.yaml b/tests/components/sts3x/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sts3x/test.esp32-c3-idf.yaml +++ b/tests/components/sts3x/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sts3x/test.esp32-idf.yaml b/tests/components/sts3x/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sts3x/test.esp32-idf.yaml +++ b/tests/components/sts3x/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sts3x/test.esp8266-ard.yaml b/tests/components/sts3x/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sts3x/test.esp8266-ard.yaml +++ b/tests/components/sts3x/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sts3x/test.rp2040-ard.yaml b/tests/components/sts3x/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sts3x/test.rp2040-ard.yaml +++ b/tests/components/sts3x/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/sun_gtil2/common.yaml b/tests/components/sun_gtil2/common.yaml index 15cc892d90..37fd4b056f 100644 --- a/tests/components/sun_gtil2/common.yaml +++ b/tests/components/sun_gtil2/common.yaml @@ -1,8 +1,3 @@ -uart: - - id: uart_sun_gtil2 - rx_pin: ${rx_pin} - baud_rate: 9600 - sun_gtil2: sensor: diff --git a/tests/components/sun_gtil2/test.esp32-c3-idf.yaml b/tests/components/sun_gtil2/test.esp32-c3-idf.yaml index b8a6b85616..4b7c8351a7 100644 --- a/tests/components/sun_gtil2/test.esp32-c3-idf.yaml +++ b/tests/components/sun_gtil2/test.esp32-c3-idf.yaml @@ -1,4 +1,5 @@ substitutions: - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.esp32-idf.yaml b/tests/components/sun_gtil2/test.esp32-idf.yaml index ad420099ff..29c7835b4e 100644 --- a/tests/components/sun_gtil2/test.esp32-idf.yaml +++ b/tests/components/sun_gtil2/test.esp32-idf.yaml @@ -1,4 +1,7 @@ substitutions: - rx_pin: GPIO16 + rx_pin: GPIO4 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.esp8266-ard.yaml b/tests/components/sun_gtil2/test.esp8266-ard.yaml index b8a6b85616..a591df42cb 100644 --- a/tests/components/sun_gtil2/test.esp8266-ard.yaml +++ b/tests/components/sun_gtil2/test.esp8266-ard.yaml @@ -1,4 +1,7 @@ substitutions: - rx_pin: GPIO5 + rx_pin: GPIO0 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.rp2040-ard.yaml b/tests/components/sun_gtil2/test.rp2040-ard.yaml index b8a6b85616..281ea9480e 100644 --- a/tests/components/sun_gtil2/test.rp2040-ard.yaml +++ b/tests/components/sun_gtil2/test.rp2040-ard.yaml @@ -1,4 +1,7 @@ substitutions: rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sx126x/common.yaml b/tests/components/sx126x/common.yaml index 05db2ef812..3f540a4bae 100644 --- a/tests/components/sx126x/common.yaml +++ b/tests/components/sx126x/common.yaml @@ -1,8 +1,3 @@ -spi: - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sx126x: dio1_pin: ${dio1_pin} cs_pin: ${cs_pin} diff --git a/tests/components/sx126x/test.esp32-c3-idf.yaml b/tests/components/sx126x/test.esp32-c3-idf.yaml index 91450e24ce..e27f11032e 100644 --- a/tests/components/sx126x/test.esp32-c3-idf.yaml +++ b/tests/components/sx126x/test.esp32-c3-idf.yaml @@ -1,10 +1,10 @@ substitutions: - clk_pin: GPIO5 - mosi_pin: GPIO18 - miso_pin: GPIO19 cs_pin: GPIO1 rst_pin: GPIO2 - busy_pin: GPIO4 + busy_pin: GPIO7 dio1_pin: GPIO3 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/sx126x/test.esp32-idf.yaml b/tests/components/sx126x/test.esp32-idf.yaml index 9770f52229..854638ea9c 100644 --- a/tests/components/sx126x/test.esp32-idf.yaml +++ b/tests/components/sx126x/test.esp32-idf.yaml @@ -1,10 +1,10 @@ substitutions: - clk_pin: GPIO5 - mosi_pin: GPIO27 - miso_pin: GPIO19 - cs_pin: GPIO18 - rst_pin: GPIO23 + cs_pin: GPIO12 + rst_pin: GPIO13 busy_pin: GPIO25 dio1_pin: GPIO26 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/sx126x/test.esp8266-ard.yaml b/tests/components/sx126x/test.esp8266-ard.yaml index d2c07c5bb7..98a3fbbd67 100644 --- a/tests/components/sx126x/test.esp8266-ard.yaml +++ b/tests/components/sx126x/test.esp8266-ard.yaml @@ -1,10 +1,13 @@ substitutions: - clk_pin: GPIO5 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO15 + miso_pin: GPIO16 cs_pin: GPIO1 rst_pin: GPIO2 busy_pin: GPIO4 dio1_pin: GPIO3 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sx126x/test.rp2040-ard.yaml b/tests/components/sx126x/test.rp2040-ard.yaml index 8881e96971..9bc12a370d 100644 --- a/tests/components/sx126x/test.rp2040-ard.yaml +++ b/tests/components/sx126x/test.rp2040-ard.yaml @@ -7,4 +7,7 @@ substitutions: busy_pin: GPIO8 dio1_pin: GPIO7 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sx127x/common.yaml b/tests/components/sx127x/common.yaml index 63adc2e91c..540381fc08 100644 --- a/tests/components/sx127x/common.yaml +++ b/tests/components/sx127x/common.yaml @@ -1,8 +1,3 @@ -spi: - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - sx127x: cs_pin: ${cs_pin} rst_pin: ${rst_pin} diff --git a/tests/components/sx127x/test.esp32-c3-idf.yaml b/tests/components/sx127x/test.esp32-c3-idf.yaml index 36535a950d..dfee192545 100644 --- a/tests/components/sx127x/test.esp32-c3-idf.yaml +++ b/tests/components/sx127x/test.esp32-c3-idf.yaml @@ -1,9 +1,8 @@ substitutions: - clk_pin: GPIO5 - mosi_pin: GPIO18 - miso_pin: GPIO19 cs_pin: GPIO1 rst_pin: GPIO2 dio0_pin: GPIO3 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sx127x/test.esp32-idf.yaml b/tests/components/sx127x/test.esp32-idf.yaml index 71270462a2..c9d58bb27e 100644 --- a/tests/components/sx127x/test.esp32-idf.yaml +++ b/tests/components/sx127x/test.esp32-idf.yaml @@ -1,9 +1,9 @@ substitutions: - clk_pin: GPIO5 - mosi_pin: GPIO27 - miso_pin: GPIO19 - cs_pin: GPIO18 - rst_pin: GPIO23 + cs_pin: GPIO12 + rst_pin: GPIO13 dio0_pin: GPIO26 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/sx127x/test.esp8266-ard.yaml b/tests/components/sx127x/test.esp8266-ard.yaml index 64c01edd44..d58166137c 100644 --- a/tests/components/sx127x/test.esp8266-ard.yaml +++ b/tests/components/sx127x/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO5 - mosi_pin: GPIO13 - miso_pin: GPIO12 + clk_pin: GPIO0 + mosi_pin: GPIO15 + miso_pin: GPIO16 cs_pin: GPIO1 rst_pin: GPIO2 dio0_pin: GPIO3 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sx127x/test.rp2040-ard.yaml b/tests/components/sx127x/test.rp2040-ard.yaml index 0af7b29790..09a9b3203b 100644 --- a/tests/components/sx127x/test.rp2040-ard.yaml +++ b/tests/components/sx127x/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: rst_pin: GPIO6 dio0_pin: GPIO7 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/sx1509/common.yaml b/tests/components/sx1509/common.yaml index a83217e579..cf7e234f09 100644 --- a/tests/components/sx1509/common.yaml +++ b/tests/components/sx1509/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_sx1509 - scl: ${scl_pin} - sda: ${sda_pin} - sx1509: - id: sx1509_hub + i2c_id: i2c_bus address: 0x3E keypad: key_rows: 2 diff --git a/tests/components/sx1509/test.esp32-c3-idf.yaml b/tests/components/sx1509/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/sx1509/test.esp32-c3-idf.yaml +++ b/tests/components/sx1509/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/sx1509/test.esp32-idf.yaml b/tests/components/sx1509/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/sx1509/test.esp32-idf.yaml +++ b/tests/components/sx1509/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/sx1509/test.esp8266-ard.yaml b/tests/components/sx1509/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/sx1509/test.esp8266-ard.yaml +++ b/tests/components/sx1509/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/sx1509/test.rp2040-ard.yaml b/tests/components/sx1509/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/sx1509/test.rp2040-ard.yaml +++ b/tests/components/sx1509/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/syslog/test.host.yaml b/tests/components/syslog/test.host.yaml index e735c37e4d..31122437d5 100644 --- a/tests/components/syslog/test.host.yaml +++ b/tests/components/syslog/test.host.yaml @@ -1,4 +1,11 @@ -packages: - common: !include common.yaml +udp: + addresses: ["239.0.60.53"] -wifi: !remove +time: + platform: host + +syslog: + port: 514 + strip: true + level: info + facility: 16 diff --git a/tests/components/t6615/common.yaml b/tests/components/t6615/common.yaml index 3ad715ae2b..4317130461 100644 --- a/tests/components/t6615/common.yaml +++ b/tests/components/t6615/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_t6615 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 19200 - sensor: - platform: t6615 co2: diff --git a/tests/components/t6615/test.esp32-c3-idf.yaml b/tests/components/t6615/test.esp32-c3-idf.yaml index b516342f3b..147d967dd4 100644 --- a/tests/components/t6615/test.esp32-c3-idf.yaml +++ b/tests/components/t6615/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/t6615/test.esp32-idf.yaml b/tests/components/t6615/test.esp32-idf.yaml index f486544afa..4bb9e7fff6 100644 --- a/tests/components/t6615/test.esp32-idf.yaml +++ b/tests/components/t6615/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/t6615/test.esp8266-ard.yaml b/tests/components/t6615/test.esp8266-ard.yaml index b516342f3b..1dd8409590 100644 --- a/tests/components/t6615/test.esp8266-ard.yaml +++ b/tests/components/t6615/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/t6615/test.rp2040-ard.yaml b/tests/components/t6615/test.rp2040-ard.yaml index b516342f3b..f4dada6605 100644 --- a/tests/components/t6615/test.rp2040-ard.yaml +++ b/tests/components/t6615/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/tc74/common.yaml b/tests/components/tc74/common.yaml index 88f5c91e12..c2430ee2d4 100644 --- a/tests/components/tc74/common.yaml +++ b/tests/components/tc74/common.yaml @@ -1,8 +1,4 @@ -i2c: - - id: i2c_tc74 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: tc74 + i2c_id: i2c_bus name: TC74 Temperature diff --git a/tests/components/tc74/test.esp32-c3-idf.yaml b/tests/components/tc74/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tc74/test.esp32-c3-idf.yaml +++ b/tests/components/tc74/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tc74/test.esp32-idf.yaml b/tests/components/tc74/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tc74/test.esp32-idf.yaml +++ b/tests/components/tc74/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tc74/test.esp8266-ard.yaml b/tests/components/tc74/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tc74/test.esp8266-ard.yaml +++ b/tests/components/tc74/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tc74/test.rp2040-ard.yaml b/tests/components/tc74/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tc74/test.rp2040-ard.yaml +++ b/tests/components/tc74/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tca9548a/common.yaml b/tests/components/tca9548a/common.yaml index 67e812e08b..bfaf1e26fb 100644 --- a/tests/components/tca9548a/common.yaml +++ b/tests/components/tca9548a/common.yaml @@ -1,15 +1,10 @@ -i2c: - - id: i2c_tca9548a - scl: ${scl_pin} - sda: ${sda_pin} - tca9548a: - id: multiplex0 + i2c_id: i2c_bus address: 0x70 channels: - bus_id: multiplex0_chan0 channel: 0 - i2c_id: i2c_tca9548a - id: multiplex1 + i2c_id: i2c_bus address: 0x71 - i2c_id: multiplex0_chan0 diff --git a/tests/components/tca9548a/test.esp32-c3-idf.yaml b/tests/components/tca9548a/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tca9548a/test.esp32-c3-idf.yaml +++ b/tests/components/tca9548a/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tca9548a/test.esp32-idf.yaml b/tests/components/tca9548a/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tca9548a/test.esp32-idf.yaml +++ b/tests/components/tca9548a/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tca9548a/test.esp8266-ard.yaml b/tests/components/tca9548a/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tca9548a/test.esp8266-ard.yaml +++ b/tests/components/tca9548a/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tca9548a/test.rp2040-ard.yaml b/tests/components/tca9548a/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tca9548a/test.rp2040-ard.yaml +++ b/tests/components/tca9548a/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tca9555/common.yaml b/tests/components/tca9555/common.yaml index 0fc3086786..82b4c959d8 100644 --- a/tests/components/tca9555/common.yaml +++ b/tests/components/tca9555/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_tca9555 - scl: ${scl_pin} - sda: ${sda_pin} - tca9555: - id: tca9555_hub + i2c_id: i2c_bus address: 0x21 binary_sensor: diff --git a/tests/components/tca9555/test.esp32-c3-idf.yaml b/tests/components/tca9555/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tca9555/test.esp32-c3-idf.yaml +++ b/tests/components/tca9555/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tca9555/test.esp32-idf.yaml b/tests/components/tca9555/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tca9555/test.esp32-idf.yaml +++ b/tests/components/tca9555/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tca9555/test.esp8266-ard.yaml b/tests/components/tca9555/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tca9555/test.esp8266-ard.yaml +++ b/tests/components/tca9555/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tca9555/test.rp2040-ard.yaml b/tests/components/tca9555/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tca9555/test.rp2040-ard.yaml +++ b/tests/components/tca9555/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tcs34725/common.yaml b/tests/components/tcs34725/common.yaml index 5296988fa5..e16862035e 100644 --- a/tests/components/tcs34725/common.yaml +++ b/tests/components/tcs34725/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_tcs34725 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: tcs34725 + i2c_id: i2c_bus red_channel: name: Red Channel green_channel: diff --git a/tests/components/tcs34725/test.esp32-c3-idf.yaml b/tests/components/tcs34725/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tcs34725/test.esp32-c3-idf.yaml +++ b/tests/components/tcs34725/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tcs34725/test.esp32-idf.yaml b/tests/components/tcs34725/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tcs34725/test.esp32-idf.yaml +++ b/tests/components/tcs34725/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tcs34725/test.esp8266-ard.yaml b/tests/components/tcs34725/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tcs34725/test.esp8266-ard.yaml +++ b/tests/components/tcs34725/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tcs34725/test.rp2040-ard.yaml b/tests/components/tcs34725/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tcs34725/test.rp2040-ard.yaml +++ b/tests/components/tcs34725/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tee501/common.yaml b/tests/components/tee501/common.yaml index c01ab7e37a..82091faccf 100644 --- a/tests/components/tee501/common.yaml +++ b/tests/components/tee501/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tee501 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: tee501 + i2c_id: i2c_bus name: TEE501 Temperature address: 0x48 diff --git a/tests/components/tee501/test.esp32-c3-idf.yaml b/tests/components/tee501/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tee501/test.esp32-c3-idf.yaml +++ b/tests/components/tee501/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tee501/test.esp32-idf.yaml b/tests/components/tee501/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tee501/test.esp32-idf.yaml +++ b/tests/components/tee501/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tee501/test.esp8266-ard.yaml b/tests/components/tee501/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tee501/test.esp8266-ard.yaml +++ b/tests/components/tee501/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tee501/test.rp2040-ard.yaml b/tests/components/tee501/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tee501/test.rp2040-ard.yaml +++ b/tests/components/tee501/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/teleinfo/common.yaml b/tests/components/teleinfo/common.yaml index 90b684e977..dcb2cb14bd 100644 --- a/tests/components/teleinfo/common.yaml +++ b/tests/components/teleinfo/common.yaml @@ -1,10 +1,3 @@ -uart: - - id: uart_teleinfo - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 1200 - parity: EVEN - button: - platform: template name: Poller component suspend test diff --git a/tests/components/teleinfo/test.esp32-c3-idf.yaml b/tests/components/teleinfo/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/teleinfo/test.esp32-c3-idf.yaml +++ b/tests/components/teleinfo/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/teleinfo/test.esp32-idf.yaml b/tests/components/teleinfo/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/teleinfo/test.esp32-idf.yaml +++ b/tests/components/teleinfo/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/teleinfo/test.esp8266-ard.yaml b/tests/components/teleinfo/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/teleinfo/test.esp8266-ard.yaml +++ b/tests/components/teleinfo/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/teleinfo/test.rp2040-ard.yaml b/tests/components/teleinfo/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/teleinfo/test.rp2040-ard.yaml +++ b/tests/components/teleinfo/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/tem3200/common.yaml b/tests/components/tem3200/common.yaml index 392c853bf4..cef7317450 100644 --- a/tests/components/tem3200/common.yaml +++ b/tests/components/tem3200/common.yaml @@ -1,13 +1,7 @@ -i2c: - id: i2c_bus - scl: ${scl_pin} - sda: ${sda_pin} - frequency: 200kHz - sensor: - platform: tem3200 - update_interval: 1s i2c_id: i2c_bus + update_interval: 1s temperature: name: water temperature diff --git a/tests/components/tem3200/test.esp32-idf.yaml b/tests/components/tem3200/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/tem3200/test.esp32-idf.yaml +++ b/tests/components/tem3200/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tem3200/test.esp32-s3-idf.yaml b/tests/components/tem3200/test.esp32-s3-idf.yaml index 4942e3c2b3..e9d826aa7c 100644 --- a/tests/components/tem3200/test.esp32-s3-idf.yaml +++ b/tests/components/tem3200/test.esp32-s3-idf.yaml @@ -2,4 +2,7 @@ substitutions: scl_pin: GPIO40 sda_pin: GPIO41 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/tem3200/test.esp8266-ard.yaml b/tests/components/tem3200/test.esp8266-ard.yaml index 3be5e53dcb..4a98b9388a 100644 --- a/tests/components/tem3200/test.esp8266-ard.yaml +++ b/tests/components/tem3200/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO05 - sda_pin: GPIO04 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/template/common-base.yaml b/tests/components/template/common-base.yaml new file mode 100644 index 0000000000..48537d21bc --- /dev/null +++ b/tests/components/template/common-base.yaml @@ -0,0 +1,342 @@ +esphome: + on_boot: + - sensor.template.publish: + id: template_sens + state: 42.0 + + # Templated + - sensor.template.publish: + id: template_sens + state: !lambda "return 42.0;" + + - datetime.date.set: + id: test_date + date: + year: 2021 + month: 1 + day: 1 + - datetime.date.set: + id: test_date + date: !lambda "return {.day_of_month = 1, .month = 1, .year = 2021};" + - datetime.date.set: + id: test_date + date: "2021-01-01" + +binary_sensor: + - platform: template + id: some_binary_sensor + name: "Garage Door Open" + lambda: |- + if (id(template_sens).state > 30) { + // Garage Door is open. + return true; + } else { + // Garage Door is closed. + return false; + } + - platform: template + id: other_binary_sensor + name: "Garage Door Closed" + condition: + sensor.in_range: + id: template_sens + below: 30.0 + filters: + - invert: + - delayed_on: 100ms + - delayed_off: 100ms + - delayed_on_off: !lambda "if (id(test_switch).state) return 1000; else return 0;" + - delayed_on_off: + time_on: 10s + time_off: !lambda "if (id(test_switch).state) return 1000; else return 0;" + - autorepeat: + - delay: 1s + time_off: 100ms + time_on: 900ms + - delay: 5s + time_off: 100ms + time_on: 400ms + - lambda: |- + if (id(other_binary_sensor).state) { + return x; + } else { + return {}; + } + - settle: 500ms + - timeout: 5s + +sensor: + - platform: template + name: "Template Sensor" + id: template_sens + lambda: |- + if (id(some_binary_sensor).state) { + return 42.0; + } else { + return 0.0; + } + update_interval: 60s + filters: + - calibrate_linear: + - 0.0 -> 0.0 + - 40.0 -> 45.0 + - 100.0 -> 102.5 + - calibrate_polynomial: + degree: 2 + datapoints: + # Map 0.0 (from sensor) to 0.0 (true value) + - 0.0 -> 0.0 + - 10.0 -> 12.1 + - 13.0 -> 14.0 + - clamp: + max_value: 10.0 + min_value: -10.0 + - debounce: 0.1s + - delta: 5.0 + - exponential_moving_average: + alpha: 0.1 + send_every: 15 + - filter_out: + - 10 + - 20 + - !lambda return 10; + - filter_out: 10 + - filter_out: !lambda return NAN; + - heartbeat: 5s + - lambda: return x * (9.0/5.0) + 32.0; + - max: + window_size: 10 + send_every: 2 + send_first_at: 1 + - median: + window_size: 7 + send_every: 4 + send_first_at: 3 + - min: + window_size: 10 + send_every: 2 + send_first_at: 1 + - multiply: 1 + - multiply: !lambda return 2; + - offset: 10 + - offset: !lambda return 10; + - or: + - quantile: + window_size: 7 + send_every: 4 + send_first_at: 3 + quantile: .9 + - round: 1 + - round_to_multiple_of: 0.25 + - skip_initial: 3 + - sliding_window_moving_average: + window_size: 15 + send_every: 15 + - throttle: 1s + - throttle_average: 2s + - throttle_with_priority: 5s + - throttle_with_priority: + timeout: 3s + value: 42.0 + - throttle_with_priority: + timeout: 3s + value: !lambda return 1.0f / 2.0f; + - throttle_with_priority: + timeout: 3s + value: + - 42.0 + - !lambda return 2.0f / 2.0f; + - nan + - timeout: + timeout: 10s + value: !lambda return 10; + - timeout: + timeout: 1h + value: 20.0 + - timeout: + timeout: 1min + value: last + - timeout: + timeout: 1d + - to_ntc_resistance: + calibration: + - 10.0kOhm -> 25°C + - 27.219kOhm -> 0°C + - 14.674kOhm -> 15°C + - to_ntc_temperature: + calibration: + - 10.0kOhm -> 25°C + - 27.219kOhm -> 0°C + - 14.674kOhm -> 15°C + +output: + - platform: template + id: outputsplit + type: float + write_action: + - logger.log: "write_action" + +switch: + - platform: template + id: test_switch + name: "Template Switch" + lambda: |- + if (id(some_binary_sensor).state) { + return true; + } else { + return false; + } + turn_on_action: + - logger.log: "turn_on_action" + turn_off_action: + - logger.log: "turn_off_action" + +button: + - platform: template + name: "Template Button" + on_press: + - logger.log: Button Pressed + +cover: + - platform: template + name: "Template Cover" + lambda: |- + if (id(some_binary_sensor).state) { + return COVER_OPEN; + } else { + return COVER_CLOSED; + } + open_action: + - logger.log: open_action + close_action: + - logger.log: close_action + stop_action: + - logger.log: stop_action + optimistic: true + +number: + - platform: template + name: "Template number" + optimistic: true + min_value: 0 + max_value: 100 + step: 1 + +select: + - platform: template + name: "Template select" + optimistic: true + options: + - one + - two + - three + initial_option: two + +lock: + - platform: template + name: "Template Lock" + lambda: |- + if (id(some_binary_sensor).state) { + return LOCK_STATE_LOCKED; + } else { + return LOCK_STATE_UNLOCKED; + } + lock_action: + - logger.log: lock_action + unlock_action: + - logger.log: unlock_action + open_action: + - logger.log: open_action + +valve: + - platform: template + id: template_valve + name: "Template Valve" + lambda: |- + if (id(some_binary_sensor).state) { + return VALVE_OPEN; + } else { + return VALVE_CLOSED; + } + open_action: + - logger.log: open_action + close_action: + - logger.log: close_action + - valve.template.publish: + id: template_valve + state: CLOSED + stop_action: + - logger.log: stop_action + optimistic: true + +text: + - platform: template + name: "Template text" + optimistic: true + min_length: 0 + max_length: 100 + mode: text + - platform: template + name: "Template text lambda" + mode: text + update_interval: 1s + lambda: | + return std::string{"Hello!"}; + set_action: + then: + - logger.log: + format: Template Text set to %s + args: ["x.c_str()"] + +alarm_control_panel: + - platform: template + name: Alarm Panel + codes: + - "1234" + +datetime: + - platform: template + name: Date + id: test_date + type: date + initial_value: "2000-1-2" + set_action: + - logger.log: "set_value" + on_value: + - logger.log: + format: "Date: %04d-%02d-%02d" + args: + - x.year + - x.month + - x.day_of_month + - platform: template + name: Time + id: test_time + type: time + initial_value: "12:34:56am" + set_action: + - logger.log: "set_value" + on_value: + - logger.log: + format: "Time: %02d:%02d:%02d" + args: + - x.hour + - x.minute + - x.second + - platform: template + name: DateTime + id: test_datetime + type: datetime + initial_value: "2000-1-2 12:34:56" + set_action: + - logger.log: "set_value" + on_value: + - logger.log: + format: "DateTime: %04d-%02d-%02d %02d:%02d:%02d" + args: + - x.year + - x.month + - x.day_of_month + - x.hour + - x.minute + - x.second diff --git a/tests/components/template/common.yaml b/tests/components/template/common.yaml index efbb83ee06..d06f3ce131 100644 --- a/tests/components/template/common.yaml +++ b/tests/components/template/common.yaml @@ -1,343 +1,4 @@ -esphome: - on_boot: - - sensor.template.publish: - id: template_sens - state: 42.0 - - # Templated - - sensor.template.publish: - id: template_sens - state: !lambda "return 42.0;" - - - datetime.date.set: - id: test_date - date: - year: 2021 - month: 1 - day: 1 - - datetime.date.set: - id: test_date - date: !lambda "return {.day_of_month = 1, .month = 1, .year = 2021};" - - datetime.date.set: - id: test_date - date: "2021-01-01" - -binary_sensor: - - platform: template - id: some_binary_sensor - name: "Garage Door Open" - lambda: |- - if (id(template_sens).state > 30) { - // Garage Door is open. - return true; - } else { - // Garage Door is closed. - return false; - } - - platform: template - id: other_binary_sensor - name: "Garage Door Closed" - condition: - sensor.in_range: - id: template_sens - below: 30.0 - filters: - - invert: - - delayed_on: 100ms - - delayed_off: 100ms - - delayed_on_off: !lambda "if (id(test_switch).state) return 1000; else return 0;" - - delayed_on_off: - time_on: 10s - time_off: !lambda "if (id(test_switch).state) return 1000; else return 0;" - - autorepeat: - - delay: 1s - time_off: 100ms - time_on: 900ms - - delay: 5s - time_off: 100ms - time_on: 400ms - - lambda: |- - if (id(other_binary_sensor).state) { - return x; - } else { - return {}; - } - - settle: 500ms - - timeout: 5s - -sensor: - - platform: template - name: "Template Sensor" - id: template_sens - lambda: |- - if (id(some_binary_sensor).state) { - return 42.0; - } else { - return 0.0; - } - update_interval: 60s - filters: - - calibrate_linear: - - 0.0 -> 0.0 - - 40.0 -> 45.0 - - 100.0 -> 102.5 - - calibrate_polynomial: - degree: 2 - datapoints: - # Map 0.0 (from sensor) to 0.0 (true value) - - 0.0 -> 0.0 - - 10.0 -> 12.1 - - 13.0 -> 14.0 - - clamp: - max_value: 10.0 - min_value: -10.0 - - debounce: 0.1s - - delta: 5.0 - - exponential_moving_average: - alpha: 0.1 - send_every: 15 - - filter_out: - - 10 - - 20 - - !lambda return 10; - - filter_out: 10 - - filter_out: !lambda return NAN; - - heartbeat: 5s - - lambda: return x * (9.0/5.0) + 32.0; - - max: - window_size: 10 - send_every: 2 - send_first_at: 1 - - median: - window_size: 7 - send_every: 4 - send_first_at: 3 - - min: - window_size: 10 - send_every: 2 - send_first_at: 1 - - multiply: 1 - - multiply: !lambda return 2; - - offset: 10 - - offset: !lambda return 10; - - or: - - quantile: - window_size: 7 - send_every: 4 - send_first_at: 3 - quantile: .9 - - round: 1 - - round_to_multiple_of: 0.25 - - skip_initial: 3 - - sliding_window_moving_average: - window_size: 15 - send_every: 15 - - throttle: 1s - - throttle_average: 2s - - throttle_with_priority: 5s - - throttle_with_priority: - timeout: 3s - value: 42.0 - - throttle_with_priority: - timeout: 3s - value: !lambda return 1.0f / 2.0f; - - throttle_with_priority: - timeout: 3s - value: - - 42.0 - - !lambda return 2.0f / 2.0f; - - nan - - timeout: - timeout: 10s - value: !lambda return 10; - - timeout: - timeout: 1h - value: 20.0 - - timeout: - timeout: 1min - value: last - - timeout: - timeout: 1d - - to_ntc_resistance: - calibration: - - 10.0kOhm -> 25°C - - 27.219kOhm -> 0°C - - 14.674kOhm -> 15°C - - to_ntc_temperature: - calibration: - - 10.0kOhm -> 25°C - - 27.219kOhm -> 0°C - - 14.674kOhm -> 15°C - -output: - - platform: template - id: outputsplit - type: float - write_action: - - logger.log: "write_action" - -switch: - - platform: template - id: test_switch - name: "Template Switch" - lambda: |- - if (id(some_binary_sensor).state) { - return true; - } else { - return false; - } - turn_on_action: - - logger.log: "turn_on_action" - turn_off_action: - - logger.log: "turn_off_action" - -button: - - platform: template - name: "Template Button" - on_press: - - logger.log: Button Pressed - -cover: - - platform: template - name: "Template Cover" - lambda: |- - if (id(some_binary_sensor).state) { - return COVER_OPEN; - } else { - return COVER_CLOSED; - } - open_action: - - logger.log: open_action - close_action: - - logger.log: close_action - stop_action: - - logger.log: stop_action - optimistic: true - -number: - - platform: template - name: "Template number" - optimistic: true - min_value: 0 - max_value: 100 - step: 1 - -select: - - platform: template - name: "Template select" - optimistic: true - options: - - one - - two - - three - initial_option: two - -lock: - - platform: template - name: "Template Lock" - lambda: |- - if (id(some_binary_sensor).state) { - return LOCK_STATE_LOCKED; - } else { - return LOCK_STATE_UNLOCKED; - } - lock_action: - - logger.log: lock_action - unlock_action: - - logger.log: unlock_action - open_action: - - logger.log: open_action - -valve: - - platform: template - name: "Template Valve" - lambda: |- - if (id(some_binary_sensor).state) { - return VALVE_OPEN; - } else { - return VALVE_CLOSED; - } - open_action: - - logger.log: open_action - close_action: - - logger.log: close_action - - valve.template.publish: - state: CLOSED - stop_action: - - logger.log: stop_action - optimistic: true - -text: - - platform: template - name: "Template text" - optimistic: true - min_length: 0 - max_length: 100 - mode: text - - platform: template - name: "Template text lambda" - mode: text - update_interval: 1s - lambda: | - return std::string{"Hello!"}; - set_action: - then: - - logger.log: - format: Template Text set to %s - args: ["x.c_str()"] - -alarm_control_panel: - - platform: template - name: Alarm Panel - codes: - - "1234" - -datetime: - - platform: template - name: Date - id: test_date - type: date - initial_value: "2000-1-2" - set_action: - - logger.log: "set_value" - on_value: - - logger.log: - format: "Date: %04d-%02d-%02d" - args: - - x.year - - x.month - - x.day_of_month - - platform: template - name: Time - id: test_time - type: time - initial_value: "12:34:56am" - set_action: - - logger.log: "set_value" - on_value: - - logger.log: - format: "Time: %02d:%02d:%02d" - args: - - x.hour - - x.minute - - x.second - - platform: template - name: DateTime - id: test_datetime - type: datetime - initial_value: "2000-1-2 12:34:56" - set_action: - - logger.log: "set_value" - on_value: - - logger.log: - format: "DateTime: %04d-%02d-%02d %02d:%02d:%02d" - args: - - x.year - - x.month - - x.day_of_month - - x.hour - - x.minute - - x.second +<<: !include common-base.yaml time: - platform: sntp # Required for datetime diff --git a/tests/components/template/test.nrf52-adafruit.yaml b/tests/components/template/test.nrf52-adafruit.yaml index 6a8c01560a..45751c6398 100644 --- a/tests/components/template/test.nrf52-adafruit.yaml +++ b/tests/components/template/test.nrf52-adafruit.yaml @@ -1,6 +1 @@ -packages: !include common.yaml - -time: - - id: !remove sntp_time - -wifi: !remove +<<: !include common-base.yaml diff --git a/tests/components/template/test.nrf52-mcumgr.yaml b/tests/components/template/test.nrf52-mcumgr.yaml index 6a8c01560a..45751c6398 100644 --- a/tests/components/template/test.nrf52-mcumgr.yaml +++ b/tests/components/template/test.nrf52-mcumgr.yaml @@ -1,6 +1 @@ -packages: !include common.yaml - -time: - - id: !remove sntp_time - -wifi: !remove +<<: !include common-base.yaml diff --git a/tests/components/tlc59208f/common.yaml b/tests/components/tlc59208f/common.yaml index 49460dcefc..1943063347 100644 --- a/tests/components/tlc59208f/common.yaml +++ b/tests/components/tlc59208f/common.yaml @@ -1,15 +1,13 @@ -i2c: - - id: i2c_tlc59208f - scl: ${scl_pin} - sda: ${sda_pin} - tlc59208f: - address: 0x20 id: tlc59208f_1 + i2c_id: i2c_bus - address: 0x22 id: tlc59208f_2 + i2c_id: i2c_bus - address: 0x24 id: tlc59208f_3 + i2c_id: i2c_bus output: - platform: tlc59208f diff --git a/tests/components/tlc59208f/test.esp32-c3-idf.yaml b/tests/components/tlc59208f/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tlc59208f/test.esp32-c3-idf.yaml +++ b/tests/components/tlc59208f/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tlc59208f/test.esp32-idf.yaml b/tests/components/tlc59208f/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tlc59208f/test.esp32-idf.yaml +++ b/tests/components/tlc59208f/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tlc59208f/test.esp8266-ard.yaml b/tests/components/tlc59208f/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tlc59208f/test.esp8266-ard.yaml +++ b/tests/components/tlc59208f/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tlc59208f/test.rp2040-ard.yaml b/tests/components/tlc59208f/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tlc59208f/test.rp2040-ard.yaml +++ b/tests/components/tlc59208f/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tlc5947/test.esp8266-ard.yaml b/tests/components/tlc5947/test.esp8266-ard.yaml index 44da5a07b3..1eb9bcfae2 100644 --- a/tests/components/tlc5947/test.esp8266-ard.yaml +++ b/tests/components/tlc5947/test.esp8266-ard.yaml @@ -1,7 +1,7 @@ substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - lat_pin: GPIO13 + clock_pin: GPIO0 + data_pin: GPIO2 + lat_pin: GPIO15 packages: common: !include common.yaml diff --git a/tests/components/tlc5971/test.esp8266-ard.yaml b/tests/components/tlc5971/test.esp8266-ard.yaml index 52411bc1e9..7923874f96 100644 --- a/tests/components/tlc5971/test.esp8266-ard.yaml +++ b/tests/components/tlc5971/test.esp8266-ard.yaml @@ -1,7 +1,7 @@ substitutions: clock_pin: GPIO15 - data_pin: GPIO14 - lat_pin: GPIO13 + data_pin: GPIO0 + lat_pin: GPIO2 packages: common: !include common.yaml diff --git a/tests/components/tm1621/test.esp32-idf.yaml b/tests/components/tm1621/test.esp32-idf.yaml index 0441e4bffe..21402f3216 100644 --- a/tests/components/tm1621/test.esp32-idf.yaml +++ b/tests/components/tm1621/test.esp32-idf.yaml @@ -1,7 +1,10 @@ substitutions: - cs_pin: GPIO16 - data_pin: GPIO17 + cs_pin: GPIO4 + data_pin: GPIO5 read_pin: GPIO12 write_pin: GPIO13 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/tm1621/test.esp8266-ard.yaml b/tests/components/tm1621/test.esp8266-ard.yaml index ee7b62ce35..304ca71eb2 100644 --- a/tests/components/tm1621/test.esp8266-ard.yaml +++ b/tests/components/tm1621/test.esp8266-ard.yaml @@ -1,7 +1,10 @@ substitutions: cs_pin: GPIO15 - data_pin: GPIO14 - read_pin: GPIO12 - write_pin: GPIO13 + data_pin: GPIO0 + read_pin: GPIO2 + write_pin: GPIO16 + +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tm1621/test.rp2040-ard.yaml b/tests/components/tm1621/test.rp2040-ard.yaml index 562ced7485..9a880da876 100644 --- a/tests/components/tm1621/test.rp2040-ard.yaml +++ b/tests/components/tm1621/test.rp2040-ard.yaml @@ -1,7 +1,10 @@ substitutions: cs_pin: GPIO6 data_pin: GPIO7 - read_pin: GPIO2 - write_pin: GPIO3 + read_pin: GPIO8 + write_pin: GPIO9 + +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tm1637/test.esp32-c3-idf.yaml b/tests/components/tm1637/test.esp32-c3-idf.yaml index 96f6708a3b..0c4d4a9a7a 100644 --- a/tests/components/tm1637/test.esp32-c3-idf.yaml +++ b/tests/components/tm1637/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - clk_pin: GPIO4 + clk_pin: GPIO7 dio_pin: GPIO3 <<: !include common.yaml diff --git a/tests/components/tm1637/test.esp8266-ard.yaml b/tests/components/tm1637/test.esp8266-ard.yaml index 2c5786c47c..0a4221cbc0 100644 --- a/tests/components/tm1637/test.esp8266-ard.yaml +++ b/tests/components/tm1637/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clk_pin: GPIO14 - dio_pin: GPIO13 + clk_pin: GPIO0 + dio_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/tmp102/common.yaml b/tests/components/tmp102/common.yaml index afc4a27fad..049a8067da 100644 --- a/tests/components/tmp102/common.yaml +++ b/tests/components/tmp102/common.yaml @@ -1,8 +1,4 @@ -i2c: - - id: i2c_tmp102 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: tmp102 + i2c_id: i2c_bus name: TMP102 Temperature diff --git a/tests/components/tmp102/test.esp32-c3-idf.yaml b/tests/components/tmp102/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tmp102/test.esp32-c3-idf.yaml +++ b/tests/components/tmp102/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tmp102/test.esp32-idf.yaml b/tests/components/tmp102/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tmp102/test.esp32-idf.yaml +++ b/tests/components/tmp102/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tmp102/test.esp8266-ard.yaml b/tests/components/tmp102/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tmp102/test.esp8266-ard.yaml +++ b/tests/components/tmp102/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tmp102/test.rp2040-ard.yaml b/tests/components/tmp102/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tmp102/test.rp2040-ard.yaml +++ b/tests/components/tmp102/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tmp1075/common.yaml b/tests/components/tmp1075/common.yaml index 4c4c6c6f35..90025f231e 100644 --- a/tests/components/tmp1075/common.yaml +++ b/tests/components/tmp1075/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_tmp1075 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: tmp1075 + i2c_id: i2c_bus name: Temperature TMP1075 conversion_rate: 27.5ms alert: diff --git a/tests/components/tmp1075/test.esp32-c3-idf.yaml b/tests/components/tmp1075/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tmp1075/test.esp32-c3-idf.yaml +++ b/tests/components/tmp1075/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tmp1075/test.esp32-idf.yaml b/tests/components/tmp1075/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tmp1075/test.esp32-idf.yaml +++ b/tests/components/tmp1075/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tmp1075/test.esp8266-ard.yaml b/tests/components/tmp1075/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tmp1075/test.esp8266-ard.yaml +++ b/tests/components/tmp1075/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tmp1075/test.rp2040-ard.yaml b/tests/components/tmp1075/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tmp1075/test.rp2040-ard.yaml +++ b/tests/components/tmp1075/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tmp117/common.yaml b/tests/components/tmp117/common.yaml index f4a5688933..58419c2134 100644 --- a/tests/components/tmp117/common.yaml +++ b/tests/components/tmp117/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tmp117 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: tmp117 + i2c_id: i2c_bus name: TMP117 Temperature update_interval: 5s diff --git a/tests/components/tmp117/test.esp32-c3-idf.yaml b/tests/components/tmp117/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tmp117/test.esp32-c3-idf.yaml +++ b/tests/components/tmp117/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tmp117/test.esp32-idf.yaml b/tests/components/tmp117/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tmp117/test.esp32-idf.yaml +++ b/tests/components/tmp117/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tmp117/test.esp8266-ard.yaml b/tests/components/tmp117/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tmp117/test.esp8266-ard.yaml +++ b/tests/components/tmp117/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tmp117/test.rp2040-ard.yaml b/tests/components/tmp117/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tmp117/test.rp2040-ard.yaml +++ b/tests/components/tmp117/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tof10120/common.yaml b/tests/components/tof10120/common.yaml index 67643323d9..b360a27248 100644 --- a/tests/components/tof10120/common.yaml +++ b/tests/components/tof10120/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tof10120 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: tof10120 + i2c_id: i2c_bus name: Distance sensor update_interval: 5s diff --git a/tests/components/tof10120/test.esp32-c3-idf.yaml b/tests/components/tof10120/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tof10120/test.esp32-c3-idf.yaml +++ b/tests/components/tof10120/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tof10120/test.esp32-idf.yaml b/tests/components/tof10120/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tof10120/test.esp32-idf.yaml +++ b/tests/components/tof10120/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tof10120/test.esp8266-ard.yaml b/tests/components/tof10120/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tof10120/test.esp8266-ard.yaml +++ b/tests/components/tof10120/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tof10120/test.rp2040-ard.yaml b/tests/components/tof10120/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tof10120/test.rp2040-ard.yaml +++ b/tests/components/tof10120/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tormatic/common.yaml b/tests/components/tormatic/common.yaml index 0f1b33ac12..712bf7569b 100644 --- a/tests/components/tormatic/common.yaml +++ b/tests/components/tormatic/common.yaml @@ -1,12 +1,5 @@ -uart: - - id: uart_tormatic - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - cover: - platform: tormatic - uart_id: uart_tormatic id: tormatic_garage_door name: Tormatic Garage Door open_duration: 15s diff --git a/tests/components/tormatic/test.esp32-c3-idf.yaml b/tests/components/tormatic/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/tormatic/test.esp32-c3-idf.yaml +++ b/tests/components/tormatic/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tormatic/test.esp32-idf.yaml b/tests/components/tormatic/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/tormatic/test.esp32-idf.yaml +++ b/tests/components/tormatic/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tormatic/test.esp8266-ard.yaml b/tests/components/tormatic/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/tormatic/test.esp8266-ard.yaml +++ b/tests/components/tormatic/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tormatic/test.rp2040-ard.yaml b/tests/components/tormatic/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/tormatic/test.rp2040-ard.yaml +++ b/tests/components/tormatic/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/total_daily_energy/test.esp8266-ard.yaml b/tests/components/total_daily_energy/test.esp8266-ard.yaml index 8b42b21b54..ec9c0e43dc 100644 --- a/tests/components/total_daily_energy/test.esp8266-ard.yaml +++ b/tests/components/total_daily_energy/test.esp8266-ard.yaml @@ -1,6 +1,6 @@ substitutions: - sel_pin: GPIO12 - cf_pin: GPIO13 - cf1_pin: GPIO14 + sel_pin: GPIO0 + cf_pin: GPIO2 + cf1_pin: GPIO15 <<: !include common.yaml diff --git a/tests/components/tsl2561/common.yaml b/tests/components/tsl2561/common.yaml index d2b4f75df3..132bdb890e 100644 --- a/tests/components/tsl2561/common.yaml +++ b/tests/components/tsl2561/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_tsl2561 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: tsl2561 + i2c_id: i2c_bus name: TSL2561 Ambient Light address: 0x39 is_cs_package: true diff --git a/tests/components/tsl2561/test.esp32-c3-idf.yaml b/tests/components/tsl2561/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tsl2561/test.esp32-c3-idf.yaml +++ b/tests/components/tsl2561/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tsl2561/test.esp32-idf.yaml b/tests/components/tsl2561/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tsl2561/test.esp32-idf.yaml +++ b/tests/components/tsl2561/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tsl2561/test.esp8266-ard.yaml b/tests/components/tsl2561/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tsl2561/test.esp8266-ard.yaml +++ b/tests/components/tsl2561/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tsl2561/test.rp2040-ard.yaml b/tests/components/tsl2561/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tsl2561/test.rp2040-ard.yaml +++ b/tests/components/tsl2561/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tsl2591/common.yaml b/tests/components/tsl2591/common.yaml index d58c46fb48..93433cf968 100644 --- a/tests/components/tsl2591/common.yaml +++ b/tests/components/tsl2591/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_tsl2591 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: tsl2591 + i2c_id: i2c_bus id: test_tsl2591 address: 0x29 integration_time: 600ms diff --git a/tests/components/tsl2591/test.esp32-c3-idf.yaml b/tests/components/tsl2591/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/tsl2591/test.esp32-c3-idf.yaml +++ b/tests/components/tsl2591/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tsl2591/test.esp32-idf.yaml b/tests/components/tsl2591/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/tsl2591/test.esp32-idf.yaml +++ b/tests/components/tsl2591/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tsl2591/test.esp8266-ard.yaml b/tests/components/tsl2591/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/tsl2591/test.esp8266-ard.yaml +++ b/tests/components/tsl2591/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tsl2591/test.rp2040-ard.yaml b/tests/components/tsl2591/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/tsl2591/test.rp2040-ard.yaml +++ b/tests/components/tsl2591/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tt21100/common.yaml b/tests/components/tt21100/common.yaml index a5d7970429..bd1830ea8b 100644 --- a/tests/components/tt21100/common.yaml +++ b/tests/components/tt21100/common.yaml @@ -1,25 +1,24 @@ -i2c: - - id: i2c_tt21100 - scl: ${scl_pin} - sda: ${sda_pin} - display: - platform: ssd1306_i2c + i2c_id: i2c_bus id: ssd1306_display model: SSD1306_128X64 reset_pin: ${disp_reset_pin} pages: - - id: page1 + - id: tt21100_page1 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); touchscreen: - platform: tt21100 + i2c_id: i2c_bus + id: tt21100_touchscreen display: ssd1306_display interrupt_pin: ${interrupt_pin} reset_pin: ${reset_pin} binary_sensor: - platform: tt21100 + id: tt21100_button name: Home Button index: 1 diff --git a/tests/components/tt21100/test.esp32-c3-idf.yaml b/tests/components/tt21100/test.esp32-c3-idf.yaml index 36a8ce2778..a7265e10b2 100644 --- a/tests/components/tt21100/test.esp32-c3-idf.yaml +++ b/tests/components/tt21100/test.esp32-c3-idf.yaml @@ -1,8 +1,9 @@ substitutions: - disp_reset_pin: GPIO10 - scl_pin: GPIO0 - sda_pin: GPIO1 + disp_reset_pin: GPIO7 interrupt_pin: GPIO2 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/tt21100/test.esp32-idf.yaml b/tests/components/tt21100/test.esp32-idf.yaml index 05598719f9..033aafb73c 100644 --- a/tests/components/tt21100/test.esp32-idf.yaml +++ b/tests/components/tt21100/test.esp32-idf.yaml @@ -1,8 +1,9 @@ substitutions: disp_reset_pin: GPIO12 - scl_pin: GPIO13 - sda_pin: GPIO14 interrupt_pin: GPIO15 - reset_pin: GPIO16 + reset_pin: GPIO4 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tt21100/test.esp8266-ard.yaml b/tests/components/tt21100/test.esp8266-ard.yaml index 05598719f9..25d1ff82e3 100644 --- a/tests/components/tt21100/test.esp8266-ard.yaml +++ b/tests/components/tt21100/test.esp8266-ard.yaml @@ -1,8 +1,9 @@ substitutions: - disp_reset_pin: GPIO12 - scl_pin: GPIO13 - sda_pin: GPIO14 + disp_reset_pin: GPIO0 interrupt_pin: GPIO15 reset_pin: GPIO16 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/tt21100/test.rp2040-ard.yaml b/tests/components/tt21100/test.rp2040-ard.yaml index 36a8ce2778..0d13628294 100644 --- a/tests/components/tt21100/test.rp2040-ard.yaml +++ b/tests/components/tt21100/test.rp2040-ard.yaml @@ -1,8 +1,9 @@ substitutions: disp_reset_pin: GPIO10 - scl_pin: GPIO0 - sda_pin: GPIO1 interrupt_pin: GPIO2 reset_pin: GPIO3 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ttp229_bsf/common.yaml b/tests/components/ttp229_bsf/common.yaml index 42c26a5d51..91134f5098 100644 --- a/tests/components/ttp229_bsf/common.yaml +++ b/tests/components/ttp229_bsf/common.yaml @@ -1,6 +1,6 @@ ttp229_bsf: - scl_pin: ${scl_pin} - sdo_pin: ${sdo_pin} + scl_pin: ${ttp229_scl_pin} + sdo_pin: ${ttp229_sdo_pin} binary_sensor: - platform: ttp229_bsf diff --git a/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml b/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml index 135b213edc..ad1c58b40e 100644 --- a/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml +++ b/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml @@ -1,5 +1,8 @@ substitutions: - scl_pin: GPIO5 - sdo_pin: GPIO4 + ttp229_scl_pin: GPIO7 + ttp229_sdo_pin: GPIO4 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.esp32-idf.yaml b/tests/components/ttp229_bsf/test.esp32-idf.yaml index 80ed75293f..8b7f6fe6ea 100644 --- a/tests/components/ttp229_bsf/test.esp32-idf.yaml +++ b/tests/components/ttp229_bsf/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - scl_pin: GPIO16 - sdo_pin: GPIO17 + ttp229_scl_pin: GPIO14 + ttp229_sdo_pin: GPIO5 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.esp8266-ard.yaml b/tests/components/ttp229_bsf/test.esp8266-ard.yaml index 135b213edc..1832fde07f 100644 --- a/tests/components/ttp229_bsf/test.esp8266-ard.yaml +++ b/tests/components/ttp229_bsf/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - scl_pin: GPIO5 - sdo_pin: GPIO4 + ttp229_scl_pin: GPIO0 + ttp229_sdo_pin: GPIO2 + +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.rp2040-ard.yaml b/tests/components/ttp229_bsf/test.rp2040-ard.yaml index 135b213edc..1448c82347 100644 --- a/tests/components/ttp229_bsf/test.rp2040-ard.yaml +++ b/tests/components/ttp229_bsf/test.rp2040-ard.yaml @@ -1,5 +1,8 @@ substitutions: - scl_pin: GPIO5 - sdo_pin: GPIO4 + ttp229_scl_pin: GPIO2 + ttp229_sdo_pin: GPIO6 + +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ttp229_lsf/common.yaml b/tests/components/ttp229_lsf/common.yaml index 5c0dbf9517..41402f5aac 100644 --- a/tests/components/ttp229_lsf/common.yaml +++ b/tests/components/ttp229_lsf/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_ttp229_lsf - scl: ${scl_pin} - sda: ${sda_pin} - ttp229_lsf: + i2c_id: i2c_bus binary_sensor: - platform: ttp229_lsf diff --git a/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml b/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml +++ b/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.esp32-idf.yaml b/tests/components/ttp229_lsf/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ttp229_lsf/test.esp32-idf.yaml +++ b/tests/components/ttp229_lsf/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.esp8266-ard.yaml b/tests/components/ttp229_lsf/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ttp229_lsf/test.esp8266-ard.yaml +++ b/tests/components/ttp229_lsf/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.rp2040-ard.yaml b/tests/components/ttp229_lsf/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ttp229_lsf/test.rp2040-ard.yaml +++ b/tests/components/ttp229_lsf/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/tuya/common.yaml b/tests/components/tuya/common.yaml index 2c40628139..e177b7d056 100644 --- a/tests/components/tuya/common.yaml +++ b/tests/components/tuya/common.yaml @@ -2,12 +2,6 @@ wifi: ssid: MySSID password: password1 -uart: - - id: uart_tuya - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - tuya: status_pin: number: ${status_pin} diff --git a/tests/components/tuya/test.esp32-c3-idf.yaml b/tests/components/tuya/test.esp32-c3-idf.yaml index c62a0b10f6..43c28ba7b3 100644 --- a/tests/components/tuya/test.esp32-c3-idf.yaml +++ b/tests/components/tuya/test.esp32-c3-idf.yaml @@ -1,6 +1,6 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 status_pin: GPIO2 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tuya/test.esp32-idf.yaml b/tests/components/tuya/test.esp32-idf.yaml index 926a46cf73..0baa48a6c5 100644 --- a/tests/components/tuya/test.esp32-idf.yaml +++ b/tests/components/tuya/test.esp32-idf.yaml @@ -1,6 +1,9 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 status_pin: GPIO12 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/tuya/test.esp8266-ard.yaml b/tests/components/tuya/test.esp8266-ard.yaml index 11d46ed50e..1565495cf8 100644 --- a/tests/components/tuya/test.esp8266-ard.yaml +++ b/tests/components/tuya/test.esp8266-ard.yaml @@ -1,6 +1,9 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - status_pin: GPIO12 + tx_pin: GPIO0 + rx_pin: GPIO2 + status_pin: GPIO15 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tuya/test.rp2040-ard.yaml b/tests/components/tuya/test.rp2040-ard.yaml index 11d46ed50e..0c3db54644 100644 --- a/tests/components/tuya/test.rp2040-ard.yaml +++ b/tests/components/tuya/test.rp2040-ard.yaml @@ -3,4 +3,7 @@ substitutions: rx_pin: GPIO5 status_pin: GPIO12 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/udp/test.esp32-c3-idf.yaml b/tests/components/udp/test.esp32-c3-idf.yaml index dade44d145..9990d96d29 100644 --- a/tests/components/udp/test.esp32-c3-idf.yaml +++ b/tests/components/udp/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/udp/test.esp32-idf.yaml b/tests/components/udp/test.esp32-idf.yaml index dade44d145..b47e39c389 100644 --- a/tests/components/udp/test.esp32-idf.yaml +++ b/tests/components/udp/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/udp/test.esp8266-ard.yaml b/tests/components/udp/test.esp8266-ard.yaml index dade44d145..4a98b9388a 100644 --- a/tests/components/udp/test.esp8266-ard.yaml +++ b/tests/components/udp/test.esp8266-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/udp/test.host.yaml b/tests/components/udp/test.host.yaml index e735c37e4d..84e78894e5 100644 --- a/tests/components/udp/test.host.yaml +++ b/tests/components/udp/test.host.yaml @@ -1,4 +1,15 @@ -packages: - common: !include common.yaml - -wifi: !remove +udp: + id: my_udp + listen_address: 239.0.60.53 + addresses: ["239.0.60.53"] + on_receive: + - logger.log: + format: "Received %d bytes" + args: [data.size()] + - udp.write: + id: my_udp + data: "hello world" + - udp.write: + id: my_udp + data: !lambda |- + return std::vector{1,3,4,5,6}; diff --git a/tests/components/udp/test.rp2040-ard.yaml b/tests/components/udp/test.rp2040-ard.yaml index dade44d145..319a7c71a6 100644 --- a/tests/components/udp/test.rp2040-ard.yaml +++ b/tests/components/udp/test.rp2040-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/ufire_ec/common.yaml b/tests/components/ufire_ec/common.yaml index dcc957aaee..4260f0ab4c 100644 --- a/tests/components/ufire_ec/common.yaml +++ b/tests/components/ufire_ec/common.yaml @@ -7,16 +7,12 @@ esphome: temperature: !lambda "return id(test_sensor).state;" - ufire_ec.reset: -i2c: - - id: i2c_ufire_ec - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: template id: test_sensor lambda: "return 21;" - platform: ufire_ec + i2c_id: i2c_bus id: ufire_ec_board ec: name: Ufire EC diff --git a/tests/components/ufire_ec/test.esp32-c3-idf.yaml b/tests/components/ufire_ec/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ufire_ec/test.esp32-c3-idf.yaml +++ b/tests/components/ufire_ec/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ufire_ec/test.esp32-idf.yaml b/tests/components/ufire_ec/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ufire_ec/test.esp32-idf.yaml +++ b/tests/components/ufire_ec/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ufire_ec/test.esp8266-ard.yaml b/tests/components/ufire_ec/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ufire_ec/test.esp8266-ard.yaml +++ b/tests/components/ufire_ec/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ufire_ec/test.rp2040-ard.yaml b/tests/components/ufire_ec/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ufire_ec/test.rp2040-ard.yaml +++ b/tests/components/ufire_ec/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/ufire_ise/common.yaml b/tests/components/ufire_ise/common.yaml index d6ead8c479..f7865ea87b 100644 --- a/tests/components/ufire_ise/common.yaml +++ b/tests/components/ufire_ise/common.yaml @@ -9,16 +9,12 @@ esphome: solution: 4.0 - ufire_ise.reset: -i2c: - - id: i2c_ufire_ise - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: template id: test_sensor lambda: "return 21;" - platform: ufire_ise + i2c_id: i2c_bus id: ufire_ise_sensor temperature_sensor: test_sensor ph: diff --git a/tests/components/ufire_ise/test.esp32-c3-idf.yaml b/tests/components/ufire_ise/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/ufire_ise/test.esp32-c3-idf.yaml +++ b/tests/components/ufire_ise/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/ufire_ise/test.esp32-idf.yaml b/tests/components/ufire_ise/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/ufire_ise/test.esp32-idf.yaml +++ b/tests/components/ufire_ise/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/ufire_ise/test.esp8266-ard.yaml b/tests/components/ufire_ise/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/ufire_ise/test.esp8266-ard.yaml +++ b/tests/components/ufire_ise/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/ufire_ise/test.rp2040-ard.yaml b/tests/components/ufire_ise/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/ufire_ise/test.rp2040-ard.yaml +++ b/tests/components/ufire_ise/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/update/common.yaml b/tests/components/update/common.yaml index 45ed110352..521a0a6a5c 100644 --- a/tests/components/update/common.yaml +++ b/tests/components/update/common.yaml @@ -21,10 +21,12 @@ http_request: ota: - platform: http_request + id: update_http_request_ota update: - platform: http_request name: Firmware Update + ota_id: update_http_request_ota source: http://example.com/manifest.json on_update_available: - logger.log: "A new update is available" diff --git a/tests/components/uponor_smatrix/common.yaml b/tests/components/uponor_smatrix/common.yaml index 8ee92bdfc5..786a604aec 100644 --- a/tests/components/uponor_smatrix/common.yaml +++ b/tests/components/uponor_smatrix/common.yaml @@ -2,12 +2,6 @@ wifi: ssid: MySSID password: password1 -uart: - - id: uponor_uart - baud_rate: 19200 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - time: - platform: sntp id: sntp_time @@ -17,7 +11,6 @@ time: - 192.168.178.1 uponor_smatrix: - uart_id: uponor_uart address: 0x110B time_id: sntp_time time_device_address: 0xDE13 diff --git a/tests/components/uponor_smatrix/test.esp32-c3-idf.yaml b/tests/components/uponor_smatrix/test.esp32-c3-idf.yaml index b516342f3b..cd26c783c2 100644 --- a/tests/components/uponor_smatrix/test.esp32-c3-idf.yaml +++ b/tests/components/uponor_smatrix/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/esp32-c3-idf.yaml + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/uponor_smatrix/test.esp32-idf.yaml b/tests/components/uponor_smatrix/test.esp32-idf.yaml index f486544afa..76222997a8 100644 --- a/tests/components/uponor_smatrix/test.esp32-idf.yaml +++ b/tests/components/uponor_smatrix/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/uponor_smatrix/test.esp8266-ard.yaml b/tests/components/uponor_smatrix/test.esp8266-ard.yaml index b516342f3b..1f4483954b 100644 --- a/tests/components/uponor_smatrix/test.esp8266-ard.yaml +++ b/tests/components/uponor_smatrix/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/esp8266-ard.yaml + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/uponor_smatrix/test.rp2040-ard.yaml b/tests/components/uponor_smatrix/test.rp2040-ard.yaml index b516342f3b..65ba185aef 100644 --- a/tests/components/uponor_smatrix/test.rp2040-ard.yaml +++ b/tests/components/uponor_smatrix/test.rp2040-ard.yaml @@ -1,5 +1,5 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart_19200: !include ../../test_build_components/common/uart_19200/rp2040-ard.yaml + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/vbus/common.yaml b/tests/components/vbus/common.yaml index a1f94cd839..33d9e2935d 100644 --- a/tests/components/vbus/common.yaml +++ b/tests/components/vbus/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_vbus - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - vbus: binary_sensor: diff --git a/tests/components/vbus/test.esp32-c3-idf.yaml b/tests/components/vbus/test.esp32-c3-idf.yaml index b516342f3b..a19013bf54 100644 --- a/tests/components/vbus/test.esp32-c3-idf.yaml +++ b/tests/components/vbus/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/vbus/test.esp32-idf.yaml b/tests/components/vbus/test.esp32-idf.yaml index f486544afa..2d29656c94 100644 --- a/tests/components/vbus/test.esp32-idf.yaml +++ b/tests/components/vbus/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/vbus/test.esp8266-ard.yaml b/tests/components/vbus/test.esp8266-ard.yaml index b516342f3b..5a05efa259 100644 --- a/tests/components/vbus/test.esp8266-ard.yaml +++ b/tests/components/vbus/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/vbus/test.rp2040-ard.yaml b/tests/components/vbus/test.rp2040-ard.yaml index b516342f3b..f1df2daf83 100644 --- a/tests/components/vbus/test.rp2040-ard.yaml +++ b/tests/components/vbus/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/veml3235/common.yaml b/tests/components/veml3235/common.yaml index b89a9e12c7..98ffb0729c 100644 --- a/tests/components/veml3235/common.yaml +++ b/tests/components/veml3235/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_veml3235 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: veml3235 + i2c_id: i2c_bus id: veml3235_sensor name: VEML3235 Light Sensor auto_gain: true diff --git a/tests/components/veml3235/test.esp32-c3-idf.yaml b/tests/components/veml3235/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/veml3235/test.esp32-c3-idf.yaml +++ b/tests/components/veml3235/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/veml3235/test.esp32-idf.yaml b/tests/components/veml3235/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/veml3235/test.esp32-idf.yaml +++ b/tests/components/veml3235/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/veml3235/test.esp8266-ard.yaml b/tests/components/veml3235/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/veml3235/test.esp8266-ard.yaml +++ b/tests/components/veml3235/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/veml3235/test.rp2040-ard.yaml b/tests/components/veml3235/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/veml3235/test.rp2040-ard.yaml +++ b/tests/components/veml3235/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/veml7700/common.yaml b/tests/components/veml7700/common.yaml index af4ebee6e7..06c1d304c3 100644 --- a/tests/components/veml7700/common.yaml +++ b/tests/components/veml7700/common.yaml @@ -1,12 +1,7 @@ -i2c: - - id: i2c_veml7700 - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: veml7700 + i2c_id: i2c_bus address: 0x10 - i2c_id: i2c_veml7700 ambient_light: Ambient light ambient_light_counts: Ambient light counts full_spectrum: Full spectrum diff --git a/tests/components/veml7700/test.esp32-c3-idf.yaml b/tests/components/veml7700/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/veml7700/test.esp32-c3-idf.yaml +++ b/tests/components/veml7700/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/veml7700/test.esp32-idf.yaml b/tests/components/veml7700/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/veml7700/test.esp32-idf.yaml +++ b/tests/components/veml7700/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/veml7700/test.esp8266-ard.yaml b/tests/components/veml7700/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/veml7700/test.esp8266-ard.yaml +++ b/tests/components/veml7700/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/veml7700/test.rp2040-ard.yaml b/tests/components/veml7700/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/veml7700/test.rp2040-ard.yaml +++ b/tests/components/veml7700/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/vl53l0x/common.yaml b/tests/components/vl53l0x/common.yaml index 8346eae854..98277639cf 100644 --- a/tests/components/vl53l0x/common.yaml +++ b/tests/components/vl53l0x/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_vl53l0x - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: vl53l0x + i2c_id: i2c_bus name: VL53L0x Distance address: 0x29 enable_pin: 3 diff --git a/tests/components/vl53l0x/test.esp32-c3-idf.yaml b/tests/components/vl53l0x/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/vl53l0x/test.esp32-c3-idf.yaml +++ b/tests/components/vl53l0x/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/vl53l0x/test.esp32-idf.yaml b/tests/components/vl53l0x/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/vl53l0x/test.esp32-idf.yaml +++ b/tests/components/vl53l0x/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/vl53l0x/test.esp8266-ard.yaml b/tests/components/vl53l0x/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/vl53l0x/test.esp8266-ard.yaml +++ b/tests/components/vl53l0x/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/vl53l0x/test.rp2040-ard.yaml b/tests/components/vl53l0x/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/vl53l0x/test.rp2040-ard.yaml +++ b/tests/components/vl53l0x/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/voice_assistant/common-idf.yaml b/tests/components/voice_assistant/common-idf.yaml index b1d249d5b4..ab8cbf2434 100644 --- a/tests/components/voice_assistant/common-idf.yaml +++ b/tests/components/voice_assistant/common-idf.yaml @@ -18,6 +18,7 @@ i2s_audio: micro_wake_word: id: mww_id + microphone: mic_id_external on_wake_word_detected: - voice_assistant.start: wake_word: !lambda return wake_word; diff --git a/tests/components/voice_assistant/test.esp32-idf.yaml b/tests/components/voice_assistant/test.esp32-idf.yaml index 0fe5d347be..1c5c9ddf99 100644 --- a/tests/components/voice_assistant/test.esp32-idf.yaml +++ b/tests/components/voice_assistant/test.esp32-idf.yaml @@ -1,6 +1,6 @@ substitutions: - i2s_lrclk_pin: GPIO16 - i2s_bclk_pin: GPIO17 + i2s_lrclk_pin: GPIO4 + i2s_bclk_pin: GPIO5 i2s_mclk_pin: GPIO15 i2s_din_pin: GPIO13 i2s_dout_pin: GPIO12 diff --git a/tests/components/wake_on_lan/test.esp32-c3-idf.yaml b/tests/components/wake_on_lan/test.esp32-c3-idf.yaml index dade44d145..9990d96d29 100644 --- a/tests/components/wake_on_lan/test.esp32-c3-idf.yaml +++ b/tests/components/wake_on_lan/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wake_on_lan/test.esp32-idf.yaml b/tests/components/wake_on_lan/test.esp32-idf.yaml index dade44d145..b47e39c389 100644 --- a/tests/components/wake_on_lan/test.esp32-idf.yaml +++ b/tests/components/wake_on_lan/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wake_on_lan/test.esp8266-ard.yaml b/tests/components/wake_on_lan/test.esp8266-ard.yaml index dade44d145..4a98b9388a 100644 --- a/tests/components/wake_on_lan/test.esp8266-ard.yaml +++ b/tests/components/wake_on_lan/test.esp8266-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/wake_on_lan/test.rp2040-ard.yaml b/tests/components/wake_on_lan/test.rp2040-ard.yaml index dade44d145..319a7c71a6 100644 --- a/tests/components/wake_on_lan/test.rp2040-ard.yaml +++ b/tests/components/wake_on_lan/test.rp2040-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/waveshare_epaper/common.yaml b/tests/components/waveshare_epaper/common.yaml index a2aa3134b5..b80a352c1f 100644 --- a/tests/components/waveshare_epaper/common.yaml +++ b/tests/components/waveshare_epaper/common.yaml @@ -1,14 +1,8 @@ -spi: - - id: spi_waveshare_epaper - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - display: # 1.54 inch displays - platform: waveshare_epaper id: epd_1_54 model: 1.54in - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -28,7 +22,6 @@ display: - platform: waveshare_epaper id: epd_1_54v2 model: 1.54inv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -48,7 +41,6 @@ display: - platform: waveshare_epaper id: epd_1_54v2b model: 1.54inv2-b - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -67,7 +59,6 @@ display: - platform: waveshare_epaper id: epd_1_54m09 model: 1.54in-m5coreink-m09 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -87,7 +78,6 @@ display: - platform: waveshare_epaper id: epd_2_13 model: 2.13in - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -107,7 +97,6 @@ display: - platform: waveshare_epaper id: epd_2_13v2 model: 2.13inv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -127,7 +116,6 @@ display: - platform: waveshare_epaper id: epd_2_13ttgo model: 2.13in-ttgo - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -147,7 +135,6 @@ display: - platform: waveshare_epaper id: epd_2_13ttgo_b1 model: 2.13in-ttgo-b1 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -167,7 +154,6 @@ display: - platform: waveshare_epaper id: epd_2_13ttgo_b73 model: 2.13in-ttgo-b73 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -187,7 +173,6 @@ display: - platform: waveshare_epaper id: epd_2_13ttgo_b74 model: 2.13in-ttgo-b74 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -207,7 +192,6 @@ display: - platform: waveshare_epaper id: epd_2_13dke model: 2.13in-ttgo-dke - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -227,7 +211,6 @@ display: - platform: waveshare_epaper id: epd_2_13v3 model: 2.13inv3 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -248,7 +231,6 @@ display: - platform: waveshare_epaper id: epd_2_70 model: 2.70in - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -267,7 +249,6 @@ display: - platform: waveshare_epaper id: epd_2_70b model: 2.70in-b - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -286,7 +267,6 @@ display: - platform: waveshare_epaper id: epd_2_70bv2 model: 2.70in-bv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -305,7 +285,6 @@ display: - platform: waveshare_epaper id: epd_2_70v2 model: 2.70inv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -325,7 +304,6 @@ display: - platform: waveshare_epaper id: epd_2_90 model: 2.90in - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -346,7 +324,6 @@ display: - platform: waveshare_epaper id: epd_2_90v2 model: 2.90inv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -367,7 +344,6 @@ display: - platform: waveshare_epaper id: epd_2_90b model: 2.90in-b - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -386,7 +362,6 @@ display: - platform: waveshare_epaper id: epd_2_90bv3 model: 2.90in-bv3 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -405,7 +380,6 @@ display: - platform: waveshare_epaper id: epd_2_90v2r2 model: 2.90inv2-r2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -425,7 +399,6 @@ display: - platform: waveshare_epaper id: epd_2_90dke model: 2.90in-dke - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -446,7 +419,6 @@ display: - platform: waveshare_epaper id: epd_gdew029t5 model: gdew029t5 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -466,7 +438,6 @@ display: - platform: waveshare_epaper id: epd_gdew042t81 model: gdey042t81 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -487,7 +458,6 @@ display: - platform: waveshare_epaper id: epd_4_20 model: 4.20in - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -506,7 +476,6 @@ display: - platform: waveshare_epaper id: epd_4_20bv2 model: 4.20in-bv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -525,7 +494,6 @@ display: - platform: waveshare_epaper id: epd_4_20in_bv2_bwr model: 4.20in-bv2-bwr - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -545,7 +513,6 @@ display: - platform: waveshare_epaper id: epd_5_65 model: 5.65in-f - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -565,7 +532,6 @@ display: - platform: waveshare_epaper id: epd_5_83 model: 5.83in - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -584,7 +550,6 @@ display: - platform: waveshare_epaper id: epd_5_83v2 model: 5.83inv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -604,7 +569,6 @@ display: - platform: waveshare_epaper id: epd_7_50 model: 7.50in - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -623,7 +587,6 @@ display: - platform: waveshare_epaper id: epd_7_50bv2 model: 7.50in-bv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -642,7 +605,6 @@ display: - platform: waveshare_epaper id: epd_7_50bv3 model: 7.50in-bv3 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -661,7 +623,6 @@ display: - platform: waveshare_epaper id: epd_7_50bv3_bwr model: 7.50in-bv3-bwr - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -680,7 +641,6 @@ display: - platform: waveshare_epaper id: epd_7_50bc model: 7.50in-bc - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -699,7 +659,6 @@ display: - platform: waveshare_epaper id: epd_7_50v2 model: 7.50inv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -718,7 +677,6 @@ display: - platform: waveshare_epaper id: epd_7_50v2alt model: 7.50inv2alt - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -737,7 +695,6 @@ display: - platform: waveshare_epaper id: epd_7_50inv2p model: 7.50inv2p - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -757,7 +714,6 @@ display: - platform: waveshare_epaper id: epd_7_50hdb model: 7.50in-hd-b - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -777,7 +733,6 @@ display: - platform: waveshare_epaper id: epd_13_3k model: 13.3in-k - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -795,7 +750,6 @@ display: - platform: waveshare_epaper model: 2.90in-d - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -814,7 +768,6 @@ display: - platform: waveshare_epaper model: 2.90in - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -834,7 +787,6 @@ display: - platform: waveshare_epaper model: 2.90inv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -853,7 +805,6 @@ display: - platform: waveshare_epaper model: 2.70in-b - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -871,7 +822,6 @@ display: - platform: waveshare_epaper model: 2.70in-bv2 - spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} diff --git a/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml b/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml index 4cb230f6f2..bdd7e1d350 100644 --- a/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml +++ b/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml @@ -1,9 +1,9 @@ substitutions: - clk_pin: GPIO6 - mosi_pin: GPIO7 - cs_pin: GPIO4 + cs_pin: GPIO7 dc_pin: GPIO1 busy_pin: GPIO2 reset_pin: GPIO3 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp32-idf.yaml b/tests/components/waveshare_epaper/test.esp32-idf.yaml index 9e8b2fdec8..b7b12c8c66 100644 --- a/tests/components/waveshare_epaper/test.esp32-idf.yaml +++ b/tests/components/waveshare_epaper/test.esp32-idf.yaml @@ -1,9 +1,10 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 cs_pin: GPIO4 dc_pin: GPIO5 - busy_pin: GPIO18 - reset_pin: GPIO19 + busy_pin: GPIO14 + reset_pin: GPIO15 + +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp8266-ard.yaml b/tests/components/waveshare_epaper/test.esp8266-ard.yaml index ee8199bcc0..92452c840a 100644 --- a/tests/components/waveshare_epaper/test.esp8266-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp8266-ard.yaml @@ -1,9 +1,12 @@ substitutions: - clk_pin: GPIO14 - mosi_pin: GPIO13 + clk_pin: GPIO0 + mosi_pin: GPIO2 cs_pin: GPIO4 dc_pin: GPIO5 busy_pin: GPIO15 reset_pin: GPIO16 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.rp2040-ard.yaml b/tests/components/waveshare_epaper/test.rp2040-ard.yaml index e92f6d421d..e9e001cdd4 100644 --- a/tests/components/waveshare_epaper/test.rp2040-ard.yaml +++ b/tests/components/waveshare_epaper/test.rp2040-ard.yaml @@ -6,4 +6,7 @@ substitutions: busy_pin: GPIO7 reset_pin: GPIO8 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/web_server/test_ota.esp32-idf.yaml b/tests/components/web_server/test_ota.esp32-idf.yaml index 99873aa27b..98b080386b 100644 --- a/tests/components/web_server/test_ota.esp32-idf.yaml +++ b/tests/components/web_server/test_ota.esp32-idf.yaml @@ -12,8 +12,10 @@ packages: # Enable OTA for multipart upload testing ota: - platform: esphome + id: web_server_esphome_ota password: "test_ota_password" - platform: web_server + id: web_server_web_server_ota # Web server configuration web_server: diff --git a/tests/components/wifi_info/test.esp32-c3-idf.yaml b/tests/components/wifi_info/test.esp32-c3-idf.yaml index dade44d145..9990d96d29 100644 --- a/tests/components/wifi_info/test.esp32-c3-idf.yaml +++ b/tests/components/wifi_info/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wifi_info/test.esp32-idf.yaml b/tests/components/wifi_info/test.esp32-idf.yaml index dade44d145..b47e39c389 100644 --- a/tests/components/wifi_info/test.esp32-idf.yaml +++ b/tests/components/wifi_info/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wifi_info/test.esp8266-ard.yaml b/tests/components/wifi_info/test.esp8266-ard.yaml index dade44d145..4a98b9388a 100644 --- a/tests/components/wifi_info/test.esp8266-ard.yaml +++ b/tests/components/wifi_info/test.esp8266-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/wifi_info/test.rp2040-ard.yaml b/tests/components/wifi_info/test.rp2040-ard.yaml index dade44d145..319a7c71a6 100644 --- a/tests/components/wifi_info/test.rp2040-ard.yaml +++ b/tests/components/wifi_info/test.rp2040-ard.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/wireguard/test.esp32-c3-idf.yaml b/tests/components/wireguard/test.esp32-c3-idf.yaml index dade44d145..9990d96d29 100644 --- a/tests/components/wireguard/test.esp32-c3-idf.yaml +++ b/tests/components/wireguard/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wireguard/test.esp32-idf.yaml b/tests/components/wireguard/test.esp32-idf.yaml index 2798f8e566..90dbc1cf7d 100644 --- a/tests/components/wireguard/test.esp32-idf.yaml +++ b/tests/components/wireguard/test.esp32-idf.yaml @@ -1,3 +1,6 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + <<: !include common.yaml network: diff --git a/tests/components/wireguard/test.esp8266-ard.yaml b/tests/components/wireguard/test.esp8266-ard.yaml index 2798f8e566..ae6a3d6ce0 100644 --- a/tests/components/wireguard/test.esp8266-ard.yaml +++ b/tests/components/wireguard/test.esp8266-ard.yaml @@ -1,3 +1,6 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + <<: !include common.yaml network: diff --git a/tests/components/wk2132_i2c/common.yaml b/tests/components/wk2132_i2c/common.yaml index 942e01aafc..39013baeb2 100644 --- a/tests/components/wk2132_i2c/common.yaml +++ b/tests/components/wk2132_i2c/common.yaml @@ -1,14 +1,7 @@ -i2c: - id: i2c_bus - scl: ${scl_pin} - sda: ${sda_pin} - scan: true - frequency: 600kHz - wk2132_i2c: - id: wk2132_i2c_id - address: 0x70 i2c_id: i2c_bus + address: 0x70 uart: - id: wk2132_id_0 channel: 0 diff --git a/tests/components/wk2132_i2c/test.esp32-idf.yaml b/tests/components/wk2132_i2c/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/wk2132_i2c/test.esp32-idf.yaml +++ b/tests/components/wk2132_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml index 4942e3c2b3..e9d826aa7c 100644 --- a/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml +++ b/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml @@ -2,4 +2,7 @@ substitutions: scl_pin: GPIO40 sda_pin: GPIO41 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2132_spi/common.yaml b/tests/components/wk2132_spi/common.yaml index a077b36998..a762c10c92 100644 --- a/tests/components/wk2132_spi/common.yaml +++ b/tests/components/wk2132_spi/common.yaml @@ -1,13 +1,6 @@ -spi: - id: spi_bus - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - wk2132_spi: - id: wk2132_spi_id cs_pin: ${cs_pin} - spi_id: spi_bus crystal: 11059200 data_rate: 1MHz uart: diff --git a/tests/components/wk2132_spi/test.esp32-idf.yaml b/tests/components/wk2132_spi/test.esp32-idf.yaml index 76e7138ab0..a3352cf880 100644 --- a/tests/components/wk2132_spi/test.esp32-idf.yaml +++ b/tests/components/wk2132_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO18 - miso_pin: GPIO19 - mosi_pin: GPIO23 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2132_spi/test.esp32-s3-idf.yaml b/tests/components/wk2132_spi/test.esp32-s3-idf.yaml index b0aadf620a..6a60c90fb2 100644 --- a/tests/components/wk2132_spi/test.esp32-s3-idf.yaml +++ b/tests/components/wk2132_spi/test.esp32-s3-idf.yaml @@ -4,4 +4,7 @@ substitutions: mosi_pin: GPIO6 cs_pin: GPIO19 +packages: + spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2168_i2c/common.yaml b/tests/components/wk2168_i2c/common.yaml index 10463e8abf..49f0d1ec6b 100644 --- a/tests/components/wk2168_i2c/common.yaml +++ b/tests/components/wk2168_i2c/common.yaml @@ -1,35 +1,28 @@ -i2c: - id: i2c_bus - scl: ${scl_pin} - sda: ${sda_pin} - scan: true - frequency: 600kHz - # component declaration wk2168_i2c: - - id: bridge_i2c + - id: wk2168_i2c_bridge i2c_id: i2c_bus address: 0x70 uart: - - id: id0 + - id: wk2168_i2c_uart0 channel: 0 baud_rate: 115200 stop_bits: 1 parity: none - - id: id1 + - id: wk2168_i2c_uart1 channel: 1 baud_rate: 115200 - - id: id2 + - id: wk2168_i2c_uart2 channel: 2 baud_rate: 115200 - - id: id3 + - id: wk2168_i2c_uart3 channel: 3 baud_rate: 9600 # Ensures a sensor doesn't break validation sensor: - platform: a02yyuw - uart_id: id3 + uart_id: wk2168_i2c_uart3 id: distance_sensor # individual binary_sensor inputs @@ -37,14 +30,14 @@ binary_sensor: - platform: gpio name: "pin_0" pin: - wk2168_i2c: bridge_i2c + wk2168_i2c: wk2168_i2c_bridge number: 0 mode: input: true - platform: gpio name: "pin_1" pin: - wk2168_i2c: bridge_i2c + wk2168_i2c: wk2168_i2c_bridge number: 1 mode: input: true @@ -55,14 +48,14 @@ switch: - platform: gpio name: "pin_2" pin: - wk2168_i2c: bridge_i2c + wk2168_i2c: wk2168_i2c_bridge number: 2 mode: output: true - platform: gpio name: "pin_3" pin: - wk2168_i2c: bridge_i2c + wk2168_i2c: wk2168_i2c_bridge number: 3 mode: output: true diff --git a/tests/components/wk2168_i2c/test.esp32-idf.yaml b/tests/components/wk2168_i2c/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/wk2168_i2c/test.esp32-idf.yaml +++ b/tests/components/wk2168_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml index 4942e3c2b3..e9d826aa7c 100644 --- a/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml +++ b/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml @@ -2,4 +2,7 @@ substitutions: scl_pin: GPIO40 sda_pin: GPIO41 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2168_spi/common.yaml b/tests/components/wk2168_spi/common.yaml index fb126193fc..b402077aa3 100644 --- a/tests/components/wk2168_spi/common.yaml +++ b/tests/components/wk2168_spi/common.yaml @@ -1,35 +1,28 @@ -spi: - id: spi_bus - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - wk2168_spi: - - id: bridge_spi + - id: wk2168_spi_bridge cs_pin: ${cs_pin} - spi_id: spi_bus crystal: 11059200 data_rate: 1MHz uart: - - id: id0 + - id: wk2168_spi_uart0 channel: 0 baud_rate: 115200 stop_bits: 1 parity: none - - id: id1 + - id: wk2168_spi_uart1 channel: 1 baud_rate: 115200 - - id: id2 + - id: wk2168_spi_uart2 channel: 2 baud_rate: 115200 - - id: id3 + - id: wk2168_spi_uart3 channel: 3 baud_rate: 9600 # Ensures a sensor doesn't break validation sensor: - platform: a02yyuw - uart_id: id3 + uart_id: wk2168_spi_uart3 id: distance_sensor # individual binary_sensor inputs @@ -37,14 +30,14 @@ binary_sensor: - platform: gpio name: "pin_0" pin: - wk2168_spi: bridge_spi + wk2168_spi: wk2168_spi_bridge number: 0 mode: input: true - platform: gpio name: "pin_1" pin: - wk2168_spi: bridge_spi + wk2168_spi: wk2168_spi_bridge number: 1 mode: input: true @@ -55,14 +48,14 @@ switch: - platform: gpio name: "pin_2" pin: - wk2168_spi: bridge_spi + wk2168_spi: wk2168_spi_bridge number: 2 mode: output: true - platform: gpio name: "pin_3" pin: - wk2168_spi: bridge_spi + wk2168_spi: wk2168_spi_bridge number: 3 mode: output: true diff --git a/tests/components/wk2168_spi/test.esp32-idf.yaml b/tests/components/wk2168_spi/test.esp32-idf.yaml index 76e7138ab0..a3352cf880 100644 --- a/tests/components/wk2168_spi/test.esp32-idf.yaml +++ b/tests/components/wk2168_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO18 - miso_pin: GPIO19 - mosi_pin: GPIO23 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2168_spi/test.esp32-s3-idf.yaml b/tests/components/wk2168_spi/test.esp32-s3-idf.yaml index b0aadf620a..6a60c90fb2 100644 --- a/tests/components/wk2168_spi/test.esp32-s3-idf.yaml +++ b/tests/components/wk2168_spi/test.esp32-s3-idf.yaml @@ -4,4 +4,7 @@ substitutions: mosi_pin: GPIO6 cs_pin: GPIO19 +packages: + spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2204_i2c/common.yaml b/tests/components/wk2204_i2c/common.yaml index 70c0f4babf..863633937b 100644 --- a/tests/components/wk2204_i2c/common.yaml +++ b/tests/components/wk2204_i2c/common.yaml @@ -1,10 +1,3 @@ -i2c: - id: i2c_bus - scl: ${scl_pin} - sda: ${sda_pin} - scan: true - frequency: 600kHz - wk2204_i2c: - id: wk2204_i2c_id i2c_id: i2c_bus diff --git a/tests/components/wk2204_i2c/test.esp32-idf.yaml b/tests/components/wk2204_i2c/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/wk2204_i2c/test.esp32-idf.yaml +++ b/tests/components/wk2204_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml index 4942e3c2b3..e9d826aa7c 100644 --- a/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml +++ b/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml @@ -2,4 +2,7 @@ substitutions: scl_pin: GPIO40 sda_pin: GPIO41 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2204_spi/common.yaml b/tests/components/wk2204_spi/common.yaml index a08cdb906f..939c54cc40 100644 --- a/tests/components/wk2204_spi/common.yaml +++ b/tests/components/wk2204_spi/common.yaml @@ -1,13 +1,6 @@ -spi: - id: spi_bus - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - wk2204_spi: - id: wk2204_spi_id cs_pin: ${cs_pin} - spi_id: spi_bus crystal: 11059200 data_rate: 1MHz uart: diff --git a/tests/components/wk2204_spi/test.esp32-idf.yaml b/tests/components/wk2204_spi/test.esp32-idf.yaml index 76e7138ab0..a3352cf880 100644 --- a/tests/components/wk2204_spi/test.esp32-idf.yaml +++ b/tests/components/wk2204_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO18 - miso_pin: GPIO19 - mosi_pin: GPIO23 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2204_spi/test.esp32-s3-idf.yaml b/tests/components/wk2204_spi/test.esp32-s3-idf.yaml index b0aadf620a..6a60c90fb2 100644 --- a/tests/components/wk2204_spi/test.esp32-s3-idf.yaml +++ b/tests/components/wk2204_spi/test.esp32-s3-idf.yaml @@ -4,4 +4,7 @@ substitutions: mosi_pin: GPIO6 cs_pin: GPIO19 +packages: + spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2212_i2c/common.yaml b/tests/components/wk2212_i2c/common.yaml index 0759ef8688..a754bec5c7 100644 --- a/tests/components/wk2212_i2c/common.yaml +++ b/tests/components/wk2212_i2c/common.yaml @@ -1,13 +1,6 @@ -i2c: - id: i2c_bus - scl: ${scl_pin} - sda: ${sda_pin} - scan: true - frequency: 600kHz - # component declaration wk2212_i2c: - - id: bridge_i2c + - id: wk2212_i2c_bridge i2c_id: i2c_bus address: 0x70 uart: @@ -33,14 +26,14 @@ binary_sensor: - platform: gpio name: "pin_0" pin: - wk2212_i2c: bridge_i2c + wk2212_i2c: wk2212_i2c_bridge number: 0 mode: input: true - platform: gpio name: "pin_1" pin: - wk2212_i2c: bridge_i2c + wk2212_i2c: wk2212_i2c_bridge number: 1 mode: input: true @@ -51,14 +44,14 @@ switch: - platform: gpio name: "pin_2" pin: - wk2212_i2c: bridge_i2c + wk2212_i2c: wk2212_i2c_bridge number: 2 mode: output: true - platform: gpio name: "pin_3" pin: - wk2212_i2c: bridge_i2c + wk2212_i2c: wk2212_i2c_bridge number: 3 mode: output: true diff --git a/tests/components/wk2212_i2c/test.esp32-idf.yaml b/tests/components/wk2212_i2c/test.esp32-idf.yaml index 3b761d3fc1..b47e39c389 100644 --- a/tests/components/wk2212_i2c/test.esp32-idf.yaml +++ b/tests/components/wk2212_i2c/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO22 - sda_pin: GPIO21 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml index 4942e3c2b3..e9d826aa7c 100644 --- a/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml +++ b/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml @@ -2,4 +2,7 @@ substitutions: scl_pin: GPIO40 sda_pin: GPIO41 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2212_spi/common.yaml b/tests/components/wk2212_spi/common.yaml index 693d2a9ab2..969f16bb12 100644 --- a/tests/components/wk2212_spi/common.yaml +++ b/tests/components/wk2212_spi/common.yaml @@ -1,29 +1,22 @@ -spi: - id: spi_bus - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - wk2212_spi: - - id: bridge_spi + - id: wk2212_spi_bridge cs_pin: ${cs_pin} - spi_id: spi_bus crystal: 11059200 data_rate: 1MHz uart: - - id: id0 + - id: wk2212_spi_uart0 channel: 0 baud_rate: 115200 stop_bits: 1 parity: none - - id: id1 + - id: wk2212_spi_uart1 channel: 1 baud_rate: 9600 # Ensures a sensor doesn't break validation sensor: - platform: a02yyuw - uart_id: id1 + uart_id: wk2212_spi_uart1 id: distance_sensor # individual binary_sensor inputs @@ -31,14 +24,14 @@ binary_sensor: - platform: gpio name: "pin_0" pin: - wk2212_spi: bridge_spi + wk2212_spi: wk2212_spi_bridge number: 0 mode: input: true - platform: gpio name: "pin_1" pin: - wk2212_spi: bridge_spi + wk2212_spi: wk2212_spi_bridge number: 1 mode: input: true @@ -49,14 +42,14 @@ switch: - platform: gpio name: "pin_2" pin: - wk2212_spi: bridge_spi + wk2212_spi: wk2212_spi_bridge number: 2 mode: output: true - platform: gpio name: "pin_3" pin: - wk2212_spi: bridge_spi + wk2212_spi: wk2212_spi_bridge number: 3 mode: output: true diff --git a/tests/components/wk2212_spi/test.esp32-idf.yaml b/tests/components/wk2212_spi/test.esp32-idf.yaml index 76e7138ab0..a3352cf880 100644 --- a/tests/components/wk2212_spi/test.esp32-idf.yaml +++ b/tests/components/wk2212_spi/test.esp32-idf.yaml @@ -1,7 +1,7 @@ substitutions: - clk_pin: GPIO18 - miso_pin: GPIO19 - mosi_pin: GPIO23 cs_pin: GPIO5 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wk2212_spi/test.esp32-s3-idf.yaml b/tests/components/wk2212_spi/test.esp32-s3-idf.yaml index b0aadf620a..6a60c90fb2 100644 --- a/tests/components/wk2212_spi/test.esp32-s3-idf.yaml +++ b/tests/components/wk2212_spi/test.esp32-s3-idf.yaml @@ -4,4 +4,7 @@ substitutions: mosi_pin: GPIO6 cs_pin: GPIO19 +packages: + spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/wl_134/common.yaml b/tests/components/wl_134/common.yaml index 71c50be79b..e79331cb52 100644 --- a/tests/components/wl_134/common.yaml +++ b/tests/components/wl_134/common.yaml @@ -1,9 +1,3 @@ -uart: - - id: uart_wl_134 - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 9600 - text_sensor: - platform: wl_134 name: Transponder Code diff --git a/tests/components/wl_134/test.esp32-c3-idf.yaml b/tests/components/wl_134/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/wl_134/test.esp32-c3-idf.yaml +++ b/tests/components/wl_134/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/wl_134/test.esp32-idf.yaml b/tests/components/wl_134/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/wl_134/test.esp32-idf.yaml +++ b/tests/components/wl_134/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wl_134/test.esp8266-ard.yaml b/tests/components/wl_134/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/wl_134/test.esp8266-ard.yaml +++ b/tests/components/wl_134/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/wl_134/test.rp2040-ard.yaml b/tests/components/wl_134/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/wl_134/test.rp2040-ard.yaml +++ b/tests/components/wl_134/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/wts01/common.yaml b/tests/components/wts01/common.yaml index c26cc3e475..966588c82a 100644 --- a/tests/components/wts01/common.yaml +++ b/tests/components/wts01/common.yaml @@ -1,7 +1,3 @@ -uart: - rx_pin: ${rx_pin} - baud_rate: 9600 - sensor: - platform: wts01 id: wts01_sensor diff --git a/tests/components/wts01/test.esp32-c3-idf.yaml b/tests/components/wts01/test.esp32-c3-idf.yaml index 00cec5b3b8..4b7c8351a7 100644 --- a/tests/components/wts01/test.esp32-c3-idf.yaml +++ b/tests/components/wts01/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO6 - rx_pin: GPIO7 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/wts01/test.esp32-idf.yaml b/tests/components/wts01/test.esp32-idf.yaml index 4904e1f54f..b415125e84 100644 --- a/tests/components/wts01/test.esp32-idf.yaml +++ b/tests/components/wts01/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO16 - rx_pin: GPIO17 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wts01/test.esp8266-ard.yaml b/tests/components/wts01/test.esp8266-ard.yaml index 3b44f9c9c3..89ca3ab5ae 100644 --- a/tests/components/wts01/test.esp8266-ard.yaml +++ b/tests/components/wts01/test.esp8266-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO1 rx_pin: GPIO3 +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/wts01/test.rp2040-ard.yaml b/tests/components/wts01/test.rp2040-ard.yaml index 16b2a4b006..9246c39f08 100644 --- a/tests/components/wts01/test.rp2040-ard.yaml +++ b/tests/components/wts01/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO0 rx_pin: GPIO1 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/x9c/test.esp32-idf.yaml b/tests/components/x9c/test.esp32-idf.yaml index 6dfe3a67eb..7beb6e67cb 100644 --- a/tests/components/x9c/test.esp32-idf.yaml +++ b/tests/components/x9c/test.esp32-idf.yaml @@ -3,4 +3,7 @@ substitutions: inc_pin: GPIO14 ud_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/x9c/test.esp8266-ard.yaml b/tests/components/x9c/test.esp8266-ard.yaml index 6dfe3a67eb..ae6b775ff7 100644 --- a/tests/components/x9c/test.esp8266-ard.yaml +++ b/tests/components/x9c/test.esp8266-ard.yaml @@ -1,6 +1,9 @@ substitutions: - cs_pin: GPIO13 - inc_pin: GPIO14 + cs_pin: GPIO0 + inc_pin: GPIO2 ud_pin: GPIO15 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/x9c/test.rp2040-ard.yaml b/tests/components/x9c/test.rp2040-ard.yaml index b06e15a98c..f17627baf3 100644 --- a/tests/components/x9c/test.rp2040-ard.yaml +++ b/tests/components/x9c/test.rp2040-ard.yaml @@ -1,6 +1,9 @@ substitutions: - cs_pin: GPIO3 - inc_pin: GPIO4 - ud_pin: GPIO5 + cs_pin: GPIO6 + inc_pin: GPIO7 + ud_pin: GPIO8 + +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/xgzp68xx/common.yaml b/tests/components/xgzp68xx/common.yaml index 224dd9ed14..f76b1de508 100644 --- a/tests/components/xgzp68xx/common.yaml +++ b/tests/components/xgzp68xx/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_xgzp68xx - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: xgzp68xx + i2c_id: i2c_bus k_value: 4096 temperature: name: Pressure Temperature diff --git a/tests/components/xgzp68xx/test.esp32-c3-idf.yaml b/tests/components/xgzp68xx/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/xgzp68xx/test.esp32-c3-idf.yaml +++ b/tests/components/xgzp68xx/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.esp32-idf.yaml b/tests/components/xgzp68xx/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/xgzp68xx/test.esp32-idf.yaml +++ b/tests/components/xgzp68xx/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.esp8266-ard.yaml b/tests/components/xgzp68xx/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/xgzp68xx/test.esp8266-ard.yaml +++ b/tests/components/xgzp68xx/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.rp2040-ard.yaml b/tests/components/xgzp68xx/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/xgzp68xx/test.rp2040-ard.yaml +++ b/tests/components/xgzp68xx/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/xiaomi_ble/test.esp32-c3-idf.yaml b/tests/components/xiaomi_ble/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_ble/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_ble/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_ble/test.esp32-idf.yaml b/tests/components/xiaomi_ble/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_ble/test.esp32-idf.yaml +++ b/tests/components/xiaomi_ble/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_cgd1/test.esp32-c3-idf.yaml b/tests/components/xiaomi_cgd1/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_cgd1/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_cgd1/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_cgd1/test.esp32-idf.yaml b/tests/components/xiaomi_cgd1/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_cgd1/test.esp32-idf.yaml +++ b/tests/components/xiaomi_cgd1/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_cgdk2/test.esp32-c3-idf.yaml b/tests/components/xiaomi_cgdk2/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_cgdk2/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_cgdk2/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_cgdk2/test.esp32-idf.yaml b/tests/components/xiaomi_cgdk2/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_cgdk2/test.esp32-idf.yaml +++ b/tests/components/xiaomi_cgdk2/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_cgg1/test.esp32-c3-idf.yaml b/tests/components/xiaomi_cgg1/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_cgg1/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_cgg1/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_cgg1/test.esp32-idf.yaml b/tests/components/xiaomi_cgg1/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_cgg1/test.esp32-idf.yaml +++ b/tests/components/xiaomi_cgg1/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_cgpr1/test.esp32-c3-idf.yaml b/tests/components/xiaomi_cgpr1/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_cgpr1/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_cgpr1/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_cgpr1/test.esp32-idf.yaml b/tests/components/xiaomi_cgpr1/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_cgpr1/test.esp32-idf.yaml +++ b/tests/components/xiaomi_cgpr1/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_gcls002/test.esp32-c3-idf.yaml b/tests/components/xiaomi_gcls002/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_gcls002/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_gcls002/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_gcls002/test.esp32-idf.yaml b/tests/components/xiaomi_gcls002/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_gcls002/test.esp32-idf.yaml +++ b/tests/components/xiaomi_gcls002/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_hhccjcy01/test.esp32-c3-idf.yaml b/tests/components/xiaomi_hhccjcy01/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_hhccjcy01/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_hhccjcy01/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_hhccjcy01/test.esp32-idf.yaml b/tests/components/xiaomi_hhccjcy01/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_hhccjcy01/test.esp32-idf.yaml +++ b/tests/components/xiaomi_hhccjcy01/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_hhccpot002/test.esp32-c3-idf.yaml b/tests/components/xiaomi_hhccpot002/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_hhccpot002/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_hhccpot002/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_hhccpot002/test.esp32-idf.yaml b/tests/components/xiaomi_hhccpot002/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_hhccpot002/test.esp32-idf.yaml +++ b/tests/components/xiaomi_hhccpot002/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_jqjcy01ym/test.esp32-c3-idf.yaml b/tests/components/xiaomi_jqjcy01ym/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_jqjcy01ym/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_jqjcy01ym/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_jqjcy01ym/test.esp32-idf.yaml b/tests/components/xiaomi_jqjcy01ym/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_jqjcy01ym/test.esp32-idf.yaml +++ b/tests/components/xiaomi_jqjcy01ym/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02/test.esp32-c3-idf.yaml b/tests/components/xiaomi_lywsd02/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_lywsd02/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_lywsd02/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02/test.esp32-idf.yaml b/tests/components/xiaomi_lywsd02/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_lywsd02/test.esp32-idf.yaml +++ b/tests/components/xiaomi_lywsd02/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml b/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02mmc/test.esp32-idf.yaml b/tests/components/xiaomi_lywsd02mmc/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_lywsd02mmc/test.esp32-idf.yaml +++ b/tests/components/xiaomi_lywsd02mmc/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd03mmc/test.esp32-c3-idf.yaml b/tests/components/xiaomi_lywsd03mmc/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_lywsd03mmc/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_lywsd03mmc/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd03mmc/test.esp32-idf.yaml b/tests/components/xiaomi_lywsd03mmc/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_lywsd03mmc/test.esp32-idf.yaml +++ b/tests/components/xiaomi_lywsd03mmc/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_lywsdcgq/test.esp32-c3-idf.yaml b/tests/components/xiaomi_lywsdcgq/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_lywsdcgq/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_lywsdcgq/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_lywsdcgq/test.esp32-idf.yaml b/tests/components/xiaomi_lywsdcgq/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_lywsdcgq/test.esp32-idf.yaml +++ b/tests/components/xiaomi_lywsdcgq/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_mhoc303/test.esp32-c3-idf.yaml b/tests/components/xiaomi_mhoc303/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_mhoc303/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_mhoc303/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_mhoc303/test.esp32-idf.yaml b/tests/components/xiaomi_mhoc303/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_mhoc303/test.esp32-idf.yaml +++ b/tests/components/xiaomi_mhoc303/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_mhoc401/test.esp32-c3-idf.yaml b/tests/components/xiaomi_mhoc401/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_mhoc401/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_mhoc401/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_mhoc401/test.esp32-idf.yaml b/tests/components/xiaomi_mhoc401/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_mhoc401/test.esp32-idf.yaml +++ b/tests/components/xiaomi_mhoc401/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_miscale copy/common.yaml b/tests/components/xiaomi_miscale copy/common.yaml deleted file mode 100644 index 89f32ad199..0000000000 --- a/tests/components/xiaomi_miscale copy/common.yaml +++ /dev/null @@ -1,9 +0,0 @@ -esp32_ble_tracker: - -sensor: - - platform: xiaomi_miscale - mac_address: '5C:CA:D3:70:D4:A2' - weight: - name: "Xiaomi Mi Scale Weight" - impedance: - name: "Xiaomi Mi Scale Impedance" diff --git a/tests/components/xiaomi_miscale copy/test.esp32-ard.yaml b/tests/components/xiaomi_miscale copy/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_miscale copy/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_miscale copy/test.esp32-c3-ard.yaml b/tests/components/xiaomi_miscale copy/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_miscale copy/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_miscale copy/test.esp32-c3-idf.yaml b/tests/components/xiaomi_miscale copy/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_miscale copy/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_miscale copy/test.esp32-idf.yaml b/tests/components/xiaomi_miscale copy/test.esp32-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/xiaomi_miscale copy/test.esp32-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/xiaomi_miscale/test.esp32-c3-idf.yaml b/tests/components/xiaomi_miscale/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_miscale/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_miscale/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_miscale/test.esp32-idf.yaml b/tests/components/xiaomi_miscale/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_miscale/test.esp32-idf.yaml +++ b/tests/components/xiaomi_miscale/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_mjyd02yla/test.esp32-c3-idf.yaml b/tests/components/xiaomi_mjyd02yla/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_mjyd02yla/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_mjyd02yla/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_mjyd02yla/test.esp32-idf.yaml b/tests/components/xiaomi_mjyd02yla/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_mjyd02yla/test.esp32-idf.yaml +++ b/tests/components/xiaomi_mjyd02yla/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_mue4094rt/test.esp32-c3-idf.yaml b/tests/components/xiaomi_mue4094rt/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_mue4094rt/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_mue4094rt/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_mue4094rt/test.esp32-idf.yaml b/tests/components/xiaomi_mue4094rt/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_mue4094rt/test.esp32-idf.yaml +++ b/tests/components/xiaomi_mue4094rt/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_rtcgq02lm/test.esp32-c3-idf.yaml b/tests/components/xiaomi_rtcgq02lm/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_rtcgq02lm/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_rtcgq02lm/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_rtcgq02lm/test.esp32-idf.yaml b/tests/components/xiaomi_rtcgq02lm/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_rtcgq02lm/test.esp32-idf.yaml +++ b/tests/components/xiaomi_rtcgq02lm/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_wx08zm/test.esp32-c3-idf.yaml b/tests/components/xiaomi_wx08zm/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_wx08zm/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_wx08zm/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_wx08zm/test.esp32-idf.yaml b/tests/components/xiaomi_wx08zm/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_wx08zm/test.esp32-idf.yaml +++ b/tests/components/xiaomi_wx08zm/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml index dade44d145..9f2634f967 100644 --- a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml +++ b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-idf.yaml b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-idf.yaml index dade44d145..7a6541ae76 100644 --- a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-idf.yaml +++ b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + ble: !include ../../test_build_components/common/ble/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xl9535/common.yaml b/tests/components/xl9535/common.yaml index e01163cf12..81e96131ab 100644 --- a/tests/components/xl9535/common.yaml +++ b/tests/components/xl9535/common.yaml @@ -1,10 +1,6 @@ -i2c: - - id: i2c_xl9535 - scl: ${scl_pin} - sda: ${sda_pin} - xl9535: - id: xl9535_hub + i2c_id: i2c_bus address: 0x20 binary_sensor: diff --git a/tests/components/xl9535/test.esp32-c3-idf.yaml b/tests/components/xl9535/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/xl9535/test.esp32-c3-idf.yaml +++ b/tests/components/xl9535/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/xl9535/test.esp32-idf.yaml b/tests/components/xl9535/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/xl9535/test.esp32-idf.yaml +++ b/tests/components/xl9535/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/xl9535/test.esp8266-ard.yaml b/tests/components/xl9535/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/xl9535/test.esp8266-ard.yaml +++ b/tests/components/xl9535/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/xl9535/test.rp2040-ard.yaml b/tests/components/xl9535/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/xl9535/test.rp2040-ard.yaml +++ b/tests/components/xl9535/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/xpt2046/common.yaml b/tests/components/xpt2046/common.yaml index 9ef680cff4..3a8e3286a6 100644 --- a/tests/components/xpt2046/common.yaml +++ b/tests/components/xpt2046/common.yaml @@ -1,9 +1,3 @@ -spi: - - id: spi_xpt2046 - clk_pin: ${clk_pin} - mosi_pin: ${mosi_pin} - miso_pin: ${miso_pin} - display: - platform: ili9xxx id: xpt_display diff --git a/tests/components/xpt2046/test.esp32-c3-idf.yaml b/tests/components/xpt2046/test.esp32-c3-idf.yaml index 79b84902ac..ff7a32c26c 100644 --- a/tests/components/xpt2046/test.esp32-c3-idf.yaml +++ b/tests/components/xpt2046/test.esp32-c3-idf.yaml @@ -1,11 +1,10 @@ substitutions: - clk_pin: GPIO4 - mosi_pin: GPIO5 - miso_pin: GPIO6 dc_pin: GPIO7 cs_pin: GPIO0 disp_cs_pin: GPIO1 interrupt_pin: GPIO3 reset_pin: GPIO10 +packages: + spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/xpt2046/test.esp32-idf.yaml b/tests/components/xpt2046/test.esp32-idf.yaml index b39174947b..608e135d74 100644 --- a/tests/components/xpt2046/test.esp32-idf.yaml +++ b/tests/components/xpt2046/test.esp32-idf.yaml @@ -1,11 +1,11 @@ substitutions: - clk_pin: GPIO17 - mosi_pin: GPIO18 - miso_pin: GPIO19 dc_pin: GPIO13 cs_pin: GPIO14 disp_cs_pin: GPIO4 interrupt_pin: GPIO21 reset_pin: GPIO22 +packages: + spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/xpt2046/test.esp8266-ard.yaml b/tests/components/xpt2046/test.esp8266-ard.yaml index 246c5c8953..e2779a03b9 100644 --- a/tests/components/xpt2046/test.esp8266-ard.yaml +++ b/tests/components/xpt2046/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clk_pin: GPIO14 + clk_pin: GPIO0 mosi_pin: GPIO13 miso_pin: GPIO12 dc_pin: GPIO15 @@ -8,4 +8,7 @@ substitutions: interrupt_pin: GPIO5 reset_pin: GPIO2 +packages: + spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/xpt2046/test.rp2040-ard.yaml b/tests/components/xpt2046/test.rp2040-ard.yaml index e693b363d9..c547bdc0c9 100644 --- a/tests/components/xpt2046/test.rp2040-ard.yaml +++ b/tests/components/xpt2046/test.rp2040-ard.yaml @@ -8,4 +8,7 @@ substitutions: interrupt_pin: GPIO2 reset_pin: GPIO3 +packages: + spi: !include ../../test_build_components/common/spi/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/common.yaml b/tests/components/zio_ultrasonic/common.yaml index e13853d8f1..34a3144db9 100644 --- a/tests/components/zio_ultrasonic/common.yaml +++ b/tests/components/zio_ultrasonic/common.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_zio_ultrasonic - scl: ${scl_pin} - sda: ${sda_pin} - sensor: - platform: zio_ultrasonic + i2c_id: i2c_bus name: "Distance" update_interval: 60s diff --git a/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml b/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml index ee2c29ca4e..9990d96d29 100644 --- a/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml +++ b/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.esp32-idf.yaml b/tests/components/zio_ultrasonic/test.esp32-idf.yaml index 63c3bd6afd..b47e39c389 100644 --- a/tests/components/zio_ultrasonic/test.esp32-idf.yaml +++ b/tests/components/zio_ultrasonic/test.esp32-idf.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO16 - sda_pin: GPIO17 +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.esp8266-ard.yaml b/tests/components/zio_ultrasonic/test.esp8266-ard.yaml index ee2c29ca4e..4a98b9388a 100644 --- a/tests/components/zio_ultrasonic/test.esp8266-ard.yaml +++ b/tests/components/zio_ultrasonic/test.esp8266-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.rp2040-ard.yaml b/tests/components/zio_ultrasonic/test.rp2040-ard.yaml index ee2c29ca4e..319a7c71a6 100644 --- a/tests/components/zio_ultrasonic/test.rp2040-ard.yaml +++ b/tests/components/zio_ultrasonic/test.rp2040-ard.yaml @@ -1,5 +1,4 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml <<: !include common.yaml diff --git a/tests/components/zwave_proxy/common.yaml b/tests/components/zwave_proxy/common.yaml index 08092ebe55..097e4b43a5 100644 --- a/tests/components/zwave_proxy/common.yaml +++ b/tests/components/zwave_proxy/common.yaml @@ -3,12 +3,6 @@ wifi: password: password1 power_save_mode: none -uart: - - id: uart_zwave_proxy - tx_pin: ${tx_pin} - rx_pin: ${rx_pin} - baud_rate: 115200 - api: zwave_proxy: diff --git a/tests/components/zwave_proxy/test.esp32-c3-idf.yaml b/tests/components/zwave_proxy/test.esp32-c3-idf.yaml index b516342f3b..4b7c8351a7 100644 --- a/tests/components/zwave_proxy/test.esp32-c3-idf.yaml +++ b/tests/components/zwave_proxy/test.esp32-c3-idf.yaml @@ -1,5 +1,5 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/zwave_proxy/test.esp32-idf.yaml b/tests/components/zwave_proxy/test.esp32-idf.yaml index f486544afa..b415125e84 100644 --- a/tests/components/zwave_proxy/test.esp32-idf.yaml +++ b/tests/components/zwave_proxy/test.esp32-idf.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO17 - rx_pin: GPIO16 + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/zwave_proxy/test.esp8266-ard.yaml b/tests/components/zwave_proxy/test.esp8266-ard.yaml index b516342f3b..96ab4ef6ac 100644 --- a/tests/components/zwave_proxy/test.esp8266-ard.yaml +++ b/tests/components/zwave_proxy/test.esp8266-ard.yaml @@ -1,5 +1,8 @@ substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 + tx_pin: GPIO0 + rx_pin: GPIO2 + +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/zwave_proxy/test.rp2040-ard.yaml b/tests/components/zwave_proxy/test.rp2040-ard.yaml index b516342f3b..b28f2b5e05 100644 --- a/tests/components/zwave_proxy/test.rp2040-ard.yaml +++ b/tests/components/zwave_proxy/test.rp2040-ard.yaml @@ -2,4 +2,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml + <<: !include common.yaml diff --git a/tests/components/zyaura/test.esp32-idf.yaml b/tests/components/zyaura/test.esp32-idf.yaml index d295973e3f..a4ecdb6c49 100644 --- a/tests/components/zyaura/test.esp32-idf.yaml +++ b/tests/components/zyaura/test.esp32-idf.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO16 - data_pin: GPIO17 + clock_pin: GPIO4 + data_pin: GPIO5 <<: !include common.yaml diff --git a/tests/components/zyaura/test.esp8266-ard.yaml b/tests/components/zyaura/test.esp8266-ard.yaml index 7808481215..7c7f1e1a11 100644 --- a/tests/components/zyaura/test.esp8266-ard.yaml +++ b/tests/components/zyaura/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 + clock_pin: GPIO0 + data_pin: GPIO2 <<: !include common.yaml diff --git a/tests/test_build_components/build_components_base.bk72xx-ard.yaml b/tests/test_build_components/build_components_base.bk72xx-ard.yaml index 9a4e15d5cf..817acc3c39 100644 --- a/tests/test_build_components/build_components_base.bk72xx-ard.yaml +++ b/tests/test_build_components/build_components_base.bk72xx-ard.yaml @@ -3,7 +3,7 @@ esphome: friendly_name: $component_name bk72xx: - board: generic-bk7231n-qfn32-tuya + board: generic-bk7252 logger: level: VERY_VERBOSE diff --git a/tests/test_build_components/common/README.md b/tests/test_build_components/common/README.md new file mode 100644 index 0000000000..76f14b8664 --- /dev/null +++ b/tests/test_build_components/common/README.md @@ -0,0 +1,248 @@ +# Common Bus Configurations for Component Tests + +This directory contains standardized bus configurations (I2C, SPI, UART, Modbus, BLE) that component tests use, enabling multiple components to be tested together with intelligent grouping. + +## Purpose + +These common configs allow multiple components to **share a single bus**, dramatically reducing CI time by compiling multiple compatible components together. Components with identical bus configurations are automatically grouped and tested together. + +## Structure + +``` +common/ +├── i2c/ # Standard I2C (50kHz) +│ ├── esp32-idf.yaml +│ ├── esp32-ard.yaml +│ ├── esp32-c3-idf.yaml +│ ├── esp32-c3-ard.yaml +│ ├── esp32-s2-idf.yaml +│ ├── esp32-s2-ard.yaml +│ ├── esp32-s3-idf.yaml +│ ├── esp32-s3-ard.yaml +│ ├── esp8266-ard.yaml +│ ├── rp2040-ard.yaml +│ └── bk72xx-ard.yaml +├── i2c_low_freq/ # Low frequency I2C (10kHz) +│ └── (same platform variants) +├── spi/ +│ └── (same platform variants) +├── uart/ +│ ├── esp32-idf.yaml +│ ├── esp32-c3-idf.yaml +│ ├── esp8266-ard.yaml +│ └── rp2040-ard.yaml +├── modbus/ # Modbus (includes uart via packages) +│ ├── esp32-idf.yaml +│ ├── esp32-c3-idf.yaml +│ ├── esp8266-ard.yaml +│ └── rp2040-ard.yaml +└── ble/ + ├── esp32-idf.yaml + ├── esp32-ard.yaml + └── esp32-c3-idf.yaml +``` + +## How It Works + +### Component Test Structure +Each component test includes the common bus config: + +```yaml +# tests/components/bh1750/test.esp32-idf.yaml +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + +<<: !include common.yaml +``` + +The common config provides: +- Standardized pin assignments +- A shared bus instance (`i2c_bus`, `spi_bus`, `uart_bus`, `modbus_bus`, etc.) + +The component's `common.yaml` references the shared bus: +```yaml +# tests/components/bh1750/common.yaml +sensor: + - platform: bh1750 + i2c_id: i2c_bus + name: Living Room Brightness + address: 0x23 +``` + +### Intelligent Grouping (Implemented) +Components with identical bus configurations are automatically grouped and tested together: + +```yaml +# Auto-generated merged config (created by test_build_components.py) +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + +sensor: + - platform: bme280_i2c + i2c_id: i2c_bus + temperature: + name: BME280 Temperature + - platform: bh1750 + i2c_id: i2c_bus + name: BH1750 Illuminance + - platform: sht3xd + i2c_id: i2c_bus + temperature: + name: SHT3xD Temperature +``` + +**Result**: 3 components compile in one test instead of 3 separate tests! + +### Package Dependencies +Some packages include other packages to avoid duplication: + +```yaml +# tests/test_build_components/common/modbus/esp32-idf.yaml +packages: + uart: !include ../uart/esp32-idf.yaml # Modbus requires UART + +substitutions: + flow_control_pin: GPIO4 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} +``` + +Components using `modbus` packages automatically get `uart` as well. + +## Pin Allocations + +### I2C (Standard - 50kHz) +- **ESP32 IDF**: SCL=GPIO16, SDA=GPIO17 +- **ESP32 Arduino**: SCL=GPIO22, SDA=GPIO21 +- **ESP32-C3**: SCL=GPIO5, SDA=GPIO4 +- **ESP32-S2/S3**: SCL=GPIO9, SDA=GPIO8 +- **ESP8266**: SCL=GPIO5, SDA=GPIO4 +- **RP2040**: SCL=GPIO5, SDA=GPIO4 +- **BK72xx**: SCL=P20, SDA=P21 + +### I2C Low Frequency (10kHz) +Same pin allocations as standard I2C, but with 10kHz frequency for components requiring slower speeds. + +### SPI +- **ESP32**: CLK=GPIO18, MOSI=GPIO23, MISO=GPIO19 +- **ESP32-C3**: CLK=GPIO6, MOSI=GPIO7, MISO=GPIO5 +- **ESP32-S2**: CLK=GPIO36, MOSI=GPIO35, MISO=GPIO37 +- **ESP32-S3**: CLK=GPIO40, MOSI=GPIO6, MISO=GPIO41 +- **ESP8266**: CLK=GPIO14, MOSI=GPIO13, MISO=GPIO12 +- **RP2040**: CLK=GPIO18, MOSI=GPIO19, MISO=GPIO16 +- CS pins are component-specific (each SPI device needs unique CS) + +### UART +- **ESP32 IDF**: TX=GPIO17, RX=GPIO16 (baud: 19200) +- **ESP32-C3 IDF**: TX=GPIO7, RX=GPIO6 (baud: 19200) +- **ESP8266**: TX=GPIO1, RX=GPIO3 (baud: 19200) +- **RP2040**: TX=GPIO0, RX=GPIO1 (baud: 19200) + +### Modbus (includes UART) +Same UART pins as above, plus: +- **flow_control_pin**: GPIO4 (all platforms) + +### BLE +- **ESP32**: Shared `esp32_ble_tracker` infrastructure +- Each component defines unique `ble_client` with different MAC addresses + +## Benefits + +1. **Shared bus = less duplication** + - 200+ I2C components use common bus configs + - 60+ SPI components use common bus configs + - 80+ UART components use common bus configs + - 6 Modbus components use common modbus configs (which include UART) + +2. **Intelligent grouping reduces CI time** + - Components with identical bus configs are automatically grouped + - Typical reduction: 80-90% fewer builds + - Example: 3 I2C components → 1 merged build (saves 2 builds) + - CI runs 5 component batches in parallel (configurable via `max-parallel` in `.github/workflows/ci.yml`) + +3. **Easier maintenance** + - Change bus pins for a platform once, affects all component tests + - Consistent pin assignments across all components + - Centralized bus configuration + +## Component Compatibility + +### Groupable Components +Components are automatically grouped when they have: +- Identical bus package references (e.g., all use `i2c/esp32-idf.yaml`) +- No local file references (`$component_dir`) +- No `!extend` or `!remove` directives +- Proper bus ID references (`i2c_id: i2c_bus`, `spi_id: spi_bus`, etc.) + +### Non-Groupable Components +Components tested individually when they: +- Use different bus frequencies (e.g., `i2c` vs `i2c_low_freq`) +- Reference local files with `$component_dir` +- Are platform components (abstract base classes with `IS_PLATFORM_COMPONENT = True`) +- Define buses directly (not migrated to packages yet) +- Are in `ISOLATED_COMPONENTS` list (known build conflicts) + +### Bus Variants +- **i2c** (50kHz): Standard I2C frequency for most sensors +- **i2c_low_freq** (10kHz): For sensors requiring slower I2C speeds +- **modbus**: Includes UART via package dependencies + +### Making Components Groupable + +**WARNING**: Using `!extend` or `!remove` directives in component test files prevents automatic component grouping in CI, making builds slower. + +When platform-specific parameters are needed, inline the full configuration rather than using `!extend` or `!remove`. This allows the component to be grouped with other compatible components, reducing CI build time. + +**Note**: Some components legitimately require `!extend` or `!remove` for platform-specific features (e.g., `adc` removing ESP32-only `attenuation` parameter on ESP8266). These are correctly identified as non-groupable. + +## Testing Components + +### Testing Individual Components +Test specific components using `test_build_components`: +```bash +# Test a single component +./script/test_build_components -c bme280_i2c -t esp32-idf -e config + +# Test multiple components +./script/test_build_components -c bme280_i2c,bh1750,sht3xd -t esp32-idf -e compile +``` + +### Testing All Components Together +To verify that all components can be tested together without ID conflicts or configuration issues: +```bash +./script/test_component_grouping.py -e config --all +``` + +This tests all components in a single build to catch conflicts that might not appear when testing components individually. This is useful for: +- Detecting ID conflicts between components +- Validating that components can coexist in the same configuration +- Ensuring proper `i2c_id`, `spi_id`, `uart_id` specifications + +Use `-e config` for fast configuration validation, or `-e compile` for full compilation testing. + +### Testing Component Groups +Test specific groups of components by bus signature: +```bash +# Test all I2C components together +./script/test_component_grouping.py -s i2c -e config + +# Test with custom group sizes +./script/test_component_grouping.py --min-size 5 --max-size 20 --max-groups 3 +``` + +## Implementation Details + +### Scripts +- `script/analyze_component_buses.py`: Analyzes components to detect bus usage and grouping compatibility +- `script/merge_component_configs.py`: Merges multiple component configs into a single test file +- `script/test_build_components.py`: Main test runner with intelligent grouping +- `script/test_component_grouping.py`: Test component groups or all components together +- `script/split_components_for_ci.py`: Splits components into batches for parallel CI execution + +### Configuration +- `.github/workflows/ci.yml`: CI workflow with `max-parallel: 5` for component testing +- Package dependencies defined in `PACKAGE_DEPENDENCIES` (e.g., modbus → uart) +- Base bus components excluded from migration warnings: `i2c`, `spi`, `uart`, `modbus`, `canbus` diff --git a/tests/test_build_components/common/ble/esp32-ard.yaml b/tests/test_build_components/common/ble/esp32-ard.yaml new file mode 100644 index 0000000000..e1b5aec694 --- /dev/null +++ b/tests/test_build_components/common/ble/esp32-ard.yaml @@ -0,0 +1,9 @@ +# Common BLE tracker configuration for ESP32 Arduino tests +# BLE client components share this tracker infrastructure +# Each component defines its own ble_client with unique MAC address + +esp32_ble_tracker: + scan_parameters: + interval: 1100ms + window: 1100ms + active: true diff --git a/tests/test_build_components/common/ble/esp32-c3-idf.yaml b/tests/test_build_components/common/ble/esp32-c3-idf.yaml new file mode 100644 index 0000000000..6671722f21 --- /dev/null +++ b/tests/test_build_components/common/ble/esp32-c3-idf.yaml @@ -0,0 +1,9 @@ +# Common BLE tracker configuration for ESP32-C3 IDF tests +# BLE client components share this tracker infrastructure +# Each component defines its own ble_client with unique MAC address + +esp32_ble_tracker: + scan_parameters: + interval: 1100ms + window: 1100ms + active: true diff --git a/tests/test_build_components/common/ble/esp32-idf.yaml b/tests/test_build_components/common/ble/esp32-idf.yaml new file mode 100644 index 0000000000..7cdcea71f0 --- /dev/null +++ b/tests/test_build_components/common/ble/esp32-idf.yaml @@ -0,0 +1,9 @@ +# Common BLE tracker configuration for ESP32 IDF tests +# BLE client components share this tracker infrastructure +# Each component defines its own ble_client with unique MAC address + +esp32_ble_tracker: + scan_parameters: + interval: 1100ms + window: 1100ms + active: true diff --git a/tests/test_build_components/common/camera/esp32-idf.yaml b/tests/test_build_components/common/camera/esp32-idf.yaml new file mode 100644 index 0000000000..64f75c699a --- /dev/null +++ b/tests/test_build_components/common/camera/esp32-idf.yaml @@ -0,0 +1,29 @@ +esp32_camera: + name: ESP32 Camera + data_pins: + - number: 17 + - number: 35 + - number: 34 + - number: 5 + - number: 39 + - number: 18 + - number: 36 + - number: 19 + vsync_pin: 22 + href_pin: 26 + pixel_clock_pin: 21 + external_clock: + pin: 27 + frequency: 20MHz + i2c_pins: + sda: 25 + scl: 23 + reset_pin: 15 + power_down_pin: 1 + resolution: 640x480 + jpeg_quality: 10 + frame_buffer_location: PSRAM + on_image: + then: + - lambda: |- + ESP_LOGD("main", "image len=%d, data=%c", image.length, image.data[0]); diff --git a/tests/test_build_components/common/i2c/bk72xx-ard.yaml b/tests/test_build_components/common/i2c/bk72xx-ard.yaml new file mode 100644 index 0000000000..7d00546721 --- /dev/null +++ b/tests/test_build_components/common/i2c/bk72xx-ard.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for BK72XX Arduino tests + +substitutions: + scl_pin: P26 + sda_pin: P24 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/esp32-ard.yaml b/tests/test_build_components/common/i2c/esp32-ard.yaml new file mode 100644 index 0000000000..fff09ab85c --- /dev/null +++ b/tests/test_build_components/common/i2c/esp32-ard.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for ESP32 Arduino tests + +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/esp32-c3-ard.yaml b/tests/test_build_components/common/i2c/esp32-c3-ard.yaml new file mode 100644 index 0000000000..0216dccbfa --- /dev/null +++ b/tests/test_build_components/common/i2c/esp32-c3-ard.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for ESP32-C3 Arduino tests + +substitutions: + scl_pin: GPIO8 + sda_pin: GPIO10 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/esp32-c3-idf.yaml b/tests/test_build_components/common/i2c/esp32-c3-idf.yaml new file mode 100644 index 0000000000..fd873b6db8 --- /dev/null +++ b/tests/test_build_components/common/i2c/esp32-c3-idf.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for ESP32-C3 IDF tests + +substitutions: + scl_pin: GPIO8 + sda_pin: GPIO10 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/esp32-idf.yaml b/tests/test_build_components/common/i2c/esp32-idf.yaml new file mode 100644 index 0000000000..bb2376e7d6 --- /dev/null +++ b/tests/test_build_components/common/i2c/esp32-idf.yaml @@ -0,0 +1,13 @@ +# Common I2C configuration for ESP32 IDF tests +# Provides a shared I2C bus that all components can use +# Components will auto-use this bus if they don't specify i2c_id + +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/esp32-p4-idf.yaml b/tests/test_build_components/common/i2c/esp32-p4-idf.yaml new file mode 100644 index 0000000000..0c7d7e3414 --- /dev/null +++ b/tests/test_build_components/common/i2c/esp32-p4-idf.yaml @@ -0,0 +1,13 @@ +# Common I2C configuration for ESP32-P4 IDF tests +# Provides a shared I2C bus that all components can use +# Components will auto-use this bus if they don't specify i2c_id + +substitutions: + scl_pin: GPIO8 + sda_pin: GPIO7 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/esp32-s2-ard.yaml b/tests/test_build_components/common/i2c/esp32-s2-ard.yaml new file mode 100644 index 0000000000..d8201db042 --- /dev/null +++ b/tests/test_build_components/common/i2c/esp32-s2-ard.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for ESP32-S2 Arduino tests + +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/esp32-s2-idf.yaml b/tests/test_build_components/common/i2c/esp32-s2-idf.yaml new file mode 100644 index 0000000000..f23f7e35cc --- /dev/null +++ b/tests/test_build_components/common/i2c/esp32-s2-idf.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for ESP32-S2 IDF tests + +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/esp32-s3-ard.yaml b/tests/test_build_components/common/i2c/esp32-s3-ard.yaml new file mode 100644 index 0000000000..3499770cb7 --- /dev/null +++ b/tests/test_build_components/common/i2c/esp32-s3-ard.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for ESP32-S3 Arduino tests + +substitutions: + scl_pin: GPIO9 + sda_pin: GPIO8 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/esp32-s3-idf.yaml b/tests/test_build_components/common/i2c/esp32-s3-idf.yaml new file mode 100644 index 0000000000..9f5b002eb3 --- /dev/null +++ b/tests/test_build_components/common/i2c/esp32-s3-idf.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for ESP32-S3 IDF tests + +substitutions: + scl_pin: GPIO9 + sda_pin: GPIO8 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/esp8266-ard.yaml b/tests/test_build_components/common/i2c/esp8266-ard.yaml new file mode 100644 index 0000000000..6aa28524b2 --- /dev/null +++ b/tests/test_build_components/common/i2c/esp8266-ard.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for ESP8266 Arduino tests + +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c/rp2040-ard.yaml b/tests/test_build_components/common/i2c/rp2040-ard.yaml new file mode 100644 index 0000000000..cb241aef76 --- /dev/null +++ b/tests/test_build_components/common/i2c/rp2040-ard.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for RP2040 Arduino tests + +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true diff --git a/tests/test_build_components/common/i2c_low_freq/esp32-ard.yaml b/tests/test_build_components/common/i2c_low_freq/esp32-ard.yaml new file mode 100644 index 0000000000..53d051f174 --- /dev/null +++ b/tests/test_build_components/common/i2c_low_freq/esp32-ard.yaml @@ -0,0 +1,12 @@ +# Common I2C configuration for ESP32 Arduino tests - Low Frequency (10kHz) + +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + frequency: 10kHz + scan: true diff --git a/tests/test_build_components/common/i2c_low_freq/esp32-c3-ard.yaml b/tests/test_build_components/common/i2c_low_freq/esp32-c3-ard.yaml new file mode 100644 index 0000000000..c9856c6b29 --- /dev/null +++ b/tests/test_build_components/common/i2c_low_freq/esp32-c3-ard.yaml @@ -0,0 +1,12 @@ +# Common I2C configuration for ESP32-C3 Arduino tests - Low Frequency (10kHz) + +substitutions: + scl_pin: GPIO6 + sda_pin: GPIO5 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + frequency: 10kHz + scan: true diff --git a/tests/test_build_components/common/i2c_low_freq/esp32-c3-idf.yaml b/tests/test_build_components/common/i2c_low_freq/esp32-c3-idf.yaml new file mode 100644 index 0000000000..e9d44b585e --- /dev/null +++ b/tests/test_build_components/common/i2c_low_freq/esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +# Common I2C configuration for ESP32-C3 IDF tests - Low Frequency (10kHz) + +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + frequency: 10kHz + scan: true diff --git a/tests/test_build_components/common/i2c_low_freq/esp32-idf.yaml b/tests/test_build_components/common/i2c_low_freq/esp32-idf.yaml new file mode 100644 index 0000000000..4afe220315 --- /dev/null +++ b/tests/test_build_components/common/i2c_low_freq/esp32-idf.yaml @@ -0,0 +1,13 @@ +# Common I2C configuration for ESP32 IDF tests - Low Frequency (10kHz) +# For components that require I2C frequency <= 15kHz (ags10, ltr501, ltr_als_ps) + +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + frequency: 10kHz + scan: true diff --git a/tests/test_build_components/common/i2c_low_freq/esp8266-ard.yaml b/tests/test_build_components/common/i2c_low_freq/esp8266-ard.yaml new file mode 100644 index 0000000000..281177ebbd --- /dev/null +++ b/tests/test_build_components/common/i2c_low_freq/esp8266-ard.yaml @@ -0,0 +1,12 @@ +# Common I2C configuration for ESP8266 Arduino tests - Low Frequency (10kHz) + +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + frequency: 10kHz + scan: true diff --git a/tests/test_build_components/common/i2c_low_freq/rp2040-ard.yaml b/tests/test_build_components/common/i2c_low_freq/rp2040-ard.yaml new file mode 100644 index 0000000000..67d893e733 --- /dev/null +++ b/tests/test_build_components/common/i2c_low_freq/rp2040-ard.yaml @@ -0,0 +1,12 @@ +# Common I2C configuration for RP2040 Arduino tests - Low Frequency (10kHz) + +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + frequency: 10kHz + scan: true diff --git a/tests/test_build_components/common/modbus/bk72xx-ard.yaml b/tests/test_build_components/common/modbus/bk72xx-ard.yaml new file mode 100644 index 0000000000..c428f0a7be --- /dev/null +++ b/tests/test_build_components/common/modbus/bk72xx-ard.yaml @@ -0,0 +1,12 @@ +# Common Modbus configuration for BK72XX Arduino tests + +packages: + uart: !include ../uart/bk72xx-ard.yaml + +substitutions: + flow_control_pin: P6 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/modbus/esp32-ard.yaml b/tests/test_build_components/common/modbus/esp32-ard.yaml new file mode 100644 index 0000000000..f327cf266e --- /dev/null +++ b/tests/test_build_components/common/modbus/esp32-ard.yaml @@ -0,0 +1,12 @@ +# Common Modbus configuration for ESP32 Arduino tests + +packages: + uart: !include ../uart/esp32-ard.yaml + +substitutions: + flow_control_pin: GPIO4 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/modbus/esp32-c3-ard.yaml b/tests/test_build_components/common/modbus/esp32-c3-ard.yaml new file mode 100644 index 0000000000..f12e5518c0 --- /dev/null +++ b/tests/test_build_components/common/modbus/esp32-c3-ard.yaml @@ -0,0 +1,12 @@ +# Common Modbus configuration for ESP32-C3 Arduino tests + +packages: + uart: !include ../uart/esp32-c3-ard.yaml + +substitutions: + flow_control_pin: GPIO4 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/modbus/esp32-c3-idf.yaml b/tests/test_build_components/common/modbus/esp32-c3-idf.yaml new file mode 100644 index 0000000000..98fc11b0b7 --- /dev/null +++ b/tests/test_build_components/common/modbus/esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +# Common Modbus configuration for ESP32-C3 IDF tests + +packages: + uart: !include ../uart/esp32-c3-idf.yaml + +substitutions: + flow_control_pin: GPIO4 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/modbus/esp32-idf.yaml b/tests/test_build_components/common/modbus/esp32-idf.yaml new file mode 100644 index 0000000000..c2d777c3d7 --- /dev/null +++ b/tests/test_build_components/common/modbus/esp32-idf.yaml @@ -0,0 +1,13 @@ +# Common Modbus configuration for ESP32 IDF tests +# Provides a shared Modbus bus that all components can use + +packages: + uart: !include ../uart/esp32-idf.yaml + +substitutions: + flow_control_pin: GPIO4 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/modbus/esp32-s2-ard.yaml b/tests/test_build_components/common/modbus/esp32-s2-ard.yaml new file mode 100644 index 0000000000..a47036f379 --- /dev/null +++ b/tests/test_build_components/common/modbus/esp32-s2-ard.yaml @@ -0,0 +1,12 @@ +# Common Modbus configuration for ESP32-S2 Arduino tests + +packages: + uart: !include ../uart/esp32-s2-ard.yaml + +substitutions: + flow_control_pin: GPIO4 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/modbus/esp32-s2-idf.yaml b/tests/test_build_components/common/modbus/esp32-s2-idf.yaml new file mode 100644 index 0000000000..2cac82aa15 --- /dev/null +++ b/tests/test_build_components/common/modbus/esp32-s2-idf.yaml @@ -0,0 +1,12 @@ +# Common Modbus configuration for ESP32-S2 IDF tests + +packages: + uart: !include ../uart/esp32-s2-idf.yaml + +substitutions: + flow_control_pin: GPIO4 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/modbus/esp32-s3-ard.yaml b/tests/test_build_components/common/modbus/esp32-s3-ard.yaml new file mode 100644 index 0000000000..3031f57159 --- /dev/null +++ b/tests/test_build_components/common/modbus/esp32-s3-ard.yaml @@ -0,0 +1,12 @@ +# Common Modbus configuration for ESP32-S3 Arduino tests + +packages: + uart: !include ../uart/esp32-s3-ard.yaml + +substitutions: + flow_control_pin: GPIO4 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/modbus/esp32-s3-idf.yaml b/tests/test_build_components/common/modbus/esp32-s3-idf.yaml new file mode 100644 index 0000000000..0a0d4dbd07 --- /dev/null +++ b/tests/test_build_components/common/modbus/esp32-s3-idf.yaml @@ -0,0 +1,12 @@ +# Common Modbus configuration for ESP32-S3 IDF tests + +packages: + uart: !include ../uart/esp32-s3-idf.yaml + +substitutions: + flow_control_pin: GPIO4 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/modbus/esp8266-ard.yaml b/tests/test_build_components/common/modbus/esp8266-ard.yaml new file mode 100644 index 0000000000..fce4c6df1d --- /dev/null +++ b/tests/test_build_components/common/modbus/esp8266-ard.yaml @@ -0,0 +1,12 @@ +# Common Modbus configuration for ESP8266 Arduino tests + +packages: + uart: !include ../uart/esp8266-ard.yaml + +substitutions: + flow_control_pin: GPIO5 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/modbus/rp2040-ard.yaml b/tests/test_build_components/common/modbus/rp2040-ard.yaml new file mode 100644 index 0000000000..264ad8944f --- /dev/null +++ b/tests/test_build_components/common/modbus/rp2040-ard.yaml @@ -0,0 +1,12 @@ +# Common Modbus configuration for RP2040 Arduino tests + +packages: + uart: !include ../uart/rp2040-ard.yaml + +substitutions: + flow_control_pin: GPIO2 + +modbus: + - id: modbus_bus + uart_id: uart_bus + flow_control_pin: ${flow_control_pin} diff --git a/tests/test_build_components/common/qspi/esp32-s3-idf.yaml b/tests/test_build_components/common/qspi/esp32-s3-idf.yaml new file mode 100644 index 0000000000..22c98ef664 --- /dev/null +++ b/tests/test_build_components/common/qspi/esp32-s3-idf.yaml @@ -0,0 +1,13 @@ +# Common QSPI configuration for ESP32-S3 IDF tests +# For components that need QuadSPI (qspi_dbi displays) + +spi: + - id: quad_spi + type: quad + interface: spi3 + clk_pin: 47 + data_pins: + - 40 + - 41 + - 42 + - 43 diff --git a/tests/test_build_components/common/spi/bk72xx-ard.yaml b/tests/test_build_components/common/spi/bk72xx-ard.yaml new file mode 100644 index 0000000000..471b147bfa --- /dev/null +++ b/tests/test_build_components/common/spi/bk72xx-ard.yaml @@ -0,0 +1,12 @@ +# Common SPI configuration for BK72XX Arduino tests + +substitutions: + clk_pin: P10 + mosi_pin: P11 + miso_pin: P6 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/spi/esp32-ard.yaml b/tests/test_build_components/common/spi/esp32-ard.yaml new file mode 100644 index 0000000000..816609688c --- /dev/null +++ b/tests/test_build_components/common/spi/esp32-ard.yaml @@ -0,0 +1,12 @@ +# Common SPI configuration for ESP32 Arduino tests + +substitutions: + clk_pin: GPIO18 + mosi_pin: GPIO23 + miso_pin: GPIO19 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/spi/esp32-c3-ard.yaml b/tests/test_build_components/common/spi/esp32-c3-ard.yaml new file mode 100644 index 0000000000..da3182f259 --- /dev/null +++ b/tests/test_build_components/common/spi/esp32-c3-ard.yaml @@ -0,0 +1,12 @@ +# Common SPI configuration for ESP32-C3 Arduino tests + +substitutions: + clk_pin: GPIO4 + mosi_pin: GPIO6 + miso_pin: GPIO5 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/spi/esp32-c3-idf.yaml b/tests/test_build_components/common/spi/esp32-c3-idf.yaml new file mode 100644 index 0000000000..6a8f76c38c --- /dev/null +++ b/tests/test_build_components/common/spi/esp32-c3-idf.yaml @@ -0,0 +1,15 @@ +# Common SPI configuration for ESP32-C3 IDF tests +# Provides a shared SPI bus that all components can use +# Components will auto-use this bus if they don't specify spi_id +# CS pins are component-specific + +substitutions: + clk_pin: GPIO4 + mosi_pin: GPIO6 + miso_pin: GPIO5 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/spi/esp32-idf.yaml b/tests/test_build_components/common/spi/esp32-idf.yaml new file mode 100644 index 0000000000..c2c39a2bc0 --- /dev/null +++ b/tests/test_build_components/common/spi/esp32-idf.yaml @@ -0,0 +1,15 @@ +# Common SPI configuration for ESP32 IDF tests +# Provides a shared SPI bus that all components can use +# Components will auto-use this bus if they don't specify spi_id +# CS pins are component-specific + +substitutions: + clk_pin: GPIO18 + mosi_pin: GPIO23 + miso_pin: GPIO19 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/spi/esp32-s2-ard.yaml b/tests/test_build_components/common/spi/esp32-s2-ard.yaml new file mode 100644 index 0000000000..7c8997ae7f --- /dev/null +++ b/tests/test_build_components/common/spi/esp32-s2-ard.yaml @@ -0,0 +1,12 @@ +# Common SPI configuration for ESP32-S2 Arduino tests + +substitutions: + clk_pin: GPIO36 + mosi_pin: GPIO35 + miso_pin: GPIO37 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/spi/esp32-s2-idf.yaml b/tests/test_build_components/common/spi/esp32-s2-idf.yaml new file mode 100644 index 0000000000..afcd83e94e --- /dev/null +++ b/tests/test_build_components/common/spi/esp32-s2-idf.yaml @@ -0,0 +1,12 @@ +# Common SPI configuration for ESP32-S2 IDF tests + +substitutions: + clk_pin: GPIO36 + mosi_pin: GPIO35 + miso_pin: GPIO37 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/spi/esp32-s3-ard.yaml b/tests/test_build_components/common/spi/esp32-s3-ard.yaml new file mode 100644 index 0000000000..06d5f65771 --- /dev/null +++ b/tests/test_build_components/common/spi/esp32-s3-ard.yaml @@ -0,0 +1,12 @@ +# Common SPI configuration for ESP32-S3 Arduino tests + +substitutions: + clk_pin: GPIO40 + mosi_pin: GPIO6 + miso_pin: GPIO41 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/spi/esp32-s3-idf.yaml b/tests/test_build_components/common/spi/esp32-s3-idf.yaml new file mode 100644 index 0000000000..ee47396ec7 --- /dev/null +++ b/tests/test_build_components/common/spi/esp32-s3-idf.yaml @@ -0,0 +1,12 @@ +# Common SPI configuration for ESP32-S3 IDF tests + +substitutions: + clk_pin: GPIO40 + mosi_pin: GPIO6 + miso_pin: GPIO41 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/spi/esp8266-ard.yaml b/tests/test_build_components/common/spi/esp8266-ard.yaml new file mode 100644 index 0000000000..4320afebb9 --- /dev/null +++ b/tests/test_build_components/common/spi/esp8266-ard.yaml @@ -0,0 +1,12 @@ +# Common SPI configuration for ESP8266 Arduino tests + +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/spi/rp2040-ard.yaml b/tests/test_build_components/common/spi/rp2040-ard.yaml new file mode 100644 index 0000000000..916a636318 --- /dev/null +++ b/tests/test_build_components/common/spi/rp2040-ard.yaml @@ -0,0 +1,12 @@ +# Common SPI configuration for RP2040 Arduino tests + +substitutions: + clk_pin: GPIO18 + mosi_pin: GPIO19 + miso_pin: GPIO16 + +spi: + - id: spi_bus + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/test_build_components/common/uart/bk72xx-ard.yaml b/tests/test_build_components/common/uart/bk72xx-ard.yaml new file mode 100644 index 0000000000..8d1abca70b --- /dev/null +++ b/tests/test_build_components/common/uart/bk72xx-ard.yaml @@ -0,0 +1,13 @@ +# Common UART configuration for BK72XX Arduino tests +# Provides a shared UART bus that components can use +# Components will auto-use this bus if they don't specify uart_id + +substitutions: + tx_pin: TX1 + rx_pin: RX1 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 diff --git a/tests/test_build_components/common/uart/esp32-ard.yaml b/tests/test_build_components/common/uart/esp32-ard.yaml new file mode 100644 index 0000000000..805695def6 --- /dev/null +++ b/tests/test_build_components/common/uart/esp32-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32 Arduino tests + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 diff --git a/tests/test_build_components/common/uart/esp32-c3-ard.yaml b/tests/test_build_components/common/uart/esp32-c3-ard.yaml new file mode 100644 index 0000000000..565b109f9a --- /dev/null +++ b/tests/test_build_components/common/uart/esp32-c3-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32-C3 Arduino tests + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 diff --git a/tests/test_build_components/common/uart/esp32-c3-idf.yaml b/tests/test_build_components/common/uart/esp32-c3-idf.yaml new file mode 100644 index 0000000000..944aa013dd --- /dev/null +++ b/tests/test_build_components/common/uart/esp32-c3-idf.yaml @@ -0,0 +1,13 @@ +# Common UART configuration for ESP32-C3 IDF tests +# Provides a shared UART bus that components can use +# Components will auto-use this bus if they don't specify uart_id + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 diff --git a/tests/test_build_components/common/uart/esp32-idf.yaml b/tests/test_build_components/common/uart/esp32-idf.yaml new file mode 100644 index 0000000000..95e5db9fb1 --- /dev/null +++ b/tests/test_build_components/common/uart/esp32-idf.yaml @@ -0,0 +1,13 @@ +# Common UART configuration for ESP32 IDF tests +# Provides a shared UART bus that components can use +# Components will auto-use this bus if they don't specify uart_id + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 diff --git a/tests/test_build_components/common/uart/esp8266-ard.yaml b/tests/test_build_components/common/uart/esp8266-ard.yaml new file mode 100644 index 0000000000..e326f4fc0d --- /dev/null +++ b/tests/test_build_components/common/uart/esp8266-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP8266 Arduino tests + +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 diff --git a/tests/test_build_components/common/uart/rp2040-ard.yaml b/tests/test_build_components/common/uart/rp2040-ard.yaml new file mode 100644 index 0000000000..cd1e54a13b --- /dev/null +++ b/tests/test_build_components/common/uart/rp2040-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for RP2040 Arduino tests + +substitutions: + tx_pin: GPIO0 + rx_pin: GPIO1 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 diff --git a/tests/test_build_components/common/uart_115200/esp32-ard.yaml b/tests/test_build_components/common/uart_115200/esp32-ard.yaml new file mode 100644 index 0000000000..9102910f31 --- /dev/null +++ b/tests/test_build_components/common/uart_115200/esp32-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32 Arduino tests - 115200 baud + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 diff --git a/tests/test_build_components/common/uart_115200/esp32-c3-ard.yaml b/tests/test_build_components/common/uart_115200/esp32-c3-ard.yaml new file mode 100644 index 0000000000..87a969c6a3 --- /dev/null +++ b/tests/test_build_components/common/uart_115200/esp32-c3-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32-C3 Arduino tests - 115200 baud + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 diff --git a/tests/test_build_components/common/uart_115200/esp32-c3-idf.yaml b/tests/test_build_components/common/uart_115200/esp32-c3-idf.yaml new file mode 100644 index 0000000000..f3768592e5 --- /dev/null +++ b/tests/test_build_components/common/uart_115200/esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32-C3 IDF tests - 115200 baud +# For components that require UART baud rate 115200 (bl0906) + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 diff --git a/tests/test_build_components/common/uart_115200/esp32-idf.yaml b/tests/test_build_components/common/uart_115200/esp32-idf.yaml new file mode 100644 index 0000000000..e405f74fe7 --- /dev/null +++ b/tests/test_build_components/common/uart_115200/esp32-idf.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32 IDF tests - 115200 baud +# For components that require UART baud rate 115200 (bl0906) + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 diff --git a/tests/test_build_components/common/uart_115200/esp8266-ard.yaml b/tests/test_build_components/common/uart_115200/esp8266-ard.yaml new file mode 100644 index 0000000000..2dcf1c4a5d --- /dev/null +++ b/tests/test_build_components/common/uart_115200/esp8266-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP8266 Arduino tests - 115200 baud + +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 diff --git a/tests/test_build_components/common/uart_115200/rp2040-ard.yaml b/tests/test_build_components/common/uart_115200/rp2040-ard.yaml new file mode 100644 index 0000000000..62a7b5aed2 --- /dev/null +++ b/tests/test_build_components/common/uart_115200/rp2040-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for RP2040 Arduino tests - 115200 baud + +substitutions: + tx_pin: GPIO0 + rx_pin: GPIO1 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 diff --git a/tests/test_build_components/common/uart_1200/esp32-ard.yaml b/tests/test_build_components/common/uart_1200/esp32-ard.yaml new file mode 100644 index 0000000000..0ff5663d1f --- /dev/null +++ b/tests/test_build_components/common/uart_1200/esp32-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32 Arduino tests - 1200 baud + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 diff --git a/tests/test_build_components/common/uart_1200/esp32-c3-ard.yaml b/tests/test_build_components/common/uart_1200/esp32-c3-ard.yaml new file mode 100644 index 0000000000..81cad70d3c --- /dev/null +++ b/tests/test_build_components/common/uart_1200/esp32-c3-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32-C3 Arduino tests - 1200 baud + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 diff --git a/tests/test_build_components/common/uart_1200/esp32-c3-idf.yaml b/tests/test_build_components/common/uart_1200/esp32-c3-idf.yaml new file mode 100644 index 0000000000..8f1dace337 --- /dev/null +++ b/tests/test_build_components/common/uart_1200/esp32-c3-idf.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32-C3 IDF tests - 1200 baud + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 diff --git a/tests/test_build_components/common/uart_1200/esp32-idf.yaml b/tests/test_build_components/common/uart_1200/esp32-idf.yaml new file mode 100644 index 0000000000..38ad1b1459 --- /dev/null +++ b/tests/test_build_components/common/uart_1200/esp32-idf.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32 IDF tests - 1200 baud + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 diff --git a/tests/test_build_components/common/uart_1200/esp8266-ard.yaml b/tests/test_build_components/common/uart_1200/esp8266-ard.yaml new file mode 100644 index 0000000000..84907a3a42 --- /dev/null +++ b/tests/test_build_components/common/uart_1200/esp8266-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP8266 Arduino tests - 1200 baud + +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 diff --git a/tests/test_build_components/common/uart_1200/rp2040-ard.yaml b/tests/test_build_components/common/uart_1200/rp2040-ard.yaml new file mode 100644 index 0000000000..3a3b322ea8 --- /dev/null +++ b/tests/test_build_components/common/uart_1200/rp2040-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for RP2040 Arduino tests - 1200 baud + +substitutions: + tx_pin: GPIO0 + rx_pin: GPIO1 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 diff --git a/tests/test_build_components/common/uart_1200_even/esp32-ard.yaml b/tests/test_build_components/common/uart_1200_even/esp32-ard.yaml new file mode 100644 index 0000000000..f5f7f0669f --- /dev/null +++ b/tests/test_build_components/common/uart_1200_even/esp32-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32 Arduino tests - 1200 baud even parity + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 + parity: EVEN diff --git a/tests/test_build_components/common/uart_1200_even/esp32-c3-ard.yaml b/tests/test_build_components/common/uart_1200_even/esp32-c3-ard.yaml new file mode 100644 index 0000000000..0b1e3ba61b --- /dev/null +++ b/tests/test_build_components/common/uart_1200_even/esp32-c3-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32-C3 Arduino tests - 1200 baud even parity + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 + parity: EVEN diff --git a/tests/test_build_components/common/uart_1200_even/esp32-c3-idf.yaml b/tests/test_build_components/common/uart_1200_even/esp32-c3-idf.yaml new file mode 100644 index 0000000000..1781babefb --- /dev/null +++ b/tests/test_build_components/common/uart_1200_even/esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32-C3 IDF tests - 1200 baud, EVEN parity + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 + parity: EVEN diff --git a/tests/test_build_components/common/uart_1200_even/esp32-idf.yaml b/tests/test_build_components/common/uart_1200_even/esp32-idf.yaml new file mode 100644 index 0000000000..3b4b17f892 --- /dev/null +++ b/tests/test_build_components/common/uart_1200_even/esp32-idf.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32 IDF tests - 1200 baud, EVEN parity + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 + parity: EVEN diff --git a/tests/test_build_components/common/uart_1200_even/esp8266-ard.yaml b/tests/test_build_components/common/uart_1200_even/esp8266-ard.yaml new file mode 100644 index 0000000000..54f7de1757 --- /dev/null +++ b/tests/test_build_components/common/uart_1200_even/esp8266-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP8266 Arduino tests - 1200 baud even parity + +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 + parity: EVEN diff --git a/tests/test_build_components/common/uart_1200_even/rp2040-ard.yaml b/tests/test_build_components/common/uart_1200_even/rp2040-ard.yaml new file mode 100644 index 0000000000..0e8bdeae1f --- /dev/null +++ b/tests/test_build_components/common/uart_1200_even/rp2040-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for RP2040 Arduino tests - 1200 baud even parity + +substitutions: + tx_pin: GPIO0 + rx_pin: GPIO1 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 + parity: EVEN diff --git a/tests/test_build_components/common/uart_19200/esp32-ard.yaml b/tests/test_build_components/common/uart_19200/esp32-ard.yaml new file mode 100644 index 0000000000..f4f04669da --- /dev/null +++ b/tests/test_build_components/common/uart_19200/esp32-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32 Arduino tests - 19200 baud + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 19200 diff --git a/tests/test_build_components/common/uart_19200/esp32-c3-ard.yaml b/tests/test_build_components/common/uart_19200/esp32-c3-ard.yaml new file mode 100644 index 0000000000..925acdc34c --- /dev/null +++ b/tests/test_build_components/common/uart_19200/esp32-c3-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32-C3 Arduino tests - 19200 baud + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 19200 diff --git a/tests/test_build_components/common/uart_19200/esp32-c3-idf.yaml b/tests/test_build_components/common/uart_19200/esp32-c3-idf.yaml new file mode 100644 index 0000000000..0d765a88a4 --- /dev/null +++ b/tests/test_build_components/common/uart_19200/esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32-C3 IDF tests - 19200 baud +# For components that require UART baud rate 19200 (bl0906) + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 19200 diff --git a/tests/test_build_components/common/uart_19200/esp32-idf.yaml b/tests/test_build_components/common/uart_19200/esp32-idf.yaml new file mode 100644 index 0000000000..e7849508c7 --- /dev/null +++ b/tests/test_build_components/common/uart_19200/esp32-idf.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32 IDF tests - 19200 baud +# For components that require UART baud rate 19200 (bl0906) + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 19200 diff --git a/tests/test_build_components/common/uart_19200/esp8266-ard.yaml b/tests/test_build_components/common/uart_19200/esp8266-ard.yaml new file mode 100644 index 0000000000..f01bc4590c --- /dev/null +++ b/tests/test_build_components/common/uart_19200/esp8266-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP8266 Arduino tests - 19200 baud + +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 19200 diff --git a/tests/test_build_components/common/uart_19200/rp2040-ard.yaml b/tests/test_build_components/common/uart_19200/rp2040-ard.yaml new file mode 100644 index 0000000000..6ebd02d451 --- /dev/null +++ b/tests/test_build_components/common/uart_19200/rp2040-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for RP2040 Arduino tests - 19200 baud + +substitutions: + tx_pin: GPIO0 + rx_pin: GPIO1 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 19200 diff --git a/tests/test_build_components/common/uart_38400/esp32-ard.yaml b/tests/test_build_components/common/uart_38400/esp32-ard.yaml new file mode 100644 index 0000000000..15da771ccc --- /dev/null +++ b/tests/test_build_components/common/uart_38400/esp32-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32 Arduino tests - 38400 baud + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 38400 diff --git a/tests/test_build_components/common/uart_38400/esp32-c3-ard.yaml b/tests/test_build_components/common/uart_38400/esp32-c3-ard.yaml new file mode 100644 index 0000000000..8838f029dc --- /dev/null +++ b/tests/test_build_components/common/uart_38400/esp32-c3-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32-C3 Arduino tests - 38400 baud + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 38400 diff --git a/tests/test_build_components/common/uart_38400/esp32-c3-idf.yaml b/tests/test_build_components/common/uart_38400/esp32-c3-idf.yaml new file mode 100644 index 0000000000..d7d902af3d --- /dev/null +++ b/tests/test_build_components/common/uart_38400/esp32-c3-idf.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32-C3 IDF tests - 38400 baud + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 38400 diff --git a/tests/test_build_components/common/uart_38400/esp32-idf.yaml b/tests/test_build_components/common/uart_38400/esp32-idf.yaml new file mode 100644 index 0000000000..f1c9587e27 --- /dev/null +++ b/tests/test_build_components/common/uart_38400/esp32-idf.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32 IDF tests - 38400 baud + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 38400 diff --git a/tests/test_build_components/common/uart_38400/esp8266-ard.yaml b/tests/test_build_components/common/uart_38400/esp8266-ard.yaml new file mode 100644 index 0000000000..b1a046ea5e --- /dev/null +++ b/tests/test_build_components/common/uart_38400/esp8266-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP8266 Arduino tests - 38400 baud + +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 38400 diff --git a/tests/test_build_components/common/uart_38400/rp2040-ard.yaml b/tests/test_build_components/common/uart_38400/rp2040-ard.yaml new file mode 100644 index 0000000000..01b5e58ed7 --- /dev/null +++ b/tests/test_build_components/common/uart_38400/rp2040-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for RP2040 Arduino tests - 38400 baud + +substitutions: + tx_pin: GPIO0 + rx_pin: GPIO1 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 38400 diff --git a/tests/test_build_components/common/uart_4800/esp32-ard.yaml b/tests/test_build_components/common/uart_4800/esp32-ard.yaml new file mode 100644 index 0000000000..7f7096e31d --- /dev/null +++ b/tests/test_build_components/common/uart_4800/esp32-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32 Arduino tests - 4800 baud + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 diff --git a/tests/test_build_components/common/uart_4800/esp32-c3-ard.yaml b/tests/test_build_components/common/uart_4800/esp32-c3-ard.yaml new file mode 100644 index 0000000000..3c814b76e4 --- /dev/null +++ b/tests/test_build_components/common/uart_4800/esp32-c3-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32-C3 Arduino tests - 4800 baud + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 diff --git a/tests/test_build_components/common/uart_4800/esp32-c3-idf.yaml b/tests/test_build_components/common/uart_4800/esp32-c3-idf.yaml new file mode 100644 index 0000000000..7b18789b19 --- /dev/null +++ b/tests/test_build_components/common/uart_4800/esp32-c3-idf.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32-C3 IDF tests - 4800 baud + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 diff --git a/tests/test_build_components/common/uart_4800/esp32-idf.yaml b/tests/test_build_components/common/uart_4800/esp32-idf.yaml new file mode 100644 index 0000000000..c2bd5b3edd --- /dev/null +++ b/tests/test_build_components/common/uart_4800/esp32-idf.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP32 IDF tests - 4800 baud + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 diff --git a/tests/test_build_components/common/uart_4800/esp8266-ard.yaml b/tests/test_build_components/common/uart_4800/esp8266-ard.yaml new file mode 100644 index 0000000000..afdbf4a599 --- /dev/null +++ b/tests/test_build_components/common/uart_4800/esp8266-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for ESP8266 Arduino tests - 4800 baud + +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 diff --git a/tests/test_build_components/common/uart_4800/rp2040-ard.yaml b/tests/test_build_components/common/uart_4800/rp2040-ard.yaml new file mode 100644 index 0000000000..3bf0d6ba47 --- /dev/null +++ b/tests/test_build_components/common/uart_4800/rp2040-ard.yaml @@ -0,0 +1,11 @@ +# Common UART configuration for RP2040 Arduino tests - 4800 baud + +substitutions: + tx_pin: GPIO0 + rx_pin: GPIO1 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 diff --git a/tests/test_build_components/common/uart_4800_even/esp32-ard.yaml b/tests/test_build_components/common/uart_4800_even/esp32-ard.yaml new file mode 100644 index 0000000000..053848615b --- /dev/null +++ b/tests/test_build_components/common/uart_4800_even/esp32-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32 Arduino tests - 4800 baud even parity + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 + parity: EVEN diff --git a/tests/test_build_components/common/uart_4800_even/esp32-c3-ard.yaml b/tests/test_build_components/common/uart_4800_even/esp32-c3-ard.yaml new file mode 100644 index 0000000000..b1370bc1cb --- /dev/null +++ b/tests/test_build_components/common/uart_4800_even/esp32-c3-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32-C3 Arduino tests - 4800 baud even parity + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 + parity: EVEN diff --git a/tests/test_build_components/common/uart_4800_even/esp32-c3-idf.yaml b/tests/test_build_components/common/uart_4800_even/esp32-c3-idf.yaml new file mode 100644 index 0000000000..173c768937 --- /dev/null +++ b/tests/test_build_components/common/uart_4800_even/esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32-C3 IDF tests - 4800 baud, EVEN parity + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 + parity: EVEN diff --git a/tests/test_build_components/common/uart_4800_even/esp32-idf.yaml b/tests/test_build_components/common/uart_4800_even/esp32-idf.yaml new file mode 100644 index 0000000000..eb850ec2dd --- /dev/null +++ b/tests/test_build_components/common/uart_4800_even/esp32-idf.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32 IDF tests - 4800 baud, EVEN parity + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 + parity: EVEN diff --git a/tests/test_build_components/common/uart_4800_even/esp8266-ard.yaml b/tests/test_build_components/common/uart_4800_even/esp8266-ard.yaml new file mode 100644 index 0000000000..0b6bd9eac1 --- /dev/null +++ b/tests/test_build_components/common/uart_4800_even/esp8266-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP8266 Arduino tests - 4800 baud even parity + +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 + parity: EVEN diff --git a/tests/test_build_components/common/uart_4800_even/rp2040-ard.yaml b/tests/test_build_components/common/uart_4800_even/rp2040-ard.yaml new file mode 100644 index 0000000000..c99421b791 --- /dev/null +++ b/tests/test_build_components/common/uart_4800_even/rp2040-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for RP2040 Arduino tests - 4800 baud even parity + +substitutions: + tx_pin: GPIO0 + rx_pin: GPIO1 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 + parity: EVEN diff --git a/tests/test_build_components/common/uart_9600_even/esp32-ard.yaml b/tests/test_build_components/common/uart_9600_even/esp32-ard.yaml new file mode 100644 index 0000000000..0a04f10705 --- /dev/null +++ b/tests/test_build_components/common/uart_9600_even/esp32-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32 Arduino tests - 9600 baud even parity + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + parity: EVEN diff --git a/tests/test_build_components/common/uart_9600_even/esp32-c3-ard.yaml b/tests/test_build_components/common/uart_9600_even/esp32-c3-ard.yaml new file mode 100644 index 0000000000..1341a91b4e --- /dev/null +++ b/tests/test_build_components/common/uart_9600_even/esp32-c3-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32-C3 Arduino tests - 9600 baud even parity + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + parity: EVEN diff --git a/tests/test_build_components/common/uart_9600_even/esp32-c3-idf.yaml b/tests/test_build_components/common/uart_9600_even/esp32-c3-idf.yaml new file mode 100644 index 0000000000..5a7bce2198 --- /dev/null +++ b/tests/test_build_components/common/uart_9600_even/esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32-C3 IDF tests - 9600 baud, EVEN parity + +substitutions: + tx_pin: GPIO20 + rx_pin: GPIO21 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + parity: EVEN diff --git a/tests/test_build_components/common/uart_9600_even/esp32-idf.yaml b/tests/test_build_components/common/uart_9600_even/esp32-idf.yaml new file mode 100644 index 0000000000..b60cf71b17 --- /dev/null +++ b/tests/test_build_components/common/uart_9600_even/esp32-idf.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP32 IDF tests - 9600 baud, EVEN parity + +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + parity: EVEN diff --git a/tests/test_build_components/common/uart_9600_even/esp8266-ard.yaml b/tests/test_build_components/common/uart_9600_even/esp8266-ard.yaml new file mode 100644 index 0000000000..300ec842df --- /dev/null +++ b/tests/test_build_components/common/uart_9600_even/esp8266-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for ESP8266 Arduino tests - 9600 baud even parity + +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + parity: EVEN diff --git a/tests/test_build_components/common/uart_9600_even/rp2040-ard.yaml b/tests/test_build_components/common/uart_9600_even/rp2040-ard.yaml new file mode 100644 index 0000000000..c281ae84b5 --- /dev/null +++ b/tests/test_build_components/common/uart_9600_even/rp2040-ard.yaml @@ -0,0 +1,12 @@ +# Common UART configuration for RP2040 Arduino tests - 9600 baud even parity + +substitutions: + tx_pin: GPIO0 + rx_pin: GPIO1 + +uart: + - id: uart_bus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + parity: EVEN From 3afa73b449b870be5a60de58d2c0a286bb0c8f2b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 11 Oct 2025 13:27:18 -1000 Subject: [PATCH 031/526] [ci] Filter out components without tests from CI test jobs (#11134 followup) (#11178) --- .github/workflows/ci.yml | 6 ++- script/determine-jobs.py | 13 ++++- tests/script/test_determine_jobs.py | 75 ++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4451007da0..f692b1f7d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -177,6 +177,7 @@ jobs: clang-tidy: ${{ steps.determine.outputs.clang-tidy }} python-linters: ${{ steps.determine.outputs.python-linters }} changed-components: ${{ steps.determine.outputs.changed-components }} + changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }} component-test-count: ${{ steps.determine.outputs.component-test-count }} steps: - name: Check out code from GitHub @@ -204,6 +205,7 @@ jobs: echo "clang-tidy=$(echo "$output" | jq -r '.clang_tidy')" >> $GITHUB_OUTPUT echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT echo "changed-components=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT + echo "changed-components-with-tests=$(echo "$output" | jq -c '.changed_components_with_tests')" >> $GITHUB_OUTPUT echo "component-test-count=$(echo "$output" | jq -r '.component_test_count')" >> $GITHUB_OUTPUT integration-tests: @@ -367,7 +369,7 @@ jobs: fail-fast: false max-parallel: 2 matrix: - file: ${{ fromJson(needs.determine-jobs.outputs.changed-components) }} + file: ${{ fromJson(needs.determine-jobs.outputs.changed-components-with-tests) }} steps: - name: Cache apt packages uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.5.3 @@ -414,7 +416,7 @@ jobs: . venv/bin/activate # Use intelligent splitter that groups components with same bus configs - components='${{ needs.determine-jobs.outputs.changed-components }}' + components='${{ needs.determine-jobs.outputs.changed-components-with-tests }}' echo "Splitting components intelligently..." output=$(python3 script/split_components_for_ci.py --components "$components" --batch-size 40 --output github) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index e26bc29c2f..a078fd8f9b 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -237,6 +237,16 @@ def main() -> None: result = subprocess.run(cmd, capture_output=True, text=True, check=True) changed_components = parse_list_components_output(result.stdout) + # Filter to only components that have test files + # Components without tests shouldn't generate CI test jobs + tests_dir = Path(root_path) / "tests" / "components" + changed_components_with_tests = [ + component + for component in changed_components + if (component_test_dir := tests_dir / component).exists() + and any(component_test_dir.glob("test.*.yaml")) + ] + # Build output output: dict[str, Any] = { "integration_tests": run_integration, @@ -244,7 +254,8 @@ def main() -> None: "clang_format": run_clang_format, "python_linters": run_python_linters, "changed_components": changed_components, - "component_test_count": len(changed_components), + "changed_components_with_tests": changed_components_with_tests, + "component_test_count": len(changed_components_with_tests), } # Output as JSON diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 7200afc2ee..5d8746f434 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -4,6 +4,7 @@ from collections.abc import Generator import importlib.util import json import os +from pathlib import Path import subprocess import sys from unittest.mock import Mock, call, patch @@ -90,7 +91,13 @@ def test_main_all_tests_should_run( assert output["clang_format"] is True assert output["python_linters"] is True assert output["changed_components"] == ["wifi", "api", "sensor"] - assert output["component_test_count"] == 3 + # changed_components_with_tests will only include components that actually have test files + assert "changed_components_with_tests" in output + assert isinstance(output["changed_components_with_tests"], list) + # component_test_count matches number of components with tests + assert output["component_test_count"] == len( + output["changed_components_with_tests"] + ) def test_main_no_tests_should_run( @@ -125,6 +132,7 @@ def test_main_no_tests_should_run( assert output["clang_format"] is False assert output["python_linters"] is False assert output["changed_components"] == [] + assert output["changed_components_with_tests"] == [] assert output["component_test_count"] == 0 @@ -197,7 +205,13 @@ def test_main_with_branch_argument( assert output["clang_format"] is False assert output["python_linters"] is True assert output["changed_components"] == ["mqtt"] - assert output["component_test_count"] == 1 + # changed_components_with_tests will only include components that actually have test files + assert "changed_components_with_tests" in output + assert isinstance(output["changed_components_with_tests"], list) + # component_test_count matches number of components with tests + assert output["component_test_count"] == len( + output["changed_components_with_tests"] + ) def test_should_run_integration_tests( @@ -377,3 +391,60 @@ def test_should_run_clang_format_with_branch() -> None: mock_changed.return_value = [] determine_jobs.should_run_clang_format("release") mock_changed.assert_called_once_with("release") + + +def test_main_filters_components_without_tests( + mock_should_run_integration_tests: Mock, + mock_should_run_clang_tidy: Mock, + mock_should_run_clang_format: Mock, + mock_should_run_python_linters: Mock, + mock_subprocess_run: Mock, + capsys: pytest.CaptureFixture[str], + tmp_path: Path, +) -> None: + """Test that components without test files are filtered out.""" + mock_should_run_integration_tests.return_value = False + mock_should_run_clang_tidy.return_value = False + mock_should_run_clang_format.return_value = False + mock_should_run_python_linters.return_value = False + + # Mock list-components.py output with 3 components + # wifi: has tests, sensor: has tests, airthings_ble: no tests + mock_result = Mock() + mock_result.stdout = "wifi\nsensor\nairthings_ble\n" + mock_subprocess_run.return_value = mock_result + + # Create test directory structure + tests_dir = tmp_path / "tests" / "components" + + # wifi has tests + wifi_dir = tests_dir / "wifi" + wifi_dir.mkdir(parents=True) + (wifi_dir / "test.esp32.yaml").write_text("test: config") + + # sensor has tests + sensor_dir = tests_dir / "sensor" + sensor_dir.mkdir(parents=True) + (sensor_dir / "test.esp8266.yaml").write_text("test: config") + + # airthings_ble exists but has no test files + airthings_dir = tests_dir / "airthings_ble" + airthings_dir.mkdir(parents=True) + + # Mock root_path to use tmp_path + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch("sys.argv", ["determine-jobs.py"]), + ): + determine_jobs.main() + + # Check output + captured = capsys.readouterr() + output = json.loads(captured.out) + + # changed_components should have all components + assert set(output["changed_components"]) == {"wifi", "sensor", "airthings_ble"} + # changed_components_with_tests should only have components with test files + assert set(output["changed_components_with_tests"]) == {"wifi", "sensor"} + # component_test_count should be based on components with tests + assert output["component_test_count"] == 2 From 2cc5e24b38b7217630525034ee3b4042cb22deb8 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Sat, 11 Oct 2025 20:44:44 -0400 Subject: [PATCH 032/526] [esp32] Change Arduino dev & latest to 3.3.2 (#11169) --- esphome/components/esp32/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index d9b8d067a4..56ab2eda88 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -325,8 +325,8 @@ def _is_framework_url(source: str) -> str: # - https://github.com/espressif/arduino-esp32/releases ARDUINO_FRAMEWORK_VERSION_LOOKUP = { "recommended": cv.Version(3, 2, 1), - "latest": cv.Version(3, 3, 1), - "dev": cv.Version(3, 3, 1), + "latest": cv.Version(3, 3, 2), + "dev": cv.Version(3, 3, 2), } ARDUINO_PLATFORM_VERSION_LOOKUP = { cv.Version(3, 3, 2): cv.Version(55, 3, 31, "1"), From 51fbc4f7a3296b7177486db6d80443b621dc0b93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Oct 2025 08:12:52 +0000 Subject: [PATCH 033/526] Bump aioesphomeapi from 41.13.0 to 41.14.0 (#11188) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8cc2b4ed45..64946263ea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251009.0 -aioesphomeapi==41.13.0 +aioesphomeapi==41.14.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import From 660adccda32b4603629cea1d7b0669b7219669f9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Oct 2025 02:58:56 -1000 Subject: [PATCH 034/526] [mipi_rgb] Fix pin conflicts introduced by shared SPI bus in #11134 (#11185) --- tests/components/mipi_rgb/test.esp32-s3-idf.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/components/mipi_rgb/test.esp32-s3-idf.yaml b/tests/components/mipi_rgb/test.esp32-s3-idf.yaml index 29f833c235..642292f7c4 100644 --- a/tests/components/mipi_rgb/test.esp32-s3-idf.yaml +++ b/tests/components/mipi_rgb/test.esp32-s3-idf.yaml @@ -40,9 +40,7 @@ display: - number: 17 blue: - number: 47 - allow_other_uses: true - - number: 41 - allow_other_uses: true + - number: 1 - number: 0 ignore_strapping_warning: true - number: 42 @@ -53,7 +51,7 @@ display: number: 45 ignore_strapping_warning: true hsync_pin: - number: 40 + number: 38 vsync_pin: number: 48 data_rate: 1000000.0 From cad747c672da80ab5484199fe7261843066d10e7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Oct 2025 08:25:35 -1000 Subject: [PATCH 035/526] [ci] Dynamic runner allocation: 8 for releases, 4 for dev (#11191) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f692b1f7d0..0363b5afdf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -433,7 +433,7 @@ jobs: if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) >= 100 strategy: fail-fast: false - max-parallel: 5 + max-parallel: ${{ (github.base_ref == 'beta' || github.base_ref == 'release') && 8 || 4 }} matrix: components: ${{ fromJson(needs.test-build-components-splitter.outputs.matrix) }} steps: From 9b6e8b4b414ede5043ca0cbd0f766b4ca446f9bf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Oct 2025 08:26:28 -1000 Subject: [PATCH 036/526] [wifi] Fix missed string literal in flash on ESP8266 (#11187) --- esphome/components/wifi/wifi_component.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 2e083d4c68..71ee4271ba 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -576,8 +576,9 @@ __attribute__((noinline)) static void log_scan_result(const WiFiScanResult &res) format_mac_addr_upper(bssid.data(), bssid_s); if (res.get_matches()) { - ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), res.get_is_hidden() ? "(HIDDEN) " : "", - bssid_s, LOG_STR_ARG(get_signal_bars(res.get_rssi()))); + ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), + res.get_is_hidden() ? LOG_STR_LITERAL("(HIDDEN) ") : LOG_STR_LITERAL(""), bssid_s, + LOG_STR_ARG(get_signal_bars(res.get_rssi()))); ESP_LOGD(TAG, " Channel: %u\n" " RSSI: %d dB", From 6bc9ed08107b89e75a3df7efe5166c7c01c8b90c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Oct 2025 08:27:43 -1000 Subject: [PATCH 037/526] [ota] Increase handshake timeout to 20s now that auth is non-blocking (#11186) --- esphome/components/esphome/ota/ota_esphome.cpp | 2 +- esphome/espota2.py | 2 +- tests/unit_tests/test_espota2.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index b65bfc5ab8..569268ea15 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -29,7 +29,7 @@ namespace esphome { static const char *const TAG = "esphome.ota"; static constexpr uint16_t OTA_BLOCK_SIZE = 8192; static constexpr size_t OTA_BUFFER_SIZE = 1024; // buffer size for OTA data transfer -static constexpr uint32_t OTA_SOCKET_TIMEOUT_HANDSHAKE = 10000; // milliseconds for initial handshake +static constexpr uint32_t OTA_SOCKET_TIMEOUT_HANDSHAKE = 20000; // milliseconds for initial handshake static constexpr uint32_t OTA_SOCKET_TIMEOUT_DATA = 90000; // milliseconds for data transfer #ifdef USE_OTA_PASSWORD diff --git a/esphome/espota2.py b/esphome/espota2.py index 2712d00127..17a1da8235 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -410,7 +410,7 @@ def run_ota_impl_( af, socktype, _, _, sa = r _LOGGER.info("Connecting to %s port %s...", sa[0], sa[1]) sock = socket.socket(af, socktype) - sock.settimeout(10.0) + sock.settimeout(20.0) try: sock.connect(sa) except OSError as err: diff --git a/tests/unit_tests/test_espota2.py b/tests/unit_tests/test_espota2.py index bd1a6bde81..52c72291d6 100644 --- a/tests/unit_tests/test_espota2.py +++ b/tests/unit_tests/test_espota2.py @@ -493,7 +493,7 @@ def test_run_ota_impl_successful( assert result_host == "192.168.1.100" # Verify socket was configured correctly - mock_socket.settimeout.assert_called_with(10.0) + mock_socket.settimeout.assert_called_with(20.0) mock_socket.connect.assert_called_once_with(("192.168.1.100", 3232)) mock_socket.close.assert_called_once() From 9ebfa9aaa82b813784b61c397ac364557fb5d47e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Oct 2025 08:30:58 -1000 Subject: [PATCH 038/526] [esp32_improv] Fix state not transitioning to PROVISIONED when WiFi configured via captive portal (#11181) --- .../esp32_improv/esp32_improv_component.cpp | 54 +++++++++++-------- .../esp32_improv/esp32_improv_component.h | 1 + 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index f773083890..d83caf931b 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -143,6 +143,7 @@ void ESP32ImprovComponent::loop() { #else this->set_state_(improv::STATE_AUTHORIZED); #endif + this->check_wifi_connection_(); break; } case improv::STATE_AUTHORIZED: { @@ -156,31 +157,12 @@ void ESP32ImprovComponent::loop() { if (!this->check_identify_()) { this->set_status_indicator_state_((now % 1000) < 500); } + this->check_wifi_connection_(); break; } case improv::STATE_PROVISIONING: { this->set_status_indicator_state_((now % 200) < 100); - if (wifi::global_wifi_component->is_connected()) { - wifi::global_wifi_component->save_wifi_sta(this->connecting_sta_.get_ssid(), - this->connecting_sta_.get_password()); - this->connecting_sta_ = {}; - this->cancel_timeout("wifi-connect-timeout"); - this->set_state_(improv::STATE_PROVISIONED); - - std::vector urls = {ESPHOME_MY_LINK}; -#ifdef USE_WEBSERVER - for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) { - if (ip.is_ip4()) { - std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT); - urls.push_back(webserver_url); - break; - } - } -#endif - std::vector data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls); - this->send_response_(data); - this->stop(); - } + this->check_wifi_connection_(); break; } case improv::STATE_PROVISIONED: { @@ -392,6 +374,36 @@ void ESP32ImprovComponent::on_wifi_connect_timeout_() { wifi::global_wifi_component->clear_sta(); } +void ESP32ImprovComponent::check_wifi_connection_() { + if (!wifi::global_wifi_component->is_connected()) { + return; + } + + if (this->state_ == improv::STATE_PROVISIONING) { + wifi::global_wifi_component->save_wifi_sta(this->connecting_sta_.get_ssid(), this->connecting_sta_.get_password()); + this->connecting_sta_ = {}; + this->cancel_timeout("wifi-connect-timeout"); + + std::vector urls = {ESPHOME_MY_LINK}; +#ifdef USE_WEBSERVER + for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) { + if (ip.is_ip4()) { + std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT); + urls.push_back(webserver_url); + break; + } + } +#endif + std::vector data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls); + this->send_response_(data); + } else if (this->is_active() && this->state_ != improv::STATE_PROVISIONED) { + ESP_LOGD(TAG, "WiFi provisioned externally"); + } + + this->set_state_(improv::STATE_PROVISIONED); + this->stop(); +} + void ESP32ImprovComponent::advertise_service_data_() { uint8_t service_data[IMPROV_SERVICE_DATA_SIZE] = {}; service_data[0] = IMPROV_PROTOCOL_ID_1; // PR diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index eb07e09dce..6782430ffe 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -111,6 +111,7 @@ class ESP32ImprovComponent : public Component { void send_response_(std::vector &response); void process_incoming_data_(); void on_wifi_connect_timeout_(); + void check_wifi_connection_(); bool check_identify_(); void advertise_service_data_(); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG From 1f13d44c1b9ebed5abd14aa43200f4ed269418e1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Oct 2025 09:04:30 -1000 Subject: [PATCH 039/526] [usb_host] Fix transfer slot exhaustion at high data rates and add configurable max_transfer_requests (#11174) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- esphome/components/usb_host/__init__.py | 11 +++- esphome/components/usb_host/usb_host.h | 42 ++++++++------ .../components/usb_host/usb_host_client.cpp | 57 ++++++------------- esphome/core/defines.h | 1 + .../usb_host/test.esp32-s3-idf.yaml | 1 + 5 files changed, 54 insertions(+), 58 deletions(-) diff --git a/esphome/components/usb_host/__init__.py b/esphome/components/usb_host/__init__.py index de734bf425..d452e0e9fa 100644 --- a/esphome/components/usb_host/__init__.py +++ b/esphome/components/usb_host/__init__.py @@ -9,6 +9,7 @@ from esphome.components.esp32 import ( import esphome.config_validation as cv from esphome.const import CONF_DEVICES, CONF_ID from esphome.cpp_types import Component +from esphome.types import ConfigType AUTO_LOAD = ["bytebuffer"] CODEOWNERS = ["@clydebarrow"] @@ -20,6 +21,7 @@ USBClient = usb_host_ns.class_("USBClient", Component) CONF_VID = "vid" CONF_PID = "pid" CONF_ENABLE_HUBS = "enable_hubs" +CONF_MAX_TRANSFER_REQUESTS = "max_transfer_requests" def usb_device_schema(cls=USBClient, vid: int = None, pid: [int] = None) -> cv.Schema: @@ -44,6 +46,9 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(USBHost), cv.Optional(CONF_ENABLE_HUBS, default=False): cv.boolean, + cv.Optional(CONF_MAX_TRANSFER_REQUESTS, default=16): cv.int_range( + min=1, max=32 + ), cv.Optional(CONF_DEVICES): cv.ensure_list(usb_device_schema()), } ), @@ -58,10 +63,14 @@ async def register_usb_client(config): return var -async def to_code(config): +async def to_code(config: ConfigType) -> None: add_idf_sdkconfig_option("CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE", 1024) if config.get(CONF_ENABLE_HUBS): add_idf_sdkconfig_option("CONFIG_USB_HOST_HUBS_SUPPORTED", True) + + max_requests = config[CONF_MAX_TRANSFER_REQUESTS] + cg.add_define("USB_HOST_MAX_REQUESTS", max_requests) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) for device in config.get(CONF_DEVICES) or (): diff --git a/esphome/components/usb_host/usb_host.h b/esphome/components/usb_host/usb_host.h index 4f8d2ec9a8..43b24a54a5 100644 --- a/esphome/components/usb_host/usb_host.h +++ b/esphome/components/usb_host/usb_host.h @@ -2,6 +2,7 @@ // Should not be needed, but it's required to pass CI clang-tidy checks #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32P4) +#include "esphome/core/defines.h" #include "esphome/core/component.h" #include #include "usb/usb_host.h" @@ -16,23 +17,25 @@ namespace usb_host { // THREADING MODEL: // This component uses a dedicated USB task for event processing to prevent data loss. -// - USB Task (high priority): Handles USB events, executes transfer callbacks -// - Main Loop Task: Initiates transfers, processes completion events +// - USB Task (high priority): Handles USB events, executes transfer callbacks, releases transfer slots +// - Main Loop Task: Initiates transfers, processes device connect/disconnect events // // Thread-safe communication: // - Lock-free queues for USB task -> main loop events (SPSC pattern) -// - Lock-free TransferRequest pool using atomic bitmask (MCSP pattern) +// - Lock-free TransferRequest pool using atomic bitmask (MCMP pattern - multi-consumer, multi-producer) // // TransferRequest pool access pattern: // - get_trq_() [allocate]: Called from BOTH USB task and main loop threads // * USB task: via USB UART input callbacks that restart transfers immediately // * Main loop: for output transfers and flow-controlled input restarts -// - release_trq() [deallocate]: Called from main loop thread only +// - release_trq() [deallocate]: Called from BOTH USB task and main loop threads +// * USB task: immediately after transfer callback completes (critical for preventing slot exhaustion) +// * Main loop: when transfer submission fails // -// The multi-threaded allocation is intentional for performance: -// - USB task can immediately restart input transfers without context switching +// The multi-threaded allocation/deallocation is intentional for performance: +// - USB task can immediately restart input transfers and release slots without context switching // - Main loop controls backpressure by deciding when to restart after consuming data -// The atomic bitmask ensures thread-safe allocation without mutex blocking. +// The atomic bitmask ensures thread-safe allocation/deallocation without mutex blocking. static const char *const TAG = "usb_host"; @@ -52,8 +55,17 @@ static const uint8_t USB_DIR_IN = 1 << 7; static const uint8_t USB_DIR_OUT = 0; static const size_t SETUP_PACKET_SIZE = 8; -static const size_t MAX_REQUESTS = 16; // maximum number of outstanding requests possible. -static_assert(MAX_REQUESTS <= 16, "MAX_REQUESTS must be <= 16 to fit in uint16_t bitmask"); +static const size_t MAX_REQUESTS = USB_HOST_MAX_REQUESTS; // maximum number of outstanding requests possible. +static_assert(MAX_REQUESTS >= 1 && MAX_REQUESTS <= 32, "MAX_REQUESTS must be between 1 and 32"); + +// Select appropriate bitmask type for tracking allocation of TransferRequest slots. +// The bitmask must have at least as many bits as MAX_REQUESTS, so: +// - Use uint16_t for up to 16 requests (MAX_REQUESTS <= 16) +// - Use uint32_t for 17-32 requests (MAX_REQUESTS > 16) +// This is tied to the static_assert above, which enforces MAX_REQUESTS is between 1 and 32. +// If MAX_REQUESTS is increased above 32, this logic and the static_assert must be updated. +using trq_bitmask_t = std::conditional<(MAX_REQUESTS <= 16), uint16_t, uint32_t>::type; + static constexpr size_t USB_EVENT_QUEUE_SIZE = 32; // Size of event queue between USB task and main loop static constexpr size_t USB_TASK_STACK_SIZE = 4096; // Stack size for USB task (same as ESP-IDF USB examples) static constexpr UBaseType_t USB_TASK_PRIORITY = 5; // Higher priority than main loop (tskIDLE_PRIORITY + 5) @@ -83,8 +95,6 @@ struct TransferRequest { enum EventType : uint8_t { EVENT_DEVICE_NEW, EVENT_DEVICE_GONE, - EVENT_TRANSFER_COMPLETE, - EVENT_CONTROL_COMPLETE, }; struct UsbEvent { @@ -96,9 +106,6 @@ struct UsbEvent { struct { usb_device_handle_t handle; } device_gone; - struct { - TransferRequest *trq; - } transfer; } data; // Required for EventPool - no cleanup needed for POD types @@ -163,10 +170,9 @@ class USBClient : public Component { uint16_t pid_{}; // Lock-free pool management using atomic bitmask (no dynamic allocation) // Bit i = 1: requests_[i] is in use, Bit i = 0: requests_[i] is available - // Supports multiple concurrent consumers (both threads can allocate) - // Single producer for deallocation (main loop only) - // Limited to 16 slots by uint16_t size (enforced by static_assert) - std::atomic trq_in_use_; + // Supports multiple concurrent consumers and producers (both threads can allocate/deallocate) + // Bitmask type automatically selected: uint16_t for <= 16 slots, uint32_t for 17-32 slots + std::atomic trq_in_use_; TransferRequest requests_[MAX_REQUESTS]{}; }; class USBHost : public Component { diff --git a/esphome/components/usb_host/usb_host_client.cpp b/esphome/components/usb_host/usb_host_client.cpp index b26385a8ef..2139ed869a 100644 --- a/esphome/components/usb_host/usb_host_client.cpp +++ b/esphome/components/usb_host/usb_host_client.cpp @@ -228,12 +228,6 @@ void USBClient::loop() { case EVENT_DEVICE_GONE: this->on_removed(event->data.device_gone.handle); break; - case EVENT_TRANSFER_COMPLETE: - case EVENT_CONTROL_COMPLETE: { - auto *trq = event->data.transfer.trq; - this->release_trq(trq); - break; - } } // Return event to pool for reuse this->event_pool.release(event); @@ -313,25 +307,6 @@ void USBClient::on_removed(usb_device_handle_t handle) { } } -// Helper to queue transfer cleanup to main loop -static void queue_transfer_cleanup(TransferRequest *trq, EventType type) { - auto *client = trq->client; - - // Allocate event from pool - UsbEvent *event = client->event_pool.allocate(); - if (event == nullptr) { - // No events available - increment counter for periodic logging - client->event_queue.increment_dropped_count(); - return; - } - - event->type = type; - event->data.transfer.trq = trq; - - // Push to lock-free queue (always succeeds since pool size == queue size) - client->event_queue.push(event); -} - // CALLBACK CONTEXT: USB task (called from usb_host_client_handle_events in USB task) static void control_callback(const usb_transfer_t *xfer) { auto *trq = static_cast(xfer->context); @@ -346,8 +321,9 @@ static void control_callback(const usb_transfer_t *xfer) { trq->callback(trq->status); } - // Queue cleanup to main loop - queue_transfer_cleanup(trq, EVENT_CONTROL_COMPLETE); + // Release transfer slot immediately in USB task + // The release_trq() uses thread-safe atomic operations + trq->client->release_trq(trq); } // THREAD CONTEXT: Called from both USB task and main loop threads (multi-consumer) @@ -358,20 +334,20 @@ static void control_callback(const usb_transfer_t *xfer) { // This multi-threaded access is intentional for performance - USB task can // immediately restart transfers without waiting for main loop scheduling. TransferRequest *USBClient::get_trq_() { - uint16_t mask = this->trq_in_use_.load(std::memory_order_relaxed); + trq_bitmask_t mask = this->trq_in_use_.load(std::memory_order_relaxed); // Find first available slot (bit = 0) and try to claim it atomically // We use a while loop to allow retrying the same slot after CAS failure size_t i = 0; while (i != MAX_REQUESTS) { - if (mask & (1U << i)) { + if (mask & (static_cast(1) << i)) { // Slot is in use, move to next slot i++; continue; } // Slot i appears available, try to claim it atomically - uint16_t desired = mask | (1U << i); // Set bit i to mark as in-use + trq_bitmask_t desired = mask | (static_cast(1) << i); // Set bit i to mark as in-use if (this->trq_in_use_.compare_exchange_weak(mask, desired, std::memory_order_acquire, std::memory_order_relaxed)) { // Successfully claimed slot i - prepare the TransferRequest @@ -386,7 +362,7 @@ TransferRequest *USBClient::get_trq_() { i = 0; } - ESP_LOGE(TAG, "All %d transfer slots in use", MAX_REQUESTS); + ESP_LOGE(TAG, "All %zu transfer slots in use", MAX_REQUESTS); return nullptr; } void USBClient::disconnect() { @@ -452,8 +428,11 @@ static void transfer_callback(usb_transfer_t *xfer) { trq->callback(trq->status); } - // Queue cleanup to main loop - queue_transfer_cleanup(trq, EVENT_TRANSFER_COMPLETE); + // Release transfer slot AFTER callback completes to prevent slot exhaustion + // This is critical for high-throughput transfers (e.g., USB UART at 115200 baud) + // The callback has finished accessing xfer->data_buffer, so it's safe to release + // The release_trq() uses thread-safe atomic operations + trq->client->release_trq(trq); } /** * Performs a transfer input operation. @@ -521,12 +500,12 @@ void USBClient::dump_config() { " Product id %04X", this->vid_, this->pid_); } -// THREAD CONTEXT: Only called from main loop thread (single producer for deallocation) -// - Via event processing when handling EVENT_TRANSFER_COMPLETE/EVENT_CONTROL_COMPLETE -// - Directly when transfer submission fails +// THREAD CONTEXT: Called from both USB task and main loop threads +// - USB task: Immediately after transfer callback completes +// - Main loop: When transfer submission fails // // THREAD SAFETY: Lock-free using atomic AND to clear bit -// Single-producer pattern makes this simpler than allocation +// Thread-safe atomic operation allows multi-threaded deallocation void USBClient::release_trq(TransferRequest *trq) { if (trq == nullptr) return; @@ -540,8 +519,8 @@ void USBClient::release_trq(TransferRequest *trq) { // Atomically clear bit i to mark slot as available // fetch_and with inverted bitmask clears the bit atomically - uint16_t bit = 1U << index; - this->trq_in_use_.fetch_and(static_cast(~bit), std::memory_order_release); + trq_bitmask_t bit = static_cast(1) << index; + this->trq_in_use_.fetch_and(static_cast(~bit), std::memory_order_release); } } // namespace usb_host diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 955d0f987c..ae44e16624 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -193,6 +193,7 @@ #define USE_WEBSERVER_PORT 80 // NOLINT #define USE_WEBSERVER_SORTING #define USE_WIFI_11KV_SUPPORT +#define USB_HOST_MAX_REQUESTS 16 #ifdef USE_ARDUINO #define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 2, 1) diff --git a/tests/components/usb_host/test.esp32-s3-idf.yaml b/tests/components/usb_host/test.esp32-s3-idf.yaml index a2892872e5..5360d1f6ff 100644 --- a/tests/components/usb_host/test.esp32-s3-idf.yaml +++ b/tests/components/usb_host/test.esp32-s3-idf.yaml @@ -1,4 +1,5 @@ usb_host: + max_transfer_requests: 32 # Test uint32_t bitmask path (17-32 requests) devices: - id: device_1 vid: 0x1234 From 59f728488eedda6976c27a041a27b41c5d772456 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 13 Oct 2025 12:58:30 +1300 Subject: [PATCH 040/526] [media_player.speaker] Dynamic auto load (#11084) Co-authored-by: J. Nick Koston --- esphome/components/psram/__init__.py | 2 ++ .../speaker/media_player/__init__.py | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/esphome/components/psram/__init__.py b/esphome/components/psram/__init__.py index 6b85e7f720..8e4f9d7eac 100644 --- a/esphome/components/psram/__init__.py +++ b/esphome/components/psram/__init__.py @@ -63,6 +63,8 @@ SPIRAM_SPEEDS = { def supported() -> bool: + if not CORE.is_esp32: + return False variant = get_esp32_variant() return variant in SPIRAM_MODES diff --git a/esphome/components/speaker/media_player/__init__.py b/esphome/components/speaker/media_player/__init__.py index 69ea0a53c6..7537a61e4e 100644 --- a/esphome/components/speaker/media_player/__init__.py +++ b/esphome/components/speaker/media_player/__init__.py @@ -6,7 +6,7 @@ from pathlib import Path from esphome import automation, external_files import esphome.codegen as cg -from esphome.components import audio, esp32, media_player, speaker +from esphome.components import audio, esp32, media_player, psram, speaker import esphome.config_validation as cv from esphome.const import ( CONF_BUFFER_SIZE, @@ -26,10 +26,21 @@ from esphome.const import ( from esphome.core import CORE, HexInt from esphome.core.entity_helpers import inherit_property_from from esphome.external_files import download_content +from esphome.types import ConfigType _LOGGER = logging.getLogger(__name__) -AUTO_LOAD = ["audio", "psram"] + +def AUTO_LOAD(config: ConfigType) -> list[str]: + load = ["audio"] + if ( + not config + or config.get(CONF_TASK_STACK_IN_PSRAM) + or config.get(CONF_CODEC_SUPPORT_ENABLED) + ): + return load + ["psram"] + return load + CODEOWNERS = ["@kahrendt", "@synesthesiam"] DOMAIN = "media_player" @@ -279,7 +290,9 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range( min=4000, max=4000000 ), - cv.Optional(CONF_CODEC_SUPPORT_ENABLED, default=True): cv.boolean, + cv.Optional( + CONF_CODEC_SUPPORT_ENABLED, default=psram.supported() + ): cv.boolean, cv.Optional(CONF_FILES): cv.ensure_list(MEDIA_FILE_TYPE_SCHEMA), cv.Optional(CONF_TASK_STACK_IN_PSRAM, default=False): cv.boolean, cv.Optional(CONF_VOLUME_INCREMENT, default=0.05): cv.percentage, From be2c859df3f3a5b237b9283f564b190d862c9767 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 08:01:47 -1000 Subject: [PATCH 041/526] [web_server] Consolidate duplicate client connection checks (saves 288 bytes of flash) (#11116) --- .../components/web_server/list_entities.cpp | 40 ----------------- esphome/components/web_server/web_server.cpp | 43 +++---------------- .../web_server_idf/web_server_idf.cpp | 3 ++ 3 files changed, 10 insertions(+), 76 deletions(-) diff --git a/esphome/components/web_server/list_entities.cpp b/esphome/components/web_server/list_entities.cpp index 3eb3764857..6b27545549 100644 --- a/esphome/components/web_server/list_entities.cpp +++ b/esphome/components/web_server/list_entities.cpp @@ -19,72 +19,54 @@ ListEntitiesIterator::~ListEntitiesIterator() {} #ifdef USE_BINARY_SENSOR bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::binary_sensor_all_json_generator); return true; } #endif #ifdef USE_COVER bool ListEntitiesIterator::on_cover(cover::Cover *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::cover_all_json_generator); return true; } #endif #ifdef USE_FAN bool ListEntitiesIterator::on_fan(fan::Fan *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::fan_all_json_generator); return true; } #endif #ifdef USE_LIGHT bool ListEntitiesIterator::on_light(light::LightState *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::light_all_json_generator); return true; } #endif #ifdef USE_SENSOR bool ListEntitiesIterator::on_sensor(sensor::Sensor *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::sensor_all_json_generator); return true; } #endif #ifdef USE_SWITCH bool ListEntitiesIterator::on_switch(switch_::Switch *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::switch_all_json_generator); return true; } #endif #ifdef USE_BUTTON bool ListEntitiesIterator::on_button(button::Button *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::button_all_json_generator); return true; } #endif #ifdef USE_TEXT_SENSOR bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::text_sensor_all_json_generator); return true; } #endif #ifdef USE_LOCK bool ListEntitiesIterator::on_lock(lock::Lock *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::lock_all_json_generator); return true; } @@ -92,8 +74,6 @@ bool ListEntitiesIterator::on_lock(lock::Lock *obj) { #ifdef USE_VALVE bool ListEntitiesIterator::on_valve(valve::Valve *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::valve_all_json_generator); return true; } @@ -101,8 +81,6 @@ bool ListEntitiesIterator::on_valve(valve::Valve *obj) { #ifdef USE_CLIMATE bool ListEntitiesIterator::on_climate(climate::Climate *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::climate_all_json_generator); return true; } @@ -110,8 +88,6 @@ bool ListEntitiesIterator::on_climate(climate::Climate *obj) { #ifdef USE_NUMBER bool ListEntitiesIterator::on_number(number::Number *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::number_all_json_generator); return true; } @@ -119,8 +95,6 @@ bool ListEntitiesIterator::on_number(number::Number *obj) { #ifdef USE_DATETIME_DATE bool ListEntitiesIterator::on_date(datetime::DateEntity *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::date_all_json_generator); return true; } @@ -128,8 +102,6 @@ bool ListEntitiesIterator::on_date(datetime::DateEntity *obj) { #ifdef USE_DATETIME_TIME bool ListEntitiesIterator::on_time(datetime::TimeEntity *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::time_all_json_generator); return true; } @@ -137,8 +109,6 @@ bool ListEntitiesIterator::on_time(datetime::TimeEntity *obj) { #ifdef USE_DATETIME_DATETIME bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::datetime_all_json_generator); return true; } @@ -146,8 +116,6 @@ bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *obj) { #ifdef USE_TEXT bool ListEntitiesIterator::on_text(text::Text *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::text_all_json_generator); return true; } @@ -155,8 +123,6 @@ bool ListEntitiesIterator::on_text(text::Text *obj) { #ifdef USE_SELECT bool ListEntitiesIterator::on_select(select::Select *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::select_all_json_generator); return true; } @@ -164,8 +130,6 @@ bool ListEntitiesIterator::on_select(select::Select *obj) { #ifdef USE_ALARM_CONTROL_PANEL bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::alarm_control_panel_all_json_generator); return true; } @@ -173,8 +137,6 @@ bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont #ifdef USE_EVENT bool ListEntitiesIterator::on_event(event::Event *obj) { - if (this->events_->count() == 0) - return true; // Null event type, since we are just iterating over entities this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::event_all_json_generator); return true; @@ -183,8 +145,6 @@ bool ListEntitiesIterator::on_event(event::Event *obj) { #ifdef USE_UPDATE bool ListEntitiesIterator::on_update(update::UpdateEntity *obj) { - if (this->events_->count() == 0) - return true; this->events_->deferrable_send_state(obj, "state_detail_all", WebServer::update_all_json_generator); return true; } diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index cfd5fc947b..6f554ac958 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -152,6 +152,10 @@ void DeferredUpdateEventSource::loop() { void DeferredUpdateEventSource::deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator) { + // Skip if no connected clients to avoid unnecessary deferred queue processing + if (this->count() == 0) + return; + // allow all json "details_all" to go through before publishing bare state events, this avoids unnamed entries showing // up in the web GUI and reduces event load during initial connect if (!entities_iterator_.completed() && 0 != strcmp(event_type, "state_detail_all")) @@ -197,6 +201,9 @@ void DeferredUpdateEventSourceList::loop() { void DeferredUpdateEventSourceList::deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator) { + // Skip if no event sources (no connected clients) to avoid unnecessary iteration + if (this->empty()) + return; for (DeferredUpdateEventSource *dues : *this) { dues->deferrable_send_state(source, event_type, message_generator); } @@ -424,8 +431,6 @@ static JsonDetail get_request_detail(AsyncWebServerRequest *request) { #ifdef USE_SENSOR void WebServer::on_sensor_update(sensor::Sensor *obj, float state) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", sensor_state_json_generator); } void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -473,8 +478,6 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail #ifdef USE_TEXT_SENSOR void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", text_sensor_state_json_generator); } void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -514,8 +517,6 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std: #ifdef USE_SWITCH void WebServer::on_switch_update(switch_::Switch *obj, bool state) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", switch_state_json_generator); } void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -627,8 +628,6 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) #ifdef USE_BINARY_SENSOR void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", binary_sensor_state_json_generator); } void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -667,8 +666,6 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool #ifdef USE_FAN void WebServer::on_fan_update(fan::Fan *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", fan_state_json_generator); } void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -743,8 +740,6 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) { #ifdef USE_LIGHT void WebServer::on_light_update(light::LightState *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", light_state_json_generator); } void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -819,8 +814,6 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi #ifdef USE_COVER void WebServer::on_cover_update(cover::Cover *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", cover_state_json_generator); } void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -906,8 +899,6 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) { #ifdef USE_NUMBER void WebServer::on_number_update(number::Number *obj, float state) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", number_state_json_generator); } void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -975,8 +966,6 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail #ifdef USE_DATETIME_DATE void WebServer::on_date_update(datetime::DateEntity *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", date_state_json_generator); } void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1034,8 +1023,6 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con #ifdef USE_DATETIME_TIME void WebServer::on_time_update(datetime::TimeEntity *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", time_state_json_generator); } void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1092,8 +1079,6 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con #ifdef USE_DATETIME_DATETIME void WebServer::on_datetime_update(datetime::DateTimeEntity *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", datetime_state_json_generator); } void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1151,8 +1136,6 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s #ifdef USE_TEXT void WebServer::on_text_update(text::Text *obj, const std::string &state) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", text_state_json_generator); } void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1212,8 +1195,6 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json #ifdef USE_SELECT void WebServer::on_select_update(select::Select *obj, const std::string &state, size_t index) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", select_state_json_generator); } void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1270,8 +1251,6 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value #ifdef USE_CLIMATE void WebServer::on_climate_update(climate::Climate *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", climate_state_json_generator); } void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1412,8 +1391,6 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf #ifdef USE_LOCK void WebServer::on_lock_update(lock::Lock *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", lock_state_json_generator); } void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1485,8 +1462,6 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet #ifdef USE_VALVE void WebServer::on_valve_update(valve::Valve *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", valve_state_json_generator); } void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1568,8 +1543,6 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) { #ifdef USE_ALARM_CONTROL_PANEL void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", alarm_control_panel_state_json_generator); } void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1714,8 +1687,6 @@ static const char *update_state_to_string(update::UpdateState state) { } void WebServer::on_update(update::UpdateEntity *obj) { - if (this->events_.empty()) - return; this->events_.deferrable_send_state(obj, "state", update_state_json_generator); } void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match) { diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index b38c5fb92a..d90efd18bc 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -412,6 +412,9 @@ void AsyncEventSource::try_send_nodefer(const char *message, const char *event, void AsyncEventSource::deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator) { + // Skip if no connected clients to avoid unnecessary processing + if (this->empty()) + return; for (auto *ses : this->sessions_) { if (ses->fd_.load() != 0) { // Skip dead sessions ses->deferrable_send_state(source, event_type, message_generator); From bcc424afed08e1c2658c0d89662c25c2c8e504ed Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 08:21:19 -1000 Subject: [PATCH 042/526] [web_server] Reduce code duplication in JSON generation with helper functions (#11117) --- esphome/components/web_server/web_server.cpp | 54 ++++++-------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 6f554ac958..f18f21b16b 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -458,13 +458,8 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail const auto uom_ref = obj->get_unit_of_measurement_ref(); - // Build JSON directly inline - std::string state; - if (std::isnan(value)) { - state = "NA"; - } else { - state = value_accuracy_with_uom_to_string(value, obj->get_accuracy_decimals(), uom_ref); - } + std::string state = + std::isnan(value) ? "NA" : value_accuracy_with_uom_to_string(value, obj->get_accuracy_decimals(), uom_ref); set_json_icon_state_value(root, obj, "sensor", state, value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); @@ -795,8 +790,7 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "light", start_config); - root["state"] = obj->remote_values.is_on() ? "ON" : "OFF"; + set_json_value(root, obj, "light", obj->remote_values.is_on() ? "ON" : "OFF", start_config); light::LightJSONSchema::dump_json(*obj, root); if (start_config == DETAIL_ALL) { @@ -939,7 +933,13 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail const auto uom_ref = obj->traits.get_unit_of_measurement_ref(); - set_json_id(root, obj, "number", start_config); + std::string val_str = std::isnan(value) + ? "\"NaN\"" + : value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); + std::string state_str = std::isnan(value) ? "NA" + : value_accuracy_with_uom_to_string( + value, step_to_accuracy_decimals(obj->traits.get_step()), uom_ref); + set_json_icon_state_value(root, obj, "number", state_str, val_str, start_config); if (start_config == DETAIL_ALL) { root["min_value"] = value_accuracy_to_string(obj->traits.get_min_value(), step_to_accuracy_decimals(obj->traits.get_step())); @@ -951,14 +951,6 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail root["uom"] = uom_ref; this->add_sorting_info_(root, obj); } - if (std::isnan(value)) { - root["value"] = "\"NaN\""; - root["state"] = "NA"; - } else { - root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); - root["state"] = - value_accuracy_with_uom_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()), uom_ref); - } return builder.serialize(); } @@ -1009,10 +1001,8 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "date", start_config); std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day); - root["value"] = value; - root["state"] = value; + set_json_icon_state_value(root, obj, "date", value, value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); } @@ -1065,10 +1055,8 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "time", start_config); std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second); - root["value"] = value; - root["state"] = value; + set_json_icon_state_value(root, obj, "time", value, value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); } @@ -1121,11 +1109,9 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "datetime", start_config); std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour, obj->minute, obj->second); - root["value"] = value; - root["state"] = value; + set_json_icon_state_value(root, obj, "datetime", value, value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); } @@ -1174,16 +1160,11 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "text", start_config); + std::string state = obj->traits.get_mode() == text::TextMode::TEXT_MODE_PASSWORD ? "********" : value; + set_json_icon_state_value(root, obj, "text", state, value, start_config); root["min_length"] = obj->traits.get_min_length(); root["max_length"] = obj->traits.get_max_length(); root["pattern"] = obj->traits.get_pattern(); - if (obj->traits.get_mode() == text::TextMode::TEXT_MODE_PASSWORD) { - root["state"] = "********"; - } else { - root["state"] = value; - } - root["value"] = value; if (start_config == DETAIL_ALL) { root["mode"] = (int) obj->traits.get_mode(); this->add_sorting_info_(root, obj); @@ -1725,9 +1706,8 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_id(root, obj, "update", start_config); - root["value"] = obj->update_info.latest_version; - root["state"] = update_state_to_string(obj->state); + set_json_icon_state_value(root, obj, "update", update_state_to_string(obj->state), obj->update_info.latest_version, + start_config); if (start_config == DETAIL_ALL) { root["current_version"] = obj->update_info.current_version; root["title"] = obj->update_info.title; From c10f68ef0cc9e68eb6a4f1f1eff3cab6766281cf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 08:24:57 -1000 Subject: [PATCH 043/526] [mdns] Conditionally store services to reduce RAM usage by 200-464 bytes (#11180) --- esphome/components/mdns/__init__.py | 19 ++++++++++++++++++- esphome/components/mdns/mdns_component.cpp | 17 +++++++++++------ esphome/components/mdns/mdns_component.h | 6 +++++- esphome/components/mdns/mdns_esp32.cpp | 5 +++-- esphome/components/mdns/mdns_esp8266.cpp | 5 +++-- esphome/components/mdns/mdns_host.cpp | 4 +++- esphome/components/mdns/mdns_libretiny.cpp | 5 +++-- esphome/components/mdns/mdns_rp2040.cpp | 5 +++-- esphome/components/openthread/__init__.py | 5 ++++- esphome/core/defines.h | 1 + 10 files changed, 54 insertions(+), 18 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 14e0420ef5..c6a9ee1a0c 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg from esphome.components.esp32 import add_idf_component -from esphome.config_helpers import filter_source_files_from_platform +from esphome.config_helpers import filter_source_files_from_platform, get_logger_level import esphome.config_validation as cv from esphome.const import ( CONF_DISABLED, @@ -125,6 +125,17 @@ def mdns_service( ) +def enable_mdns_storage(): + """Enable persistent storage of mDNS services in the MDNSComponent. + + Called by external components (like OpenThread) that need access to + services after setup() completes via get_services(). + + Public API for external components. Do not remove. + """ + cg.add_define("USE_MDNS_STORE_SERVICES") + + @coroutine_with_priority(CoroPriority.NETWORK_SERVICES) async def to_code(config): if config[CONF_DISABLED] is True: @@ -150,6 +161,8 @@ async def to_code(config): if config[CONF_SERVICES]: cg.add_define("USE_MDNS_EXTRA_SERVICES") + # Extra services need to be stored persistently + enable_mdns_storage() # Ensure at least 1 service (fallback service) cg.add_define("MDNS_SERVICE_COUNT", max(1, service_count)) @@ -171,6 +184,10 @@ async def to_code(config): # Ensure at least 1 to avoid zero-size array cg.add_define("MDNS_DYNAMIC_TXT_COUNT", max(1, dynamic_txt_count)) + # Enable storage if verbose logging is enabled (for dump_config) + if get_logger_level() in ("VERBOSE", "VERY_VERBOSE"): + enable_mdns_storage() + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index 9cb664c3c3..fea3ced99f 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -36,7 +36,7 @@ MDNS_STATIC_CONST_CHAR(SERVICE_TCP, "_tcp"); // Wrap build-time defines into flash storage MDNS_STATIC_CONST_CHAR(VALUE_VERSION, ESPHOME_VERSION); -void MDNSComponent::compile_records_() { +void MDNSComponent::compile_records_(StaticVector &services) { this->hostname_ = App.get_name(); // IMPORTANT: The #ifdef blocks below must match COMPONENTS_WITH_MDNS_SERVICES @@ -53,7 +53,7 @@ void MDNSComponent::compile_records_() { MDNS_STATIC_CONST_CHAR(VALUE_BOARD, ESPHOME_BOARD); if (api::global_api_server != nullptr) { - auto &service = this->services_.emplace_next(); + auto &service = services.emplace_next(); service.service_type = MDNS_STR(SERVICE_ESPHOMELIB); service.proto = MDNS_STR(SERVICE_TCP); service.port = api::global_api_server->get_port(); @@ -146,7 +146,7 @@ void MDNSComponent::compile_records_() { #ifdef USE_PROMETHEUS MDNS_STATIC_CONST_CHAR(SERVICE_PROMETHEUS, "_prometheus-http"); - auto &prom_service = this->services_.emplace_next(); + auto &prom_service = services.emplace_next(); prom_service.service_type = MDNS_STR(SERVICE_PROMETHEUS); prom_service.proto = MDNS_STR(SERVICE_TCP); prom_service.port = USE_WEBSERVER_PORT; @@ -155,7 +155,7 @@ void MDNSComponent::compile_records_() { #ifdef USE_WEBSERVER MDNS_STATIC_CONST_CHAR(SERVICE_HTTP, "_http"); - auto &web_service = this->services_.emplace_next(); + auto &web_service = services.emplace_next(); web_service.service_type = MDNS_STR(SERVICE_HTTP); web_service.proto = MDNS_STR(SERVICE_TCP); web_service.port = USE_WEBSERVER_PORT; @@ -167,12 +167,17 @@ void MDNSComponent::compile_records_() { // Publish "http" service if not using native API or any other services // This is just to have *some* mDNS service so that .local resolution works - auto &fallback_service = this->services_.emplace_next(); + auto &fallback_service = services.emplace_next(); fallback_service.service_type = MDNS_STR(SERVICE_HTTP); fallback_service.proto = MDNS_STR(SERVICE_TCP); fallback_service.port = USE_WEBSERVER_PORT; fallback_service.txt_records.push_back({MDNS_STR(TXT_VERSION), MDNS_STR(VALUE_VERSION)}); #endif + +#ifdef USE_MDNS_STORE_SERVICES + // Copy to member variable if storage is enabled (verbose logging, OpenThread, or extra services) + this->services_ = services; +#endif } void MDNSComponent::dump_config() { @@ -180,7 +185,7 @@ void MDNSComponent::dump_config() { "mDNS:\n" " Hostname: %s", this->hostname_.c_str()); -#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE +#ifdef USE_MDNS_STORE_SERVICES ESP_LOGV(TAG, " Services:"); for (const auto &service : this->services_) { ESP_LOGV(TAG, " - %s, %s, %d", MDNS_STR_ARG(service.service_type), MDNS_STR_ARG(service.proto), diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index 141e42d976..62476e9504 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -55,7 +55,9 @@ class MDNSComponent : public Component { void add_extra_service(MDNSService service) { this->services_.emplace_next() = std::move(service); } #endif +#ifdef USE_MDNS_STORE_SERVICES const StaticVector &get_services() const { return this->services_; } +#endif void on_shutdown() override; @@ -71,9 +73,11 @@ class MDNSComponent : public Component { StaticVector dynamic_txt_values_; protected: +#ifdef USE_MDNS_STORE_SERVICES StaticVector services_{}; +#endif std::string hostname_; - void compile_records_(); + void compile_records_(StaticVector &services); }; } // namespace mdns diff --git a/esphome/components/mdns/mdns_esp32.cpp b/esphome/components/mdns/mdns_esp32.cpp index e77c0b9b05..da47be7dbc 100644 --- a/esphome/components/mdns/mdns_esp32.cpp +++ b/esphome/components/mdns/mdns_esp32.cpp @@ -12,7 +12,8 @@ namespace mdns { static const char *const TAG = "mdns"; void MDNSComponent::setup() { - this->compile_records_(); + StaticVector services; + this->compile_records_(services); esp_err_t err = mdns_init(); if (err != ESP_OK) { @@ -24,7 +25,7 @@ void MDNSComponent::setup() { mdns_hostname_set(this->hostname_.c_str()); mdns_instance_name_set(this->hostname_.c_str()); - for (const auto &service : this->services_) { + for (const auto &service : services) { std::vector txt_records; for (const auto &record : service.txt_records) { mdns_txt_item_t it{}; diff --git a/esphome/components/mdns/mdns_esp8266.cpp b/esphome/components/mdns/mdns_esp8266.cpp index f3779042ed..06503742db 100644 --- a/esphome/components/mdns/mdns_esp8266.cpp +++ b/esphome/components/mdns/mdns_esp8266.cpp @@ -12,11 +12,12 @@ namespace esphome { namespace mdns { void MDNSComponent::setup() { - this->compile_records_(); + StaticVector services; + this->compile_records_(services); MDNS.begin(this->hostname_.c_str()); - for (const auto &service : this->services_) { + for (const auto &service : services) { // Strip the leading underscore from the proto and service_type. While it is // part of the wire protocol to have an underscore, and for example ESP-IDF // expects the underscore to be there, the ESP8266 implementation always adds diff --git a/esphome/components/mdns/mdns_host.cpp b/esphome/components/mdns/mdns_host.cpp index 78767ed136..f645d8d068 100644 --- a/esphome/components/mdns/mdns_host.cpp +++ b/esphome/components/mdns/mdns_host.cpp @@ -9,7 +9,9 @@ namespace esphome { namespace mdns { -void MDNSComponent::setup() { this->compile_records_(); } +void MDNSComponent::setup() { + // Host platform doesn't have actual mDNS implementation +} void MDNSComponent::on_shutdown() {} diff --git a/esphome/components/mdns/mdns_libretiny.cpp b/esphome/components/mdns/mdns_libretiny.cpp index 5540bf361a..a959482ff6 100644 --- a/esphome/components/mdns/mdns_libretiny.cpp +++ b/esphome/components/mdns/mdns_libretiny.cpp @@ -12,11 +12,12 @@ namespace esphome { namespace mdns { void MDNSComponent::setup() { - this->compile_records_(); + StaticVector services; + this->compile_records_(services); MDNS.begin(this->hostname_.c_str()); - for (const auto &service : this->services_) { + for (const auto &service : services) { // Strip the leading underscore from the proto and service_type. While it is // part of the wire protocol to have an underscore, and for example ESP-IDF // expects the underscore to be there, the ESP8266 implementation always adds diff --git a/esphome/components/mdns/mdns_rp2040.cpp b/esphome/components/mdns/mdns_rp2040.cpp index 5ad006f5d4..9dfb05bda9 100644 --- a/esphome/components/mdns/mdns_rp2040.cpp +++ b/esphome/components/mdns/mdns_rp2040.cpp @@ -12,11 +12,12 @@ namespace esphome { namespace mdns { void MDNSComponent::setup() { - this->compile_records_(); + StaticVector services; + this->compile_records_(services); MDNS.begin(this->hostname_.c_str()); - for (const auto &service : this->services_) { + for (const auto &service : services) { // Strip the leading underscore from the proto and service_type. While it is // part of the wire protocol to have an underscore, and for example ESP-IDF // expects the underscore to be there, the ESP8266 implementation always adds diff --git a/esphome/components/openthread/__init__.py b/esphome/components/openthread/__init__.py index 2f085ebaae..3fac497c3d 100644 --- a/esphome/components/openthread/__init__.py +++ b/esphome/components/openthread/__init__.py @@ -5,7 +5,7 @@ from esphome.components.esp32 import ( add_idf_sdkconfig_option, only_on_variant, ) -from esphome.components.mdns import MDNSComponent +from esphome.components.mdns import MDNSComponent, enable_mdns_storage import esphome.config_validation as cv from esphome.const import CONF_CHANNEL, CONF_ENABLE_IPV6, CONF_ID import esphome.final_validate as fv @@ -141,6 +141,9 @@ FINAL_VALIDATE_SCHEMA = _final_validate async def to_code(config): cg.add_define("USE_OPENTHREAD") + # OpenThread SRP needs access to mDNS services after setup + enable_mdns_storage() + ot = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(ot, config) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index ae44e16624..ed30efd0b5 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -83,6 +83,7 @@ #define USE_LVGL_TILEVIEW #define USE_LVGL_TOUCHSCREEN #define USE_MDNS +#define USE_MDNS_STORE_SERVICES #define MDNS_SERVICE_COUNT 3 #define MDNS_DYNAMIC_TXT_COUNT 3 #define USE_MEDIA_PLAYER From aec60d122b7e1e0c7fed4a4d25a07c790e49fe03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 09:34:12 -1000 Subject: [PATCH 044/526] Bump esphome-dashboard from 20251009.0 to 20251013.0 (#11212) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 64946263ea..9937709c1c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ pyserial==3.5 platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 -esphome-dashboard==20251009.0 +esphome-dashboard==20251013.0 aioesphomeapi==41.14.0 zeroconf==0.148.0 puremagic==1.30 From 0f356fcc79dfe30b4a4dd71b0221906274fbf3a3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 10:20:43 -1000 Subject: [PATCH 045/526] [core] Optimize looping_components_ with FixedVector to save flash (#11183) --- esphome/core/application.cpp | 4 +-- esphome/core/application.h | 2 +- esphome/core/helpers.h | 48 ++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 1be193bb7e..c745aa0ae5 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -340,8 +340,8 @@ void Application::calculate_looping_components_() { } } - // Pre-reserve vector to avoid reallocations - this->looping_components_.reserve(total_looping); + // Initialize FixedVector with exact size - no reallocation possible + this->looping_components_.init(total_looping); // Add all components with loop override that aren't already LOOP_DONE // Some components (like logger) may call disable_loop() during initialization diff --git a/esphome/core/application.h b/esphome/core/application.h index 1f22499051..b0f9c23191 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -472,7 +472,7 @@ class Application { // - When a component is enabled, it's swapped with the first inactive component // and active_end_ is incremented // - This eliminates branch mispredictions from flag checking in the hot loop - std::vector looping_components_{}; + FixedVector looping_components_{}; #ifdef USE_SOCKET_SELECT_SUPPORT std::vector socket_fds_; // Vector of all monitored socket file descriptors #endif diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index e06f2d15ef..b5a0a1c8ac 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -159,6 +159,54 @@ template class StaticVector { const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } }; +/// Fixed-capacity vector - allocates once at runtime, never reallocates +/// This avoids std::vector template overhead (_M_realloc_insert, _M_default_append) +/// when size is known at initialization but not at compile time +template class FixedVector { + private: + T *data_{nullptr}; + size_t size_{0}; + size_t capacity_{0}; + + public: + FixedVector() = default; + + ~FixedVector() { + if (data_ != nullptr) { + delete[] data_; + } + } + + // Disable copy to avoid accidental copies + FixedVector(const FixedVector &) = delete; + FixedVector &operator=(const FixedVector &) = delete; + + // Allocate capacity - can only be called once on empty vector + void init(size_t n) { + if (data_ == nullptr && n > 0) { + data_ = new T[n]; + capacity_ = n; + size_ = 0; + } + } + + /// Add element without bounds checking + /// Caller must ensure sufficient capacity was allocated via init() + /// Silently ignores pushes beyond capacity (no exception or assertion) + void push_back(const T &value) { + if (size_ < capacity_) { + data_[size_++] = value; + } + } + + size_t size() const { return size_; } + + /// Access element without bounds checking (matches std::vector behavior) + /// Caller must ensure index is valid (i < size()) + T &operator[](size_t i) { return data_[i]; } + const T &operator[](size_t i) const { return data_[i]; } +}; + ///@} /// @name Mathematics From 8d8fcfeda2082a74b9b7c91dc7ab37ea78530aa2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 10:39:38 -1000 Subject: [PATCH 046/526] [core] Add make_name_with_suffix helper to optimize string concatenation (#11176) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/esp32_ble/ble.cpp | 7 ++++-- .../ethernet/ethernet_component.cpp | 4 +++- esphome/components/mqtt/mqtt_client.cpp | 3 ++- esphome/components/wifi/wifi_component.cpp | 4 +++- esphome/config_validation.py | 7 ++++++ esphome/core/application.h | 12 +++++++--- esphome/core/config.py | 2 +- esphome/core/helpers.cpp | 24 +++++++++++++++++++ esphome/core/helpers.h | 10 ++++++++ 9 files changed, 64 insertions(+), 9 deletions(-) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 0c340c55cc..1f6bc6d0a0 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -213,8 +213,11 @@ bool ESP32BLE::ble_setup_() { if (this->name_.has_value()) { name = this->name_.value(); if (App.is_name_add_mac_suffix_enabled()) { - name += "-"; - name += get_mac_address().substr(6); + // MAC address suffix length (last 6 characters of 12-char MAC address string) + constexpr size_t mac_address_suffix_len = 6; + const std::string mac_addr = get_mac_address(); + const char *mac_suffix_ptr = mac_addr.c_str() + mac_address_suffix_len; + name = make_name_with_suffix(name, '-', mac_suffix_ptr, mac_address_suffix_len); } } else { name = App.get_name(); diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 28043dd969..13adab8815 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -691,7 +691,9 @@ void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ std::string EthernetComponent::get_use_address() const { if (this->use_address_.empty()) { - return App.get_name() + ".local"; + // ".local" suffix length for mDNS hostnames + constexpr size_t mdns_local_suffix_len = 5; + return make_name_with_suffix(App.get_name(), '.', "local", mdns_local_suffix_len); } return this->use_address_; } diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 7ab6efd1a1..16f54ab8a0 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -29,7 +29,8 @@ static const char *const TAG = "mqtt"; MQTTClientComponent::MQTTClientComponent() { global_mqtt_client = this; - this->credentials_.client_id = App.get_name() + "-" + get_mac_address(); + const std::string mac_addr = get_mac_address(); + this->credentials_.client_id = make_name_with_suffix(App.get_name(), '-', mac_addr.c_str(), mac_addr.size()); } // Connection diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 71ee4271ba..0f9f879181 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -267,7 +267,9 @@ network::IPAddress WiFiComponent::get_dns_address(int num) { } std::string WiFiComponent::get_use_address() const { if (this->use_address_.empty()) { - return App.get_name() + ".local"; + // ".local" suffix length for mDNS hostnames + constexpr size_t mdns_local_suffix_len = 5; + return make_name_with_suffix(App.get_name(), '.', "local", mdns_local_suffix_len); } return this->use_address_; } diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 7aaba886e3..ebfedf2017 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1195,6 +1195,13 @@ def validate_bytes(value): def hostname(value): + """Validate that the value is a valid hostname. + + Maximum length is 63 characters per RFC 1035. + + Note: If this limit is changed, update MAX_NAME_WITH_SUFFIX_SIZE in + esphome/core/helpers.cpp to accommodate the new maximum length. + """ value = string(value) if re.match(r"^[a-z0-9-]{1,63}$", value, re.IGNORECASE) is not None: return value diff --git a/esphome/core/application.h b/esphome/core/application.h index b0f9c23191..6e7f1b49f2 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -102,9 +102,15 @@ class Application { arch_init(); this->name_add_mac_suffix_ = name_add_mac_suffix; if (name_add_mac_suffix) { - const std::string mac_suffix = get_mac_address().substr(6); - this->name_ = name + "-" + mac_suffix; - this->friendly_name_ = friendly_name.empty() ? "" : friendly_name + " " + mac_suffix; + // MAC address suffix length (last 6 characters of 12-char MAC address string) + constexpr size_t mac_address_suffix_len = 6; + const std::string mac_addr = get_mac_address(); + // Use pointer + offset to avoid substr() allocation + const char *mac_suffix_ptr = mac_addr.c_str() + mac_address_suffix_len; + this->name_ = make_name_with_suffix(name, '-', mac_suffix_ptr, mac_address_suffix_len); + if (!friendly_name.empty()) { + this->friendly_name_ = make_name_with_suffix(friendly_name, ' ', mac_suffix_ptr, mac_address_suffix_len); + } } else { this->name_ = name; this->friendly_name_ = friendly_name; diff --git a/esphome/core/config.py b/esphome/core/config.py index 7bf7f82a8b..8a5876dbcf 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -200,7 +200,7 @@ CONFIG_SCHEMA = cv.All( cv.Schema( { cv.Required(CONF_NAME): cv.valid_name, - cv.Optional(CONF_FRIENDLY_NAME, ""): cv.string, + cv.Optional(CONF_FRIENDLY_NAME, ""): cv.All(cv.string, cv.Length(max=120)), cv.Optional(CONF_AREA): validate_area_config, cv.Optional(CONF_COMMENT): cv.string, cv.Required(CONF_BUILD_PATH): cv.string, diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index d4f6809776..fb8b220b2f 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -235,6 +235,30 @@ std::string str_sprintf(const char *fmt, ...) { return str; } +// Maximum size for name with suffix: 120 (max friendly name) + 1 (separator) + 6 (MAC suffix) + 1 (null term) +static constexpr size_t MAX_NAME_WITH_SUFFIX_SIZE = 128; + +std::string make_name_with_suffix(const std::string &name, char sep, const char *suffix_ptr, size_t suffix_len) { + char buffer[MAX_NAME_WITH_SUFFIX_SIZE]; + size_t name_len = name.size(); + size_t total_len = name_len + 1 + suffix_len; + + // Silently truncate if needed: prioritize keeping the full suffix + if (total_len >= MAX_NAME_WITH_SUFFIX_SIZE) { + // NOTE: This calculation could underflow if suffix_len >= MAX_NAME_WITH_SUFFIX_SIZE - 2, + // but this is safe because this helper is only called with small suffixes: + // MAC suffixes (6-12 bytes), ".local" (5 bytes), etc. + name_len = MAX_NAME_WITH_SUFFIX_SIZE - suffix_len - 2; // -2 for separator and null terminator + total_len = name_len + 1 + suffix_len; + } + + memcpy(buffer, name.c_str(), name_len); + buffer[name_len] = sep; + memcpy(buffer + name_len + 1, suffix_ptr, suffix_len); + buffer[total_len] = '\0'; + return std::string(buffer, total_len); +} + // Parsing & formatting size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count) { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index b5a0a1c8ac..ed1d5ac108 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -354,6 +354,16 @@ std::string __attribute__((format(printf, 1, 3))) str_snprintf(const char *fmt, /// sprintf-like function returning std::string. std::string __attribute__((format(printf, 1, 2))) str_sprintf(const char *fmt, ...); +/// Concatenate a name with a separator and suffix using an efficient stack-based approach. +/// This avoids multiple heap allocations during string construction. +/// Maximum name length supported is 120 characters for friendly names. +/// @param name The base name string +/// @param sep The separator character (e.g., '-', ' ', or '.') +/// @param suffix_ptr Pointer to the suffix characters +/// @param suffix_len Length of the suffix +/// @return The concatenated string: name + sep + suffix +std::string make_name_with_suffix(const std::string &name, char sep, const char *suffix_ptr, size_t suffix_len); + ///@} /// @name Parsing & formatting From 6372099df3a9a2b4dfb5bff5916b5f40cd47edf5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 10:53:11 -1000 Subject: [PATCH 047/526] [http_request] Pass parameters by const reference to reduce flash usage (#11184) --- esphome/components/http_request/http_request.h | 4 ++-- esphome/components/http_request/http_request_arduino.cpp | 5 +++-- esphome/components/http_request/http_request_arduino.h | 4 ++-- esphome/components/http_request/http_request_host.cpp | 5 +++-- esphome/components/http_request/http_request_host.h | 4 ++-- esphome/components/http_request/http_request_idf.cpp | 5 +++-- esphome/components/http_request/http_request_idf.h | 4 ++-- 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 95515f731a..bb14cc6f51 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -167,8 +167,8 @@ class HttpRequestComponent : public Component { } protected: - virtual std::shared_ptr perform(std::string url, std::string method, std::string body, - std::list
request_headers, + virtual std::shared_ptr perform(const std::string &url, const std::string &method, + const std::string &body, const std::list
&request_headers, std::set collect_headers) = 0; const char *useragent_{nullptr}; bool follow_redirects_{}; diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index c009b33c2d..dfdbbd3fab 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -14,8 +14,9 @@ namespace http_request { static const char *const TAG = "http_request.arduino"; -std::shared_ptr HttpRequestArduino::perform(std::string url, std::string method, std::string body, - std::list
request_headers, +std::shared_ptr HttpRequestArduino::perform(const std::string &url, const std::string &method, + const std::string &body, + const std::list
&request_headers, std::set collect_headers) { if (!network::is_connected()) { this->status_momentary_error("failed", 1000); diff --git a/esphome/components/http_request/http_request_arduino.h b/esphome/components/http_request/http_request_arduino.h index 44744f8c78..c8208c74d8 100644 --- a/esphome/components/http_request/http_request_arduino.h +++ b/esphome/components/http_request/http_request_arduino.h @@ -31,8 +31,8 @@ class HttpContainerArduino : public HttpContainer { class HttpRequestArduino : public HttpRequestComponent { protected: - std::shared_ptr perform(std::string url, std::string method, std::string body, - std::list
request_headers, + std::shared_ptr perform(const std::string &url, const std::string &method, const std::string &body, + const std::list
&request_headers, std::set collect_headers) override; }; diff --git a/esphome/components/http_request/http_request_host.cpp b/esphome/components/http_request/http_request_host.cpp index 0b4c998a40..c20ea552b7 100644 --- a/esphome/components/http_request/http_request_host.cpp +++ b/esphome/components/http_request/http_request_host.cpp @@ -17,8 +17,9 @@ namespace http_request { static const char *const TAG = "http_request.host"; -std::shared_ptr HttpRequestHost::perform(std::string url, std::string method, std::string body, - std::list
request_headers, +std::shared_ptr HttpRequestHost::perform(const std::string &url, const std::string &method, + const std::string &body, + const std::list
&request_headers, std::set response_headers) { if (!network::is_connected()) { this->status_momentary_error("failed", 1000); diff --git a/esphome/components/http_request/http_request_host.h b/esphome/components/http_request/http_request_host.h index bbeed87f70..fdd72e7ea5 100644 --- a/esphome/components/http_request/http_request_host.h +++ b/esphome/components/http_request/http_request_host.h @@ -18,8 +18,8 @@ class HttpContainerHost : public HttpContainer { class HttpRequestHost : public HttpRequestComponent { public: - std::shared_ptr perform(std::string url, std::string method, std::string body, - std::list
request_headers, + std::shared_ptr perform(const std::string &url, const std::string &method, const std::string &body, + const std::list
&request_headers, std::set response_headers) override; void set_ca_path(const char *ca_path) { this->ca_path_ = ca_path; } diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 89a0891b03..a91c0bfc25 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -52,8 +52,9 @@ esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) { return ESP_OK; } -std::shared_ptr HttpRequestIDF::perform(std::string url, std::string method, std::string body, - std::list
request_headers, +std::shared_ptr HttpRequestIDF::perform(const std::string &url, const std::string &method, + const std::string &body, + const std::list
&request_headers, std::set collect_headers) { if (!network::is_connected()) { this->status_momentary_error("failed", 1000); diff --git a/esphome/components/http_request/http_request_idf.h b/esphome/components/http_request/http_request_idf.h index 5c5b784853..90dee0be68 100644 --- a/esphome/components/http_request/http_request_idf.h +++ b/esphome/components/http_request/http_request_idf.h @@ -37,8 +37,8 @@ class HttpRequestIDF : public HttpRequestComponent { void set_buffer_size_tx(uint16_t buffer_size_tx) { this->buffer_size_tx_ = buffer_size_tx; } protected: - std::shared_ptr perform(std::string url, std::string method, std::string body, - std::list
request_headers, + std::shared_ptr perform(const std::string &url, const std::string &method, const std::string &body, + const std::list
&request_headers, std::set collect_headers) override; // if zero ESP-IDF will use DEFAULT_HTTP_BUF_SIZE uint16_t buffer_size_rx_{}; From 3df4dbd3a62c73480c1f6e756081f37ee76717fe Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:12:45 -0400 Subject: [PATCH 048/526] [core] Properly clean the build dir in the HA addon (#11208) --- esphome/writer.py | 25 +++++++++++------- tests/unit_tests/test_writer.py | 46 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/esphome/writer.py b/esphome/writer.py index b5cfd9b667..8eee445cf1 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -15,6 +15,8 @@ from esphome.const import ( from esphome.core import CORE, EsphomeError from esphome.helpers import ( copy_file_if_changed, + get_str_env, + is_ha_addon, read_file, walk_files, write_file_if_changed, @@ -338,16 +340,21 @@ def clean_build(): def clean_all(configuration: list[str]): import shutil - # Clean entire build dir - for dir in configuration: - build_dir = Path(dir) / ".esphome" - if build_dir.is_dir(): - _LOGGER.info("Cleaning %s", build_dir) - # Don't remove storage as it will cause the dashboard to regenerate all configs - for item in build_dir.iterdir(): - if item.is_file(): + data_dirs = [Path(dir) / ".esphome" for dir in configuration] + if is_ha_addon(): + data_dirs.append(Path("/data")) + if "ESPHOME_DATA_DIR" in os.environ: + data_dirs.append(Path(get_str_env("ESPHOME_DATA_DIR", None))) + + # Clean build dir + for dir in data_dirs: + if dir.is_dir(): + _LOGGER.info("Cleaning %s", dir) + # Don't remove storage or .json files which are needed by the dashboard + for item in dir.iterdir(): + if item.is_file() and not item.name.endswith(".json"): item.unlink() - elif item.name != "storage" and item.is_dir(): + elif item.is_dir() and item.name != "storage": shutil.rmtree(item) # Clean PlatformIO project files diff --git a/tests/unit_tests/test_writer.py b/tests/unit_tests/test_writer.py index bffd2b3881..a4490fbbc0 100644 --- a/tests/unit_tests/test_writer.py +++ b/tests/unit_tests/test_writer.py @@ -985,3 +985,49 @@ def test_clean_all_removes_non_storage_directories( # Verify logging mentions cleaning assert "Cleaning" in caplog.text assert str(build_dir) in caplog.text + + +@patch("esphome.writer.CORE") +def test_clean_all_preserves_json_files( + mock_core: MagicMock, + tmp_path: Path, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test clean_all preserves .json files.""" + # Create build directory with various files + config_dir = tmp_path / "config" + config_dir.mkdir() + + build_dir = config_dir / ".esphome" + build_dir.mkdir() + + # Create .json files (should be preserved) + (build_dir / "config.json").write_text('{"config": "data"}') + (build_dir / "metadata.json").write_text('{"metadata": "info"}') + + # Create non-.json files (should be removed) + (build_dir / "dummy.txt").write_text("x") + (build_dir / "other.log").write_text("log content") + + # Call clean_all + from esphome.writer import clean_all + + with caplog.at_level("INFO"): + clean_all([str(config_dir)]) + + # Verify .esphome directory still exists + assert build_dir.exists() + + # Verify .json files are preserved + assert (build_dir / "config.json").exists() + assert (build_dir / "config.json").read_text() == '{"config": "data"}' + assert (build_dir / "metadata.json").exists() + assert (build_dir / "metadata.json").read_text() == '{"metadata": "info"}' + + # Verify non-.json files were removed + assert not (build_dir / "dummy.txt").exists() + assert not (build_dir / "other.log").exists() + + # Verify logging mentions cleaning + assert "Cleaning" in caplog.text + assert str(build_dir) in caplog.text From 9fb254fdc21a97d7f22aa682a88a6e33c6ef94f9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 11:23:44 -1000 Subject: [PATCH 049/526] Fix log retrieval with FQDN when mDNS is disabled (#11202) --- esphome/__main__.py | 15 +++++++----- tests/unit_tests/test_main.py | 45 ++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index be4551f6b5..8e0c475525 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -268,8 +268,10 @@ def has_ip_address() -> bool: def has_resolvable_address() -> bool: - """Check if CORE.address is resolvable (via mDNS or is an IP address).""" - return has_mdns() or has_ip_address() + """Check if CORE.address is resolvable (via mDNS, DNS, or is an IP address).""" + # Any address (IP, mDNS hostname, or regular DNS hostname) is resolvable + # The resolve_ip_address() function in helpers.py handles all types via AsyncResolver + return CORE.address is not None def mqtt_get_ip(config: ConfigType, username: str, password: str, client_id: str): @@ -578,11 +580,12 @@ def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int if has_api(): addresses_to_use: list[str] | None = None - if port_type == "NETWORK" and (has_mdns() or is_ip_address(port)): + if port_type == "NETWORK": + # Network addresses (IPs, mDNS names, or regular DNS hostnames) can be used + # The resolve_ip_address() function in helpers.py handles all types addresses_to_use = devices - elif port_type in ("NETWORK", "MQTT", "MQTTIP") and has_mqtt_ip_lookup(): - # Only use MQTT IP lookup if the first condition didn't match - # (for MQTT/MQTTIP types, or for NETWORK when mdns/ip check fails) + elif port_type in ("MQTT", "MQTTIP") and has_mqtt_ip_lookup(): + # Use MQTT IP lookup for MQTT/MQTTIP types addresses_to_use = mqtt_get_ip( config, args.username, args.password, args.client_id ) diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index e35378145a..becf911fa3 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -1203,6 +1203,31 @@ def test_show_logs_api( ) +@patch("esphome.components.api.client.run_logs") +def test_show_logs_api_with_fqdn_mdns_disabled( + mock_run_logs: Mock, +) -> None: + """Test show_logs with API using FQDN when mDNS is disabled.""" + setup_core( + config={ + "logger": {}, + CONF_API: {}, + CONF_MDNS: {CONF_DISABLED: True}, + }, + platform=PLATFORM_ESP32, + ) + mock_run_logs.return_value = 0 + + args = MockArgs() + devices = ["device.example.com"] + + result = show_logs(CORE.config, args, devices) + + assert result == 0 + # Should use the FQDN directly, not try MQTT lookup + mock_run_logs.assert_called_once_with(CORE.config, ["device.example.com"]) + + @patch("esphome.components.api.client.run_logs") def test_show_logs_api_with_mqtt_fallback( mock_run_logs: Mock, @@ -1222,7 +1247,7 @@ def test_show_logs_api_with_mqtt_fallback( mock_mqtt_get_ip.return_value = ["192.168.1.200"] args = MockArgs(username="user", password="pass", client_id="client") - devices = ["device.local"] + devices = ["MQTTIP"] result = show_logs(CORE.config, args, devices) @@ -1487,27 +1512,31 @@ def test_mqtt_get_ip() -> None: def test_has_resolvable_address() -> None: """Test has_resolvable_address function.""" - # Test with mDNS enabled and hostname address + # Test with mDNS enabled and .local hostname address setup_core(config={}, address="esphome-device.local") assert has_resolvable_address() is True - # Test with mDNS disabled and hostname address + # Test with mDNS disabled and .local hostname address (still resolvable via DNS) setup_core( config={CONF_MDNS: {CONF_DISABLED: True}}, address="esphome-device.local" ) - assert has_resolvable_address() is False + assert has_resolvable_address() is True - # Test with IP address (mDNS doesn't matter) + # Test with mDNS disabled and regular DNS hostname (resolvable) + setup_core(config={CONF_MDNS: {CONF_DISABLED: True}}, address="device.example.com") + assert has_resolvable_address() is True + + # Test with IP address (always resolvable, mDNS doesn't matter) setup_core(config={}, address="192.168.1.100") assert has_resolvable_address() is True - # Test with IP address and mDNS disabled + # Test with IP address and mDNS disabled (still resolvable) setup_core(config={CONF_MDNS: {CONF_DISABLED: True}}, address="192.168.1.100") assert has_resolvable_address() is True - # Test with no address but mDNS enabled (can still resolve mDNS names) + # Test with no address setup_core(config={}, address=None) - assert has_resolvable_address() is True + assert has_resolvable_address() is False # Test with no address and mDNS disabled setup_core(config={CONF_MDNS: {CONF_DISABLED: True}}, address=None) From c652aa375a402a9888821955699bf1c93fb9de1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:10:46 -1000 Subject: [PATCH 050/526] Bump pylint from 3.3.9 to 4.0.0 (#11211) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 51b8e6f8ed..c4227fdbde 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -pylint==3.3.9 +pylint==4.0.0 flake8==7.3.0 # also change in .pre-commit-config.yaml when updating ruff==0.14.0 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating From fe07c342467b4d42388ff2eeafff338d5e03f20b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 00:00:45 +0000 Subject: [PATCH 051/526] Bump aioesphomeapi from 41.14.0 to 41.16.0 (#11215) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9937709c1c..1142c587b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==41.14.0 +aioesphomeapi==41.16.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import From 7c02f2f10adab07a9c63c574c573cc278999bf4d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 15:00:49 -1000 Subject: [PATCH 052/526] [socket] Split LWIP socket classes to reduce memory overhead on ESP8266/RP2040 (#11172) --- .../components/socket/lwip_raw_tcp_impl.cpp | 190 +++++++++++------- 1 file changed, 113 insertions(+), 77 deletions(-) diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 3377682474..4dedeffb6a 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -40,33 +40,14 @@ class LWIPRawImpl : public Socket { void init() { LWIP_LOG("init(%p)", pcb_); tcp_arg(pcb_, this); - tcp_accept(pcb_, LWIPRawImpl::s_accept_fn); tcp_recv(pcb_, LWIPRawImpl::s_recv_fn); tcp_err(pcb_, LWIPRawImpl::s_err_fn); } std::unique_ptr accept(struct sockaddr *addr, socklen_t *addrlen) override { - if (pcb_ == nullptr) { - errno = EBADF; - return nullptr; - } - if (this->accepted_socket_count_ == 0) { - errno = EWOULDBLOCK; - return nullptr; - } - // Take from front for FIFO ordering - std::unique_ptr sock = std::move(this->accepted_sockets_[0]); - // Shift remaining sockets forward - for (uint8_t i = 1; i < this->accepted_socket_count_; i++) { - this->accepted_sockets_[i - 1] = std::move(this->accepted_sockets_[i]); - } - this->accepted_socket_count_--; - LWIP_LOG("Connection accepted by application, queue size: %d", this->accepted_socket_count_); - if (addr != nullptr) { - sock->getpeername(addr, addrlen); - } - LWIP_LOG("accept(%p)", sock.get()); - return std::unique_ptr(std::move(sock)); + // Non-listening sockets return error + errno = EINVAL; + return nullptr; } int bind(const struct sockaddr *name, socklen_t addrlen) override { if (pcb_ == nullptr) { @@ -292,25 +273,10 @@ class LWIPRawImpl : public Socket { return -1; } int listen(int backlog) override { - if (pcb_ == nullptr) { - errno = EBADF; - return -1; - } - LWIP_LOG("tcp_listen_with_backlog(%p backlog=%d)", pcb_, backlog); - struct tcp_pcb *listen_pcb = tcp_listen_with_backlog(pcb_, backlog); - if (listen_pcb == nullptr) { - tcp_abort(pcb_); - pcb_ = nullptr; - errno = EOPNOTSUPP; - return -1; - } - // tcp_listen reallocates the pcb, replace ours - pcb_ = listen_pcb; - // set callbacks on new pcb - LWIP_LOG("tcp_arg(%p)", pcb_); - tcp_arg(pcb_, this); - tcp_accept(pcb_, LWIPRawImpl::s_accept_fn); - return 0; + // Regular sockets can't be converted to listening - this shouldn't happen + // as listen() should only be called on sockets created for listening + errno = EOPNOTSUPP; + return -1; } ssize_t read(void *buf, size_t len) override { if (pcb_ == nullptr) { @@ -491,29 +457,6 @@ class LWIPRawImpl : public Socket { return 0; } - err_t accept_fn(struct tcp_pcb *newpcb, err_t err) { - LWIP_LOG("accept(newpcb=%p err=%d)", newpcb, err); - if (err != ERR_OK || newpcb == nullptr) { - // "An error code if there has been an error accepting. Only return ERR_ABRT if you have - // called tcp_abort from within the callback function!" - // https://www.nongnu.org/lwip/2_1_x/tcp_8h.html#a00517abce6856d6c82f0efebdafb734d - // nothing to do here, we just don't push it to the queue - return ERR_OK; - } - // Check if we've reached the maximum accept queue size - if (this->accepted_socket_count_ >= MAX_ACCEPTED_SOCKETS) { - LWIP_LOG("Rejecting connection, queue full (%d)", this->accepted_socket_count_); - // Abort the connection when queue is full - tcp_abort(newpcb); - // Must return ERR_ABRT since we called tcp_abort() - return ERR_ABRT; - } - auto sock = make_unique(family_, newpcb); - sock->init(); - this->accepted_sockets_[this->accepted_socket_count_++] = std::move(sock); - LWIP_LOG("Accepted connection, queue size: %d", this->accepted_socket_count_); - return ERR_OK; - } void err_fn(err_t err) { LWIP_LOG("err(err=%d)", err); // "If a connection is aborted because of an error, the application is alerted of this event by @@ -545,11 +488,6 @@ class LWIPRawImpl : public Socket { return ERR_OK; } - static err_t s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err) { - LWIPRawImpl *arg_this = reinterpret_cast(arg); - return arg_this->accept_fn(newpcb, err); - } - static void s_err_fn(void *arg, err_t err) { LWIPRawImpl *arg_this = reinterpret_cast(arg); arg_this->err_fn(err); @@ -601,7 +539,107 @@ class LWIPRawImpl : public Socket { return -1; } + // Member ordering optimized to minimize padding on 32-bit systems + // Largest members first (4 bytes), then smaller members (1 byte each) struct tcp_pcb *pcb_; + pbuf *rx_buf_ = nullptr; + size_t rx_buf_offset_ = 0; + bool rx_closed_ = false; + // don't use lwip nodelay flag, it sometimes causes reconnect + // instead use it for determining whether to call lwip_output + bool nodelay_ = false; + sa_family_t family_ = 0; +}; + +// Listening socket class - only allocates accept queue when needed (for bind+listen sockets) +// This saves 16 bytes (12 bytes array + 1 byte count + 3 bytes padding) for regular connected sockets on ESP8266/RP2040 +class LWIPRawListenImpl : public LWIPRawImpl { + public: + LWIPRawListenImpl(sa_family_t family, struct tcp_pcb *pcb) : LWIPRawImpl(family, pcb) {} + + void init() { + LWIP_LOG("init(%p)", pcb_); + tcp_arg(pcb_, this); + tcp_accept(pcb_, LWIPRawListenImpl::s_accept_fn); + tcp_err(pcb_, LWIPRawImpl::s_err_fn); // Use base class error handler + } + + std::unique_ptr accept(struct sockaddr *addr, socklen_t *addrlen) override { + if (pcb_ == nullptr) { + errno = EBADF; + return nullptr; + } + if (accepted_socket_count_ == 0) { + errno = EWOULDBLOCK; + return nullptr; + } + // Take from front for FIFO ordering + std::unique_ptr sock = std::move(accepted_sockets_[0]); + // Shift remaining sockets forward + for (uint8_t i = 1; i < accepted_socket_count_; i++) { + accepted_sockets_[i - 1] = std::move(accepted_sockets_[i]); + } + accepted_socket_count_--; + LWIP_LOG("Connection accepted by application, queue size: %d", accepted_socket_count_); + if (addr != nullptr) { + sock->getpeername(addr, addrlen); + } + LWIP_LOG("accept(%p)", sock.get()); + return std::unique_ptr(std::move(sock)); + } + + int listen(int backlog) override { + if (pcb_ == nullptr) { + errno = EBADF; + return -1; + } + LWIP_LOG("tcp_listen_with_backlog(%p backlog=%d)", pcb_, backlog); + struct tcp_pcb *listen_pcb = tcp_listen_with_backlog(pcb_, backlog); + if (listen_pcb == nullptr) { + tcp_abort(pcb_); + pcb_ = nullptr; + errno = EOPNOTSUPP; + return -1; + } + // tcp_listen reallocates the pcb, replace ours + pcb_ = listen_pcb; + // set callbacks on new pcb + LWIP_LOG("tcp_arg(%p)", pcb_); + tcp_arg(pcb_, this); + tcp_accept(pcb_, LWIPRawListenImpl::s_accept_fn); + return 0; + } + + private: + err_t accept_fn(struct tcp_pcb *newpcb, err_t err) { + LWIP_LOG("accept(newpcb=%p err=%d)", newpcb, err); + if (err != ERR_OK || newpcb == nullptr) { + // "An error code if there has been an error accepting. Only return ERR_ABRT if you have + // called tcp_abort from within the callback function!" + // https://www.nongnu.org/lwip/2_1_x/tcp_8h.html#a00517abce6856d6c82f0efebdafb734d + // nothing to do here, we just don't push it to the queue + return ERR_OK; + } + // Check if we've reached the maximum accept queue size + if (accepted_socket_count_ >= MAX_ACCEPTED_SOCKETS) { + LWIP_LOG("Rejecting connection, queue full (%d)", accepted_socket_count_); + // Abort the connection when queue is full + tcp_abort(newpcb); + // Must return ERR_ABRT since we called tcp_abort() + return ERR_ABRT; + } + auto sock = make_unique(family_, newpcb); + sock->init(); + accepted_sockets_[accepted_socket_count_++] = std::move(sock); + LWIP_LOG("Accepted connection, queue size: %d", accepted_socket_count_); + return ERR_OK; + } + + static err_t s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err) { + LWIPRawListenImpl *arg_this = reinterpret_cast(arg); + return arg_this->accept_fn(newpcb, err); + } + // Accept queue - holds incoming connections briefly until the event loop calls accept() // This is NOT a connection pool - just a temporary queue between LWIP callbacks and the main loop // 3 slots is plenty since connections are pulled out quickly by the event loop @@ -613,23 +651,21 @@ class LWIPRawImpl : public Socket { // - std::array<3>: 12 bytes fixed (3 pointers × 4 bytes) // Saves ~44+ bytes RAM per listening socket + avoids ALL heap allocations // Used on ESP8266 and RP2040 (platforms using LWIP_TCP implementation) + // + // By using a separate listening socket class, regular connected sockets save + // 16 bytes (12 bytes array + 1 byte count + 3 bytes padding) of memory overhead on 32-bit systems static constexpr size_t MAX_ACCEPTED_SOCKETS = 3; std::array, MAX_ACCEPTED_SOCKETS> accepted_sockets_; uint8_t accepted_socket_count_ = 0; // Number of sockets currently in queue - bool rx_closed_ = false; - pbuf *rx_buf_ = nullptr; - size_t rx_buf_offset_ = 0; - // don't use lwip nodelay flag, it sometimes causes reconnect - // instead use it for determining whether to call lwip_output - bool nodelay_ = false; - sa_family_t family_ = 0; }; std::unique_ptr socket(int domain, int type, int protocol) { auto *pcb = tcp_new(); if (pcb == nullptr) return nullptr; - auto *sock = new LWIPRawImpl((sa_family_t) domain, pcb); // NOLINT(cppcoreguidelines-owning-memory) + // Create listening socket implementation since user sockets typically bind+listen + // Accepted connections are created directly as LWIPRawImpl in the accept callback + auto *sock = new LWIPRawListenImpl((sa_family_t) domain, pcb); // NOLINT(cppcoreguidelines-owning-memory) sock->init(); return std::unique_ptr{sock}; } From 5bb69a968cd45dc52da458faef2ea0ca41124c1e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 16:33:33 -1000 Subject: [PATCH 053/526] [esp32_ble] Replace handler vectors with StaticVector for 560B-2KB memory savings (#11200) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/esp32_ble/__init__.py | 88 ++++++++++++++++++- esphome/components/esp32_ble/ble.cpp | 56 +++++++----- esphome/components/esp32_ble/ble.h | 32 ++++--- .../components/esp32_ble_beacon/__init__.py | 2 +- .../components/esp32_ble_server/__init__.py | 4 +- .../components/esp32_ble_tracker/__init__.py | 8 +- esphome/core/defines.h | 5 ++ 7 files changed, 153 insertions(+), 42 deletions(-) diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 1c142ca7bd..fc8c65a2f4 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -1,4 +1,5 @@ from collections.abc import Callable, MutableMapping +from dataclasses import dataclass from enum import Enum import logging import re @@ -16,7 +17,7 @@ from esphome.const import ( CONF_NAME, CONF_NAME_ADD_MAC_SUFFIX, ) -from esphome.core import CORE, TimePeriod +from esphome.core import CORE, CoroPriority, TimePeriod, coroutine_with_priority import esphome.final_validate as fv DEPENDENCIES = ["esp32"] @@ -111,6 +112,58 @@ class BTLoggers(Enum): _required_loggers: set[BTLoggers] = set() +# Dataclass for handler registration counts +@dataclass +class HandlerCounts: + gap_event: int = 0 + gap_scan_event: int = 0 + gattc_event: int = 0 + gatts_event: int = 0 + ble_status_event: int = 0 + + +# Track handler registration counts for StaticVector sizing +_handler_counts = HandlerCounts() + + +def register_gap_event_handler(parent_var: cg.MockObj, handler_var: cg.MockObj) -> None: + """Register a GAP event handler and track the count.""" + _handler_counts.gap_event += 1 + cg.add(parent_var.register_gap_event_handler(handler_var)) + + +def register_gap_scan_event_handler( + parent_var: cg.MockObj, handler_var: cg.MockObj +) -> None: + """Register a GAP scan event handler and track the count.""" + _handler_counts.gap_scan_event += 1 + cg.add(parent_var.register_gap_scan_event_handler(handler_var)) + + +def register_gattc_event_handler( + parent_var: cg.MockObj, handler_var: cg.MockObj +) -> None: + """Register a GATTc event handler and track the count.""" + _handler_counts.gattc_event += 1 + cg.add(parent_var.register_gattc_event_handler(handler_var)) + + +def register_gatts_event_handler( + parent_var: cg.MockObj, handler_var: cg.MockObj +) -> None: + """Register a GATTs event handler and track the count.""" + _handler_counts.gatts_event += 1 + cg.add(parent_var.register_gatts_event_handler(handler_var)) + + +def register_ble_status_event_handler( + parent_var: cg.MockObj, handler_var: cg.MockObj +) -> None: + """Register a BLE status event handler and track the count.""" + _handler_counts.ble_status_event += 1 + cg.add(parent_var.register_ble_status_event_handler(handler_var)) + + def register_bt_logger(*loggers: BTLoggers) -> None: """Register Bluetooth logger categories that a component needs. @@ -374,6 +427,36 @@ def final_validation(config): FINAL_VALIDATE_SCHEMA = final_validation +# This needs to be run as a job with CoroPriority.FINAL priority so that all components have +# a chance to register their handlers before the counts are added to defines. +@coroutine_with_priority(CoroPriority.FINAL) +async def _add_ble_handler_defines(): + # Add defines for StaticVector sizing based on handler registration counts + # Only define if count > 0 to avoid allocating unnecessary memory + if _handler_counts.gap_event > 0: + cg.add_define( + "ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT", _handler_counts.gap_event + ) + if _handler_counts.gap_scan_event > 0: + cg.add_define( + "ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT", + _handler_counts.gap_scan_event, + ) + if _handler_counts.gattc_event > 0: + cg.add_define( + "ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT", _handler_counts.gattc_event + ) + if _handler_counts.gatts_event > 0: + cg.add_define( + "ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT", _handler_counts.gatts_event + ) + if _handler_counts.ble_status_event > 0: + cg.add_define( + "ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT", + _handler_counts.ble_status_event, + ) + + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT])) @@ -428,6 +511,9 @@ async def to_code(config): cg.add_define("USE_ESP32_BLE_ADVERTISING") cg.add_define("USE_ESP32_BLE_UUID") + # Schedule the handler defines to be added after all components register + CORE.add_job(_add_ble_handler_defines) + @automation.register_condition("ble.enabled", BLEEnabledCondition, cv.Schema({})) async def ble_enabled_to_code(config, condition_id, template_arg, args): diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 1f6bc6d0a0..9f8ca1d149 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -181,31 +181,27 @@ bool ESP32BLE::ble_setup_() { return false; } - if (!this->gap_event_handlers_.empty()) { - err = esp_ble_gap_register_callback(ESP32BLE::gap_event_handler); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err); - return false; - } - } - -#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) { - ESP_LOGE(TAG, "esp_ble_gatts_register_callback failed: %d", err); - return false; - } +#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT + err = esp_ble_gap_register_callback(ESP32BLE::gap_event_handler); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err); + 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) { - ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err); - return false; - } +#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT) + err = esp_ble_gatts_register_callback(ESP32BLE::gatts_event_handler); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ble_gatts_register_callback failed: %d", err); + return false; + } +#endif + +#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT) + err = esp_ble_gattc_register_callback(ESP32BLE::gattc_event_handler); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err); + return false; } #endif @@ -302,9 +298,11 @@ void ESP32BLE::loop() { case BLE_COMPONENT_STATE_DISABLE: { ESP_LOGD(TAG, "Disabling"); +#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT for (auto *ble_event_handler : this->ble_status_event_handlers_) { ble_event_handler->ble_before_disabled_event_handler(); } +#endif if (!ble_dismantle_()) { ESP_LOGE(TAG, "Could not be dismantled"); @@ -334,7 +332,7 @@ void ESP32BLE::loop() { BLEEvent *ble_event = this->ble_events_.pop(); while (ble_event != nullptr) { switch (ble_event->type_) { -#ifdef USE_ESP32_BLE_SERVER +#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT) 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; @@ -346,7 +344,7 @@ void ESP32BLE::loop() { break; } #endif -#ifdef USE_ESP32_BLE_CLIENT +#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT) 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; @@ -362,10 +360,12 @@ void ESP32BLE::loop() { esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event; switch (gap_event) { case ESP_GAP_BLE_SCAN_RESULT_EVT: +#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT // Use the new scan event handler - no memcpy! for (auto *scan_handler : this->gap_scan_event_handlers_) { scan_handler->gap_scan_event_handler(ble_event->scan_result()); } +#endif break; // Scan complete events @@ -377,10 +377,12 @@ void ESP32BLE::loop() { // This is verified at compile-time by static_assert checks in ble_event.h // The struct already contains our copy of the status (copied in BLEEvent constructor) ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); +#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT for (auto *gap_handler : this->gap_event_handlers_) { gap_handler->gap_event_handler( gap_event, reinterpret_cast(&ble_event->event_.gap.scan_complete)); } +#endif break; // Advertising complete events @@ -391,19 +393,23 @@ void ESP32BLE::loop() { case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: // All advertising complete events have the same structure with just status ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); +#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT for (auto *gap_handler : this->gap_event_handlers_) { gap_handler->gap_event_handler( gap_event, reinterpret_cast(&ble_event->event_.gap.adv_complete)); } +#endif break; // RSSI complete event case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); +#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT for (auto *gap_handler : this->gap_event_handlers_) { gap_handler->gap_event_handler( gap_event, reinterpret_cast(&ble_event->event_.gap.read_rssi_complete)); } +#endif break; // Security events @@ -413,10 +419,12 @@ void ESP32BLE::loop() { case ESP_GAP_BLE_PASSKEY_REQ_EVT: case ESP_GAP_BLE_NC_REQ_EVT: ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); +#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT for (auto *gap_handler : this->gap_event_handlers_) { gap_handler->gap_event_handler( gap_event, reinterpret_cast(&ble_event->event_.gap.security)); } +#endif break; default: diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 1aa3bc86ef..dc973f0e82 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -125,19 +125,25 @@ class ESP32BLE : public Component { void advertising_register_raw_advertisement_callback(std::function &&callback); #endif +#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); } +#endif +#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT void register_gap_scan_event_handler(GAPScanEventHandler *handler) { this->gap_scan_event_handlers_.push_back(handler); } -#ifdef USE_ESP32_BLE_CLIENT +#endif +#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT) void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); } #endif -#ifdef USE_ESP32_BLE_SERVER +#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT) void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); } #endif +#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT void register_ble_status_event_handler(BLEStatusEventHandler *handler) { this->ble_status_event_handlers_.push_back(handler); } +#endif void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; } protected: @@ -159,16 +165,22 @@ class ESP32BLE : public Component { private: template friend void enqueue_ble_event(Args... args); - // 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_; + // Handler vectors - use StaticVector when counts are known at compile time +#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT + StaticVector gap_event_handlers_; #endif -#ifdef USE_ESP32_BLE_SERVER - std::vector gatts_event_handlers_; +#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT + StaticVector gap_scan_event_handlers_; +#endif +#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT) + StaticVector gattc_event_handlers_; +#endif +#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT) + StaticVector gatts_event_handlers_; +#endif +#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT + StaticVector ble_status_event_handlers_; #endif - std::vector ble_status_event_handlers_; // Large objects (size depends on template parameters, but typically aligned to 4 bytes) esphome::LockFreeQueue ble_events_; diff --git a/esphome/components/esp32_ble_beacon/__init__.py b/esphome/components/esp32_ble_beacon/__init__.py index 794f5637a4..ba5ae4331c 100644 --- a/esphome/components/esp32_ble_beacon/__init__.py +++ b/esphome/components/esp32_ble_beacon/__init__.py @@ -74,7 +74,7 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID], uuid_arr) parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID]) - cg.add(parent.register_gap_event_handler(var)) + esp32_ble.register_gap_event_handler(parent, var) await cg.register_component(var, config) cg.add(var.set_major(config[CONF_MAJOR])) diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index 10fa09fcc3..55310f3275 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -546,8 +546,8 @@ async def to_code(config): await cg.register_component(var, config) parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID]) - cg.add(parent.register_gatts_event_handler(var)) - cg.add(parent.register_ble_status_event_handler(var)) + esp32_ble.register_gatts_event_handler(parent, var) + esp32_ble.register_ble_status_event_handler(parent, var) cg.add(var.set_parent(parent)) cg.add(parent.advertising_set_appearance(config[CONF_APPEARANCE])) if CONF_MANUFACTURER_DATA in config: diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 8c7f3e3930..5910be67af 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -246,10 +246,10 @@ async def to_code(config): await cg.register_component(var, config) parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID]) - cg.add(parent.register_gap_event_handler(var)) - cg.add(parent.register_gap_scan_event_handler(var)) - cg.add(parent.register_gattc_event_handler(var)) - cg.add(parent.register_ble_status_event_handler(var)) + esp32_ble.register_gap_event_handler(parent, var) + esp32_ble.register_gap_scan_event_handler(parent, var) + esp32_ble.register_gattc_event_handler(parent, var) + esp32_ble.register_ble_status_event_handler(parent, var) cg.add(var.set_parent(parent)) params = config[CONF_SCAN_PARAMETERS] diff --git a/esphome/core/defines.h b/esphome/core/defines.h index ed30efd0b5..f770edaa00 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -178,6 +178,11 @@ #define USE_ESP32_BLE_SERVER_ON_DISCONNECT #define ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT 1 #define ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT 1 +#define ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT 2 +#define ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT 1 +#define ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT 1 +#define ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT 1 +#define ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT 2 #define USE_ESP32_CAMERA_JPEG_ENCODER #define USE_I2C #define USE_IMPROV From 8a15c18066d9d65515c81e8de3d5521a90d908f5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 17:05:13 -1000 Subject: [PATCH 054/526] [bluetooth_proxy] Use FixedVector for GATT characteristics and descriptors (#11214) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/api/api.proto | 4 +- esphome/components/api/api_options.proto | 6 + esphome/components/api/api_pb2.h | 4 +- esphome/components/api/proto.h | 26 +++- .../bluetooth_proxy/bluetooth_connection.cpp | 13 +- esphome/core/helpers.h | 121 ++++++++++++++++-- script/api_protobuf/api_protobuf.py | 4 + 7 files changed, 146 insertions(+), 32 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 87f477799d..9b714d00f1 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1519,7 +1519,7 @@ message BluetoothGATTCharacteristic { repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true]; uint32 handle = 2; uint32 properties = 3; - repeated BluetoothGATTDescriptor descriptors = 4; + repeated BluetoothGATTDescriptor descriptors = 4 [(fixed_vector) = true]; // New field for efficient UUID (v1.12+) // Only one of uuid or short_uuid will be set. @@ -1531,7 +1531,7 @@ message BluetoothGATTCharacteristic { message BluetoothGATTService { repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true]; uint32 handle = 2; - repeated BluetoothGATTCharacteristic characteristics = 3; + repeated BluetoothGATTCharacteristic characteristics = 3 [(fixed_vector) = true]; // New field for efficient UUID (v1.12+) // Only one of uuid or short_uuid will be set. diff --git a/esphome/components/api/api_options.proto b/esphome/components/api/api_options.proto index 633f39b552..ead8ac0bbc 100644 --- a/esphome/components/api/api_options.proto +++ b/esphome/components/api/api_options.proto @@ -64,4 +64,10 @@ extend google.protobuf.FieldOptions { // This is typically done through methods returning const T& or special accessor // methods like get_options() or supported_modes_for_api_(). optional string container_pointer = 50001; + + // fixed_vector: Use FixedVector instead of std::vector for repeated fields + // When set, the repeated field will use FixedVector which requires calling + // init(size) before adding elements. This eliminates std::vector template overhead + // and is ideal when the exact size is known before populating the array. + optional bool fixed_vector = 50013 [default=false]; } diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index d9e68ece9b..1798458393 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1923,7 +1923,7 @@ class BluetoothGATTCharacteristic final : public ProtoMessage { std::array uuid{}; uint32_t handle{0}; uint32_t properties{0}; - std::vector descriptors{}; + FixedVector descriptors{}; uint32_t short_uuid{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(ProtoSize &size) const override; @@ -1937,7 +1937,7 @@ class BluetoothGATTService final : public ProtoMessage { public: std::array uuid{}; uint32_t handle{0}; - std::vector characteristics{}; + FixedVector characteristics{}; uint32_t short_uuid{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(ProtoSize &size) const override; diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 9d780692ec..a6a09bf7c5 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -749,13 +749,29 @@ class ProtoSize { template inline void add_repeated_message(uint32_t field_id_size, const std::vector &messages) { // Skip if the vector is empty - if (messages.empty()) { - return; + if (!messages.empty()) { + // Use the force version for all messages in the repeated field + for (const auto &message : messages) { + add_message_object_force(field_id_size, message); + } } + } - // Use the force version for all messages in the repeated field - for (const auto &message : messages) { - add_message_object_force(field_id_size, message); + /** + * @brief Calculates and adds the sizes of all messages in a repeated field to the total message size (FixedVector + * version) + * + * @tparam MessageType The type of the nested messages in the FixedVector + * @param messages FixedVector of message objects + */ + template + inline void add_repeated_message(uint32_t field_id_size, const FixedVector &messages) { + // Skip if the fixed vector is empty + if (!messages.empty()) { + // Use the force version for all messages in the repeated field + for (const auto &message : messages) { + add_message_object_force(field_id_size, message); + } } } }; diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp index cde82fbfb0..fcc344dda9 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp @@ -230,8 +230,8 @@ void BluetoothConnection::send_service_for_discovery_() { service_resp.handle = service_result.start_handle; if (total_char_count > 0) { - // Reserve space and process characteristics - service_resp.characteristics.reserve(total_char_count); + // Initialize FixedVector with exact count and process characteristics + service_resp.characteristics.init(total_char_count); uint16_t char_offset = 0; esp_gattc_char_elem_t char_result; while (true) { // characteristics @@ -253,9 +253,7 @@ void BluetoothConnection::send_service_for_discovery_() { service_resp.characteristics.emplace_back(); auto &characteristic_resp = service_resp.characteristics.back(); - fill_gatt_uuid(characteristic_resp.uuid, characteristic_resp.short_uuid, char_result.uuid, use_efficient_uuids); - characteristic_resp.handle = char_result.char_handle; characteristic_resp.properties = char_result.properties; char_offset++; @@ -271,12 +269,11 @@ void BluetoothConnection::send_service_for_discovery_() { return; } if (total_desc_count == 0) { - // No descriptors, continue to next characteristic continue; } - // Reserve space and process descriptors - characteristic_resp.descriptors.reserve(total_desc_count); + // Initialize FixedVector with exact count and process descriptors + characteristic_resp.descriptors.init(total_desc_count); uint16_t desc_offset = 0; esp_gattc_descr_elem_t desc_result; while (true) { // descriptors @@ -297,9 +294,7 @@ void BluetoothConnection::send_service_for_discovery_() { characteristic_resp.descriptors.emplace_back(); auto &descriptor_resp = characteristic_resp.descriptors.back(); - fill_gatt_uuid(descriptor_resp.uuid, descriptor_resp.short_uuid, desc_result.uuid, use_efficient_uuids); - descriptor_resp.handle = desc_result.handle; desc_offset++; } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index ed1d5ac108..e352c9c415 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -168,26 +168,83 @@ template class FixedVector { size_t size_{0}; size_t capacity_{0}; - public: - FixedVector() = default; - - ~FixedVector() { - if (data_ != nullptr) { - delete[] data_; + // Helper to destroy all elements without freeing memory + void destroy_elements_() { + // Only call destructors for non-trivially destructible types + if constexpr (!std::is_trivially_destructible::value) { + for (size_t i = 0; i < size_; i++) { + data_[i].~T(); + } } } - // Disable copy to avoid accidental copies + // Helper to destroy elements and free memory + void cleanup_() { + if (data_ != nullptr) { + destroy_elements_(); + // Free raw memory + ::operator delete(data_); + } + } + + // Helper to reset pointers after cleanup + void reset_() { + data_ = nullptr; + capacity_ = 0; + size_ = 0; + } + + public: + FixedVector() = default; + + ~FixedVector() { cleanup_(); } + + // Disable copy operations (avoid accidental expensive copies) FixedVector(const FixedVector &) = delete; FixedVector &operator=(const FixedVector &) = delete; - // Allocate capacity - can only be called once on empty vector - void init(size_t n) { - if (data_ == nullptr && n > 0) { - data_ = new T[n]; - capacity_ = n; - size_ = 0; + // Enable move semantics (allows use in move-only containers like std::vector) + FixedVector(FixedVector &&other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) { + other.reset_(); + } + + FixedVector &operator=(FixedVector &&other) noexcept { + if (this != &other) { + // Delete our current data + cleanup_(); + // Take ownership of other's data + data_ = other.data_; + size_ = other.size_; + capacity_ = other.capacity_; + // Leave other in valid empty state + other.reset_(); } + return *this; + } + + // Allocate capacity - can be called multiple times to reinit + void init(size_t n) { + cleanup_(); + reset_(); + if (n > 0) { + // Allocate raw memory without calling constructors + // sizeof(T) is correct here for any type T (value types, pointers, etc.) + // NOLINTNEXTLINE(bugprone-sizeof-expression) + data_ = static_cast(::operator new(n * sizeof(T))); + capacity_ = n; + } + } + + // Clear the vector (destroy all elements, reset size to 0, keep capacity) + void clear() { + destroy_elements_(); + size_ = 0; + } + + // Shrink capacity to fit current size (frees all memory) + void shrink_to_fit() { + cleanup_(); + reset_(); } /// Add element without bounds checking @@ -195,16 +252,52 @@ template class FixedVector { /// Silently ignores pushes beyond capacity (no exception or assertion) void push_back(const T &value) { if (size_ < capacity_) { - data_[size_++] = value; + // Use placement new to construct the object in pre-allocated memory + new (&data_[size_]) T(value); + size_++; } } + /// Add element by move without bounds checking + /// Caller must ensure sufficient capacity was allocated via init() + /// Silently ignores pushes beyond capacity (no exception or assertion) + void push_back(T &&value) { + if (size_ < capacity_) { + // Use placement new to move-construct the object in pre-allocated memory + new (&data_[size_]) T(std::move(value)); + size_++; + } + } + + /// Emplace element without bounds checking - constructs in-place + /// Caller must ensure sufficient capacity was allocated via init() + /// Returns reference to the newly constructed element + /// NOTE: Caller MUST ensure size_ < capacity_ before calling + T &emplace_back() { + // Use placement new to default-construct the object in pre-allocated memory + new (&data_[size_]) T(); + size_++; + return data_[size_ - 1]; + } + + /// Access last element (no bounds checking - matches std::vector behavior) + /// Caller must ensure vector is not empty (size() > 0) + T &back() { return data_[size_ - 1]; } + const T &back() const { return data_[size_ - 1]; } + size_t size() const { return size_; } + bool empty() const { return size_ == 0; } /// Access element without bounds checking (matches std::vector behavior) /// Caller must ensure index is valid (i < size()) T &operator[](size_t i) { return data_[i]; } const T &operator[](size_t i) const { return data_[i]; } + + // Iterator support for range-based for loops + T *begin() { return data_; } + T *end() { return data_ + size_; } + const T *begin() const { return data_; } + const T *end() const { return data_ + size_; } }; ///@} diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 487c187372..9a55f1d136 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -1415,6 +1415,8 @@ class RepeatedTypeInfo(TypeInfo): # Check if this is a pointer field by looking for container_pointer option self._container_type = get_field_opt(field, pb.container_pointer, "") self._use_pointer = bool(self._container_type) + # Check if this should use FixedVector instead of std::vector + self._use_fixed_vector = get_field_opt(field, pb.fixed_vector, False) # For repeated fields, we need to get the base type info # but we can't call create_field_type_info as it would cause recursion @@ -1438,6 +1440,8 @@ class RepeatedTypeInfo(TypeInfo): if "<" in self._container_type and ">" in self._container_type: return f"const {self._container_type}*" return f"const {self._container_type}<{self._ti.cpp_type}>*" + if self._use_fixed_vector: + return f"FixedVector<{self._ti.cpp_type}>" return f"std::vector<{self._ti.cpp_type}>" @property From 4c688a4b008cb89fff6f60e70a68da7dd6483c54 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 17:54:33 -1000 Subject: [PATCH 055/526] [network] Optimize get_use_address() to return const reference instead of a copy (#11218) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../ethernet/ethernet_component.cpp | 11 +++-------- .../components/ethernet/ethernet_component.h | 2 +- esphome/components/network/util.cpp | 19 +++++++++++-------- esphome/components/network/util.h | 2 +- esphome/components/wifi/wifi_component.cpp | 11 +++-------- esphome/components/wifi/wifi_component.h | 2 +- 6 files changed, 20 insertions(+), 27 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 13adab8815..24b6e8154b 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -689,14 +689,9 @@ void EthernetComponent::add_phy_register(PHYRegister register_value) { this->phy void EthernetComponent::set_type(EthernetType type) { this->type_ = type; } void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; } -std::string EthernetComponent::get_use_address() const { - if (this->use_address_.empty()) { - // ".local" suffix length for mDNS hostnames - constexpr size_t mdns_local_suffix_len = 5; - return make_name_with_suffix(App.get_name(), '.', "local", mdns_local_suffix_len); - } - return this->use_address_; -} +// set_use_address() is guaranteed to be called during component setup by Python code generation, +// so use_address_ will always be valid when get_use_address() is called - no fallback needed. +const std::string &EthernetComponent::get_use_address() const { return this->use_address_; } void EthernetComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 6b4e342df5..d5dda3e3ae 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -88,7 +88,7 @@ class EthernetComponent : public Component { network::IPAddresses get_ip_addresses(); network::IPAddress get_dns_address(uint8_t num); - std::string get_use_address() const; + const std::string &get_use_address() const; void set_use_address(const std::string &use_address); void get_eth_mac_address_raw(uint8_t *mac); std::string get_eth_mac_address_pretty(); diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp index bf76aefc30..27ad9448a4 100644 --- a/esphome/components/network/util.cpp +++ b/esphome/components/network/util.cpp @@ -85,22 +85,25 @@ network::IPAddresses get_ip_addresses() { return {}; } -std::string get_use_address() { +const std::string &get_use_address() { + // Global component pointers are guaranteed to be set by component constructors when USE_* is defined #ifdef USE_ETHERNET - if (ethernet::global_eth_component != nullptr) - return ethernet::global_eth_component->get_use_address(); + return ethernet::global_eth_component->get_use_address(); #endif #ifdef USE_MODEM - if (modem::global_modem_component != nullptr) - return modem::global_modem_component->get_use_address(); + return modem::global_modem_component->get_use_address(); #endif #ifdef USE_WIFI - if (wifi::global_wifi_component != nullptr) - return wifi::global_wifi_component->get_use_address(); + return wifi::global_wifi_component->get_use_address(); +#endif + +#if !defined(USE_ETHERNET) && !defined(USE_MODEM) && !defined(USE_WIFI) + // Fallback when no network component is defined (e.g., host platform) + static const std::string empty; + return empty; #endif - return ""; } } // namespace network diff --git a/esphome/components/network/util.h b/esphome/components/network/util.h index b518696e68..b4a92f8bee 100644 --- a/esphome/components/network/util.h +++ b/esphome/components/network/util.h @@ -12,7 +12,7 @@ bool is_connected(); /// Return whether the network is disabled (only wifi for now) bool is_disabled(); /// Get the active network hostname -std::string get_use_address(); +const std::string &get_use_address(); IPAddresses get_ip_addresses(); } // namespace network diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 0f9f879181..aa197b36e5 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -265,14 +265,9 @@ network::IPAddress WiFiComponent::get_dns_address(int num) { return this->wifi_dns_ip_(num); return {}; } -std::string WiFiComponent::get_use_address() const { - if (this->use_address_.empty()) { - // ".local" suffix length for mDNS hostnames - constexpr size_t mdns_local_suffix_len = 5; - return make_name_with_suffix(App.get_name(), '.', "local", mdns_local_suffix_len); - } - return this->use_address_; -} +// set_use_address() is guaranteed to be called during component setup by Python code generation, +// so use_address_ will always be valid when get_use_address() is called - no fallback needed. +const std::string &WiFiComponent::get_use_address() const { return this->use_address_; } void WiFiComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } #ifdef USE_WIFI_AP diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index ee62ec1a69..0e0f89d2c1 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -275,7 +275,7 @@ class WiFiComponent : public Component { network::IPAddress get_dns_address(int num); network::IPAddresses get_ip_addresses(); - std::string get_use_address() const; + const std::string &get_use_address() const; void set_use_address(const std::string &use_address); const std::vector &get_scan_result() const { return scan_result_; } From 8e9a68a107c12a8e4ff5256adcecc40badce3db9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 18:12:52 -1000 Subject: [PATCH 056/526] Bump aioesphomeapi from 41.16.0 to 41.16.1 (#11221) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1142c587b6..4425a4644e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==41.16.0 +aioesphomeapi==41.16.1 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import From baa010583ee2aa7c30968952099529f553279ac4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 21:09:48 -1000 Subject: [PATCH 057/526] [docs] Add state management best practices to CLAUDE.md (#11224) --- .ai/instructions.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.ai/instructions.md b/.ai/instructions.md index d2e173472a..8daee704de 100644 --- a/.ai/instructions.md +++ b/.ai/instructions.md @@ -221,6 +221,48 @@ This document provides essential context for AI models interacting with this pro * **Component Development:** Keep dependencies minimal, provide clear error messages, and write comprehensive docstrings and tests. * **Code Generation:** Generate minimal and efficient C++ code. Validate all user inputs thoroughly. Support multiple platform variations. * **Configuration Design:** Aim for simplicity with sensible defaults, while allowing for advanced customization. + * **State Management:** Use `CORE.data` for component state that needs to persist during configuration generation. Avoid module-level mutable globals. + + **Bad Pattern (Module-Level Globals):** + ```python + # Don't do this - state persists between compilation runs + _component_state = [] + _use_feature = None + + def enable_feature(): + global _use_feature + _use_feature = True + ``` + + **Good Pattern (CORE.data with Helpers):** + ```python + from esphome.core import CORE + + # Keys for CORE.data storage + COMPONENT_STATE_KEY = "my_component_state" + USE_FEATURE_KEY = "my_component_use_feature" + + def _get_component_state() -> list: + """Get component state from CORE.data.""" + return CORE.data.setdefault(COMPONENT_STATE_KEY, []) + + def _get_use_feature() -> bool | None: + """Get feature flag from CORE.data.""" + return CORE.data.get(USE_FEATURE_KEY) + + def _set_use_feature(value: bool) -> None: + """Set feature flag in CORE.data.""" + CORE.data[USE_FEATURE_KEY] = value + + def enable_feature(): + _set_use_feature(True) + ``` + + **Why this matters:** + - Module-level globals persist between compilation runs if the dashboard doesn't fork/exec + - `CORE.data` automatically clears between runs + - Typed helper functions provide better IDE support and maintainability + - Encapsulation makes state management explicit and testable * **Security:** Be mindful of security when making changes to the API, web server, or any other network-related code. Do not hardcode secrets or keys. From cbdb9d4a5623fa55f8dd632302ec7c789228d3be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 08:06:18 +0000 Subject: [PATCH 058/526] Bump aioesphomeapi from 41.16.1 to 41.17.0 (#11231) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4425a4644e..a3269b6b88 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==41.16.1 +aioesphomeapi==41.17.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import From 2175c2909b5027c6cf11b94e369191a027f3eae2 Mon Sep 17 00:00:00 2001 From: TJQ <61875111+tjq19940331@users.noreply.github.com> Date: Tue, 14 Oct 2025 19:28:06 +0800 Subject: [PATCH 059/526] [mipi_dsi] Update waveshare P4-86 display parameters (#10562) --- .../components/mipi_dsi/models/waveshare.py | 63 ++++++++----------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/esphome/components/mipi_dsi/models/waveshare.py b/esphome/components/mipi_dsi/models/waveshare.py index 7cfd6f1645..c3d080f8b2 100644 --- a/esphome/components/mipi_dsi/models/waveshare.py +++ b/esphome/components/mipi_dsi/models/waveshare.py @@ -56,50 +56,41 @@ DriverChip( "WAVESHARE-P4-86-PANEL", height=720, width=720, - hsync_back_porch=80, + hsync_back_porch=50, hsync_pulse_width=20, - hsync_front_porch=80, - vsync_back_porch=12, + hsync_front_porch=50, + vsync_back_porch=20, vsync_pulse_width=4, - vsync_front_porch=30, - pclk_frequency="46MHz", - lane_bit_rate="1Gbps", + vsync_front_porch=20, + pclk_frequency="38MHz", + lane_bit_rate="480Mbps", swap_xy=cv.UNDEFINED, color_order="RGB", reset_pin=27, initsequence=[ (0xB9, 0xF1, 0x12, 0x83), - ( - 0xBA, 0x31, 0x81, 0x05, 0xF9, 0x0E, 0x0E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 0x00, - 0x90, 0x0A, 0x00, 0x00, 0x01, 0x4F, 0x01, 0x00, 0x00, 0x37, - ), - (0xB8, 0x25, 0x22, 0xF0, 0x63), - (0xBF, 0x02, 0x11, 0x00), + (0xB1, 0x00, 0x00, 0x00, 0xDA, 0x80), + (0xB2, 0x3C, 0x12, 0x30), (0xB3, 0x10, 0x10, 0x28, 0x28, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00), - (0xC0, 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x12, 0x70, 0x00), - (0xBC, 0x46), (0xCC, 0x0B), (0xB4, 0x80), (0xB2, 0x3C, 0x12, 0x30), - (0xE3, 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10,), - (0xC1, 0x36, 0x00, 0x32, 0x32, 0x77, 0xF1, 0xCC, 0xCC, 0x77, 0x77, 0x33, 0x33), + (0xB4, 0x80), (0xB5, 0x0A, 0x0A), - (0xB6, 0xB2, 0xB2), - ( - 0xE9, 0xC8, 0x10, 0x0A, 0x10, 0x0F, 0xA1, 0x80, 0x12, 0x31, 0x23, 0x47, 0x86, 0xA1, 0x80, - 0x47, 0x08, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x48, - 0x02, 0x8B, 0xAF, 0x46, 0x02, 0x88, 0x88, 0x88, 0x88, 0x88, 0x48, 0x13, 0x8B, 0xAF, 0x57, - 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ), - ( - 0xEA, 0x96, 0x12, 0x01, 0x01, 0x01, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x31, - 0x8B, 0xA8, 0x31, 0x75, 0x88, 0x88, 0x88, 0x88, 0x88, 0x4F, 0x20, 0x8B, 0xA8, 0x20, 0x64, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xA1, 0x80, 0x00, 0x00, - 0x00, 0x00, - ), - ( - 0xE0, 0x00, 0x0A, 0x0F, 0x29, 0x3B, 0x3F, 0x42, 0x39, 0x06, 0x0D, 0x10, 0x13, 0x15, 0x14, - 0x15, 0x10, 0x17, 0x00, 0x0A, 0x0F, 0x29, 0x3B, 0x3F, 0x42, 0x39, 0x06, 0x0D, 0x10, 0x13, - 0x15, 0x14, 0x15, 0x10, 0x17, - ), + (0xB6, 0x97, 0x97), + (0xB8, 0x26, 0x22, 0xF0, 0x13), + (0xBA, 0x31, 0x81, 0x0F, 0xF9, 0x0E, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 0x00, 0x90, 0x0A, 0x00, 0x00, 0x01, 0x4F, 0x01, 0x00, 0x00, 0x37), + (0xBC, 0x47), + (0xBF, 0x02, 0x11, 0x00), + (0xC0, 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x12, 0x70, 0x00), + (0xC1, 0x25, 0x00, 0x32, 0x32, 0x77, 0xE4, 0xFF, 0xFF, 0xCC, 0xCC, 0x77, 0x77), + (0xC6, 0x82, 0x00, 0xBF, 0xFF, 0x00, 0xFF), + (0xC7, 0xB8, 0x00, 0x0A, 0x10, 0x01, 0x09), + (0xC8, 0x10, 0x40, 0x1E, 0x02), + (0xCC, 0x0B), + (0xE0, 0x00, 0x0B, 0x10, 0x2C, 0x3D, 0x3F, 0x42, 0x3A, 0x07, 0x0D, 0x0F, 0x13, 0x15, 0x13, 0x14, 0x0F, 0x16, 0x00, 0x0B, 0x10, 0x2C, 0x3D, 0x3F, 0x42, 0x3A, 0x07, 0x0D, 0x0F, 0x13, 0x15, 0x13, 0x14, 0x0F, 0x16), + (0xE3, 0x07, 0x07, 0x0B, 0x0B, 0x0B, 0x0B, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10), + (0xE9, 0xC8, 0x10, 0x0A, 0x00, 0x00, 0x80, 0x81, 0x12, 0x31, 0x23, 0x4F, 0x86, 0xA0, 0x00, 0x47, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x98, 0x02, 0x8B, 0xAF, 0x46, 0x02, 0x88, 0x88, 0x88, 0x88, 0x88, 0x98, 0x13, 0x8B, 0xAF, 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), + (0xEA, 0x97, 0x0C, 0x09, 0x09, 0x09, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x31, 0x8B, 0xA8, 0x31, 0x75, 0x88, 0x88, 0x88, 0x88, 0x88, 0x9F, 0x20, 0x8B, 0xA8, 0x20, 0x64, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00, 0x00, 0x02, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x81, 0x00, 0x00, 0x00, 0x00), + (0xEF, 0xFF, 0xFF, 0x01), + (0x11, 0x00), + (0x29, 0x00), ], ) From 0e703ddbba1815c34b1d4670dc9f6cceff66a0a9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 08:54:16 -1000 Subject: [PATCH 060/526] [docs] Add embedded systems optimization best practices to AI instructions (#11225) --- .ai/instructions.md | 98 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/.ai/instructions.md b/.ai/instructions.md index 8daee704de..5f314a0dc9 100644 --- a/.ai/instructions.md +++ b/.ai/instructions.md @@ -221,6 +221,104 @@ This document provides essential context for AI models interacting with this pro * **Component Development:** Keep dependencies minimal, provide clear error messages, and write comprehensive docstrings and tests. * **Code Generation:** Generate minimal and efficient C++ code. Validate all user inputs thoroughly. Support multiple platform variations. * **Configuration Design:** Aim for simplicity with sensible defaults, while allowing for advanced customization. + * **Embedded Systems Optimization:** ESPHome targets resource-constrained microcontrollers. Be mindful of flash size and RAM usage. + + **STL Container Guidelines:** + + ESPHome runs on embedded systems with limited resources. Choose containers carefully: + + 1. **Compile-time-known sizes:** Use `std::array` instead of `std::vector` when size is known at compile time. + ```cpp + // Bad - generates STL realloc code + std::vector values; + + // Good - no dynamic allocation + std::array values; + ``` + Use `cg.add_define("MAX_VALUES", count)` to set the size from Python configuration. + + **For byte buffers:** Avoid `std::vector` unless the buffer needs to grow. Use `std::unique_ptr` instead. + + > **Note:** `std::unique_ptr` does **not** provide bounds checking or iterator support like `std::vector`. Use it only when you do not need these features and want minimal overhead. + + ```cpp + // Bad - STL overhead for simple byte buffer + std::vector buffer; + buffer.resize(256); + + // Good - minimal overhead, single allocation + std::unique_ptr buffer = std::make_unique(256); + // Or if size is constant: + std::array buffer; + ``` + + 2. **Compile-time-known fixed sizes with vector-like API:** Use `StaticVector` from `esphome/core/helpers.h` for fixed-size stack allocation with `push_back()` interface. + ```cpp + // Bad - generates STL realloc code (_M_realloc_insert) + std::vector services; + services.reserve(5); // Still includes reallocation machinery + + // Good - compile-time fixed size, stack allocated, no reallocation machinery + StaticVector services; // Allocates all MAX_SERVICES on stack + services.push_back(record1); // Tracks count but all slots allocated + ``` + Use `cg.add_define("MAX_SERVICES", count)` to set the size from Python configuration. + Like `std::array` but with vector-like API (`push_back()`, `size()`) and no STL reallocation code. + + 3. **Runtime-known sizes:** Use `FixedVector` from `esphome/core/helpers.h` when the size is only known at runtime initialization. + ```cpp + // Bad - generates STL realloc code (_M_realloc_insert) + std::vector txt_records; + txt_records.reserve(5); // Still includes reallocation machinery + + // Good - runtime size, single allocation, no reallocation machinery + FixedVector txt_records; + txt_records.init(record_count); // Initialize with exact size at runtime + ``` + **Benefits:** + - Eliminates `_M_realloc_insert`, `_M_default_append` template instantiations (saves 200-500 bytes per instance) + - Single allocation, no upper bound needed + - No reallocation overhead + - Compatible with protobuf code generation when using `[(fixed_vector) = true]` option + + 4. **Small datasets (1-16 elements):** Use `std::vector` or `std::array` with simple structs instead of `std::map`/`std::set`/`std::unordered_map`. + ```cpp + // Bad - 2KB+ overhead for red-black tree/hash table + std::map small_lookup; + std::unordered_map tiny_map; + + // Good - simple struct with linear search (std::vector is fine) + struct LookupEntry { + const char *key; + int value; + }; + std::vector small_lookup = { + {"key1", 10}, + {"key2", 20}, + {"key3", 30}, + }; + // Or std::array if size is compile-time constant: + // std::array small_lookup = {{ ... }}; + ``` + Linear search on small datasets (1-16 elements) is often faster than hashing/tree overhead, but this depends on lookup frequency and access patterns. For frequent lookups in hot code paths, the O(1) vs O(n) complexity difference may still matter even for small datasets. `std::vector` with simple structs is usually fine—it's the heavy containers (`map`, `set`, `unordered_map`) that should be avoided for small datasets unless profiling shows otherwise. + + 5. **Detection:** Look for these patterns in compiler output: + - Large code sections with STL symbols (vector, map, set) + - `alloc`, `realloc`, `dealloc` in symbol names + - `_M_realloc_insert`, `_M_default_append` (vector reallocation) + - Red-black tree code (`rb_tree`, `_Rb_tree`) + - Hash table infrastructure (`unordered_map`, `hash`) + + **When to optimize:** + - Core components (API, network, logger) + - Widely-used components (mdns, wifi, ble) + - Components causing flash size complaints + + **When not to optimize:** + - Single-use niche components + - Code where readability matters more than bytes + - Already using appropriate containers + * **State Management:** Use `CORE.data` for component state that needs to persist during configuration generation. Avoid module-level mutable globals. **Bad Pattern (Module-Level Globals):** From 9ff6f344ab1633d0a73aabf9f60d30b0d35ae4b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:49:30 -1000 Subject: [PATCH 061/526] Bump ruamel-yaml-clib from 0.2.12 to 0.2.14 (#10842) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a3269b6b88..f9e98f999e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ aioesphomeapi==41.17.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import -ruamel.yaml.clib==0.2.12 # dashboard_import +ruamel.yaml.clib==0.2.14 # dashboard_import esphome-glyphsets==0.2.0 pillow==10.4.0 cairosvg==2.8.2 From 8f49b1da54885c3ef55088f281ddb9730e0d3665 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 11:49:39 -1000 Subject: [PATCH 062/526] Bump pillow to 11.3.0 (#11239) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f9e98f999e..1abfed0324 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import ruamel.yaml.clib==0.2.14 # dashboard_import esphome-glyphsets==0.2.0 -pillow==10.4.0 +pillow==11.3.0 cairosvg==2.8.2 freetype-py==2.5.1 jinja2==3.1.6 From 72ec9b672e8ae557e1a63b03ec7fc271b908cf82 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 12:33:19 -1000 Subject: [PATCH 063/526] [pzemac, pzemdc, sdm_meter] Fix pin conflicts in ESP32-IDF tests (#11240) --- tests/components/pzemac/test.esp32-idf.yaml | 1 + tests/components/pzemdc/test.esp32-idf.yaml | 1 + tests/components/sdm_meter/test.esp32-idf.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/components/pzemac/test.esp32-idf.yaml b/tests/components/pzemac/test.esp32-idf.yaml index 37d98696cc..b631e16677 100644 --- a/tests/components/pzemac/test.esp32-idf.yaml +++ b/tests/components/pzemac/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 + flow_control_pin: GPIO13 packages: modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml diff --git a/tests/components/pzemdc/test.esp32-idf.yaml b/tests/components/pzemdc/test.esp32-idf.yaml index 37d98696cc..b631e16677 100644 --- a/tests/components/pzemdc/test.esp32-idf.yaml +++ b/tests/components/pzemdc/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 + flow_control_pin: GPIO13 packages: modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml diff --git a/tests/components/sdm_meter/test.esp32-idf.yaml b/tests/components/sdm_meter/test.esp32-idf.yaml index 37d98696cc..b631e16677 100644 --- a/tests/components/sdm_meter/test.esp32-idf.yaml +++ b/tests/components/sdm_meter/test.esp32-idf.yaml @@ -1,6 +1,7 @@ substitutions: tx_pin: GPIO4 rx_pin: GPIO5 + flow_control_pin: GPIO13 packages: modbus: !include ../../test_build_components/common/modbus/esp32-idf.yaml From b927b29a0a70806acababcc965125db2bb43f078 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:37:27 +1300 Subject: [PATCH 064/526] [netlify] Pin python version (#11244) --- netlify.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/netlify.toml b/netlify.toml index 7783414a91..5f177e6b3a 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,3 +1,4 @@ [build] command = "script/build-api-docs" publish = "api-docs" + environment = { PYTHON_VERSION = "3.13" } From d75ae357c220b67c12d3a33a6b785e4e113579c7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 14:25:31 -1000 Subject: [PATCH 065/526] [wifi] Free scan results memory after connection (saves up to 1.2KB RAM) (#11205) --- esphome/components/wifi/__init__.py | 24 +++++++++++++++++++++ esphome/components/wifi/wifi_component.cpp | 6 ++++++ esphome/components/wifi/wifi_component.h | 2 ++ esphome/components/wifi_info/text_sensor.py | 6 ++++-- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index a784123006..ad5698519b 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -447,6 +447,8 @@ async def to_code(config): var.get_disconnect_trigger(), [], on_disconnect_config ) + CORE.add_job(final_step) + @automation.register_condition("wifi.connected", WiFiConnectedCondition, cv.Schema({})) async def wifi_connected_to_code(config, condition_id, template_arg, args): @@ -468,6 +470,28 @@ async def wifi_disable_to_code(config, action_id, template_arg, args): return cg.new_Pvariable(action_id, template_arg) +KEEP_SCAN_RESULTS_KEY = "wifi_keep_scan_results" + + +def request_wifi_scan_results(): + """Request that WiFi scan results be kept in memory after connection. + + Components that need access to scan results after WiFi is connected should + call this function during their code generation. This prevents the WiFi component from + freeing scan result memory after successful connection. + """ + CORE.data[KEEP_SCAN_RESULTS_KEY] = True + + +@coroutine_with_priority(CoroPriority.FINAL) +async def final_step(): + """Final code generation step to configure scan result retention.""" + if CORE.data.get(KEEP_SCAN_RESULTS_KEY, False): + cg.add( + cg.RawExpression("wifi::global_wifi_component->set_keep_scan_results(true)") + ) + + @automation.register_action( "wifi.configure", WiFiConfigureAction, diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index aa197b36e5..2e0a0d95c2 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -713,6 +713,12 @@ void WiFiComponent::check_connecting_finished() { this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTED; this->num_retried_ = 0; + // Free scan results memory unless a component needs them + if (!this->keep_scan_results_) { + this->scan_result_.clear(); + this->scan_result_.shrink_to_fit(); + } + if (this->fast_connect_) { this->save_fast_connect_settings_(); } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 0e0f89d2c1..cd899cec5b 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -316,6 +316,7 @@ class WiFiComponent : public Component { int8_t wifi_rssi(); void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; } + void set_keep_scan_results(bool keep_scan_results) { this->keep_scan_results_ = keep_scan_results; } Trigger<> *get_connect_trigger() const { return this->connect_trigger_; }; Trigger<> *get_disconnect_trigger() const { return this->disconnect_trigger_; }; @@ -424,6 +425,7 @@ class WiFiComponent : public Component { #endif bool enable_on_boot_; bool got_ipv4_address_{false}; + bool keep_scan_results_{false}; // Pointers at the end (naturally aligned) Trigger<> *connect_trigger_{new Trigger<>()}; diff --git a/esphome/components/wifi_info/text_sensor.py b/esphome/components/wifi_info/text_sensor.py index 4ceb73a695..a4da582c55 100644 --- a/esphome/components/wifi_info/text_sensor.py +++ b/esphome/components/wifi_info/text_sensor.py @@ -1,5 +1,5 @@ import esphome.codegen as cg -from esphome.components import text_sensor +from esphome.components import text_sensor, wifi import esphome.config_validation as cv from esphome.const import ( CONF_BSSID, @@ -77,7 +77,9 @@ async def to_code(config): await setup_conf(config, CONF_SSID) await setup_conf(config, CONF_BSSID) await setup_conf(config, CONF_MAC_ADDRESS) - await setup_conf(config, CONF_SCAN_RESULTS) + if CONF_SCAN_RESULTS in config: + await setup_conf(config, CONF_SCAN_RESULTS) + wifi.request_wifi_scan_results() await setup_conf(config, CONF_DNS_ADDRESS) if conf := config.get(CONF_IP_ADDRESS): wifi_info = await text_sensor.new_text_sensor(config[CONF_IP_ADDRESS]) From 63a87a5ef3c551f5046ddad9e4d9bf19028f6ebe Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 14:27:10 -1000 Subject: [PATCH 066/526] [core] Use FixedVector for automation condition vectors to save 384 bytes flash (#11237) --- esphome/core/base_automation.h | 13 +++++++------ esphome/core/helpers.h | 12 ++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index ba942e5e43..f1248e0035 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -7,6 +7,7 @@ #include "esphome/core/preferences.h" #include "esphome/core/scheduler.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" #include @@ -14,7 +15,7 @@ namespace esphome { template class AndCondition : public Condition { public: - explicit AndCondition(const std::vector *> &conditions) : conditions_(conditions) {} + explicit AndCondition(std::initializer_list *> conditions) : conditions_(conditions) {} bool check(Ts... x) override { for (auto *condition : this->conditions_) { if (!condition->check(x...)) @@ -25,12 +26,12 @@ template class AndCondition : public Condition { } protected: - std::vector *> conditions_; + FixedVector *> conditions_; }; template class OrCondition : public Condition { public: - explicit OrCondition(const std::vector *> &conditions) : conditions_(conditions) {} + explicit OrCondition(std::initializer_list *> conditions) : conditions_(conditions) {} bool check(Ts... x) override { for (auto *condition : this->conditions_) { if (condition->check(x...)) @@ -41,7 +42,7 @@ template class OrCondition : public Condition { } protected: - std::vector *> conditions_; + FixedVector *> conditions_; }; template class NotCondition : public Condition { @@ -55,7 +56,7 @@ template class NotCondition : public Condition { template class XorCondition : public Condition { public: - explicit XorCondition(const std::vector *> &conditions) : conditions_(conditions) {} + explicit XorCondition(std::initializer_list *> conditions) : conditions_(conditions) {} bool check(Ts... x) override { size_t result = 0; for (auto *condition : this->conditions_) { @@ -66,7 +67,7 @@ template class XorCondition : public Condition { } protected: - std::vector *> conditions_; + FixedVector *> conditions_; }; template class LambdaCondition : public Condition { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index e352c9c415..326718e974 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -197,6 +197,18 @@ template class FixedVector { public: FixedVector() = default; + /// Constructor from initializer list - allocates exact size needed + /// This enables brace initialization: FixedVector v = {1, 2, 3}; + FixedVector(std::initializer_list init_list) { + init(init_list.size()); + size_t idx = 0; + for (const auto &item : init_list) { + new (data_ + idx) T(item); + ++idx; + } + size_ = init_list.size(); + } + ~FixedVector() { cleanup_(); } // Disable copy operations (avoid accidental expensive copies) From 00230f7cc6582c9b2b8d47497423af60f07c6858 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 14:45:28 -1000 Subject: [PATCH 067/526] [wifi] Use FixedVector for scan results to reduce flash usage (#11216) --- .../improv_serial/improv_serial_component.cpp | 2 +- esphome/components/wifi/wifi_component.cpp | 2 +- esphome/components/wifi/wifi_component.h | 12 ++++++++++-- esphome/components/wifi/wifi_component_esp8266.cpp | 8 ++++++++ esphome/components/wifi/wifi_component_esp_idf.cpp | 2 +- esphome/components/wifi/wifi_component_libretiny.cpp | 2 +- 6 files changed, 22 insertions(+), 6 deletions(-) diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 528a155a7f..28245dcfdf 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -218,7 +218,7 @@ bool ImprovSerialComponent::parse_improv_payload_(improv::ImprovCommand &command } case improv::GET_WIFI_NETWORKS: { std::vector networks; - auto results = wifi::global_wifi_component->get_scan_result(); + const auto &results = wifi::global_wifi_component->get_scan_result(); for (auto &scan : results) { if (scan.get_is_hidden()) continue; diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 2e0a0d95c2..5aa2a03a14 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -549,7 +549,7 @@ void WiFiComponent::start_scanning() { // Using insertion sort instead of std::stable_sort saves flash memory // by avoiding template instantiations (std::rotate, std::stable_sort, lambdas) // IMPORTANT: This sort is stable (preserves relative order of equal elements) -static void insertion_sort_scan_results(std::vector &results) { +template static void insertion_sort_scan_results(VectorType &results) { const size_t size = results.size(); for (size_t i = 1; i < size; i++) { // Make a copy to avoid issues with move semantics during comparison diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index cd899cec5b..9d32071b2b 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -121,6 +121,14 @@ struct EAPAuth { using bssid_t = std::array; +// Use std::vector for RP2040 since scan count is unknown (callback-based) +// Use FixedVector for other platforms where count is queried first +#ifdef USE_RP2040 +template using wifi_scan_vector_t = std::vector; +#else +template using wifi_scan_vector_t = FixedVector; +#endif + class WiFiAP { public: void set_ssid(const std::string &ssid); @@ -278,7 +286,7 @@ class WiFiComponent : public Component { const std::string &get_use_address() const; void set_use_address(const std::string &use_address); - const std::vector &get_scan_result() const { return scan_result_; } + const wifi_scan_vector_t &get_scan_result() const { return scan_result_; } network::IPAddress wifi_soft_ap_ip(); @@ -386,7 +394,7 @@ class WiFiComponent : public Component { std::string use_address_; std::vector sta_; std::vector sta_priorities_; - std::vector scan_result_; + wifi_scan_vector_t scan_result_; WiFiAP selected_ap_; WiFiAP ap_; optional output_power_; diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 3b3b4b139c..59909b2cb5 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -696,7 +696,15 @@ void WiFiComponent::wifi_scan_done_callback_(void *arg, STATUS status) { this->retry_connect(); return; } + + // Count the number of results first auto *head = reinterpret_cast(arg); + size_t count = 0; + for (bss_info *it = head; it != nullptr; it = STAILQ_NEXT(it, next)) { + count++; + } + + this->scan_result_.init(count); for (bss_info *it = head; it != nullptr; it = STAILQ_NEXT(it, next)) { WiFiScanResult res({it->bssid[0], it->bssid[1], it->bssid[2], it->bssid[3], it->bssid[4], it->bssid[5]}, std::string(reinterpret_cast(it->ssid), it->ssid_len), it->channel, it->rssi, diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index ccec800205..951f5803a6 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -784,7 +784,7 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { } records.resize(number); - scan_result_.reserve(number); + scan_result_.init(number); for (int i = 0; i < number; i++) { auto &record = records[i]; bssid_t bssid; diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index b15f710150..cb179d9022 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -411,7 +411,7 @@ void WiFiComponent::wifi_scan_done_callback_() { if (num < 0) return; - this->scan_result_.reserve(static_cast(num)); + this->scan_result_.init(static_cast(num)); for (int i = 0; i < num; i++) { String ssid = WiFi.SSID(i); wifi_auth_mode_t authmode = WiFi.encryptionType(i); From 85420b0606db4d585fcd303aac81c41b6c0048eb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 14:50:40 -1000 Subject: [PATCH 068/526] [web_server_idf] Use std::vector instead of std::set for SSE sessions (#11233) --- .../components/web_server_idf/web_server_idf.cpp | 13 +++++++------ esphome/components/web_server_idf/web_server_idf.h | 6 ++++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index d90efd18bc..c3ba7ddc2b 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -380,24 +380,25 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request) { if (this->on_connect_) { this->on_connect_(rsp); } - this->sessions_.insert(rsp); + this->sessions_.push_back(rsp); } void AsyncEventSource::loop() { // Clean up dead sessions safely // This follows the ESP-IDF pattern where free_ctx marks resources as dead // and the main loop handles the actual cleanup to avoid race conditions - auto it = this->sessions_.begin(); - while (it != this->sessions_.end()) { - auto *ses = *it; + for (size_t i = 0; i < this->sessions_.size();) { + auto *ses = this->sessions_[i]; // If the session has a dead socket (marked by destroy callback) if (ses->fd_.load() == 0) { ESP_LOGD(TAG, "Removing dead event source session"); - it = this->sessions_.erase(it); delete ses; // NOLINT(cppcoreguidelines-owning-memory) + // Remove by swapping with last element (O(1) removal, order doesn't matter for sessions) + this->sessions_[i] = this->sessions_.back(); + this->sessions_.pop_back(); } else { ses->loop(); - ++it; + ++i; } } } diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index bf93dcbd34..5ec6fec009 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -315,7 +314,10 @@ class AsyncEventSource : public AsyncWebHandler { protected: std::string url_; - std::set sessions_; + // Use vector instead of set: SSE sessions are typically 1-5 connections (browsers, dashboards). + // Linear search is faster than red-black tree overhead for this small dataset. + // Only operations needed: add session, remove session, iterate sessions - no need for sorted order. + std::vector sessions_; connect_handler_t on_connect_{}; esphome::web_server::WebServer *web_server_; }; From f0ac61f2478166100b931716f0aca29abf6daf06 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 16:00:22 -1000 Subject: [PATCH 069/526] [light] Use FixedVector for LightState effects list (#11232) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/light/light_state.cpp | 5 +++-- esphome/components/light/light_state.h | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index f18d5ba1de..1d139e49e7 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -177,9 +177,10 @@ void LightState::set_gamma_correct(float gamma_correct) { this->gamma_correct_ = void LightState::set_restore_mode(LightRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } void LightState::set_initial_state(const LightStateRTCState &initial_state) { this->initial_state_ = initial_state; } bool LightState::supports_effects() { return !this->effects_.empty(); } -const std::vector &LightState::get_effects() const { return this->effects_; } +const FixedVector &LightState::get_effects() const { return this->effects_; } void LightState::add_effects(const std::vector &effects) { - this->effects_.reserve(this->effects_.size() + effects.size()); + // Called once from Python codegen during setup with all effects from YAML config + this->effects_.init(effects.size()); for (auto *effect : effects) { this->effects_.push_back(effect); } diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index 1427c02c35..a07aeb6ae5 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -11,8 +11,9 @@ #include "light_traits.h" #include "light_transformer.h" -#include +#include "esphome/core/helpers.h" #include +#include namespace esphome { namespace light { @@ -159,7 +160,7 @@ class LightState : public EntityBase, public Component { bool supports_effects(); /// Get all effects for this light state. - const std::vector &get_effects() const; + const FixedVector &get_effects() const; /// Add effects for this light state. void add_effects(const std::vector &effects); @@ -260,7 +261,7 @@ class LightState : public EntityBase, public Component { /// The currently active transformer for this light (transition/flash). std::unique_ptr transformer_{nullptr}; /// List of effects for this light. - std::vector effects_; + FixedVector effects_; /// Object used to store the persisted values of the light. ESPPreferenceObject rtc_; /// Value for storing the index of the currently active effect. 0 if no effect is active From c983581b6cbcc87cd0b711db75a759f703f0b2f2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 16:10:04 -1000 Subject: [PATCH 070/526] [api] Convert HomeassistantActionRequest vectors to FixedVector for flash savings (#11229) --- esphome/components/api/api.proto | 6 ++-- esphome/components/api/api_pb2.h | 6 ++-- esphome/components/api/custom_api_device.h | 8 ++--- .../components/api/homeassistant_service.h | 31 ++++++++----------- .../number/homeassistant_number.cpp | 7 ++--- .../switch/homeassistant_switch.cpp | 4 +-- 6 files changed, 28 insertions(+), 34 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 9b714d00f1..34864c5ce8 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -776,9 +776,9 @@ message HomeassistantActionRequest { option (ifdef) = "USE_API_HOMEASSISTANT_SERVICES"; string service = 1; - repeated HomeassistantServiceMap data = 2; - repeated HomeassistantServiceMap data_template = 3; - repeated HomeassistantServiceMap variables = 4; + repeated HomeassistantServiceMap data = 2 [(fixed_vector) = true]; + repeated HomeassistantServiceMap data_template = 3 [(fixed_vector) = true]; + repeated HomeassistantServiceMap variables = 4 [(fixed_vector) = true]; bool is_event = 5; uint32 call_id = 6 [(field_ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES"]; bool wants_response = 7 [(field_ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON"]; diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 1798458393..7d6b31ca3c 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1110,9 +1110,9 @@ class HomeassistantActionRequest final : public ProtoMessage { #endif StringRef service_ref_{}; void set_service(const StringRef &ref) { this->service_ref_ = ref; } - std::vector data{}; - std::vector data_template{}; - std::vector variables{}; + FixedVector data{}; + FixedVector data_template{}; + FixedVector variables{}; bool is_event{false}; #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES uint32_t call_id{0}; diff --git a/esphome/components/api/custom_api_device.h b/esphome/components/api/custom_api_device.h index 0c6e49d6ca..711eba2444 100644 --- a/esphome/components/api/custom_api_device.h +++ b/esphome/components/api/custom_api_device.h @@ -201,9 +201,9 @@ class CustomAPIDevice { void call_homeassistant_service(const std::string &service_name, const std::map &data) { HomeassistantActionRequest resp; resp.set_service(StringRef(service_name)); + resp.data.init(data.size()); for (auto &it : data) { - resp.data.emplace_back(); - auto &kv = resp.data.back(); + auto &kv = resp.data.emplace_back(); kv.set_key(StringRef(it.first)); kv.value = it.second; } @@ -244,9 +244,9 @@ class CustomAPIDevice { HomeassistantActionRequest resp; resp.set_service(StringRef(service_name)); resp.is_event = true; + resp.data.init(data.size()); for (auto &it : data) { - resp.data.emplace_back(); - auto &kv = resp.data.back(); + auto &kv = resp.data.emplace_back(); kv.set_key(StringRef(it.first)); kv.value = it.second; } diff --git a/esphome/components/api/homeassistant_service.h b/esphome/components/api/homeassistant_service.h index 730024f7b7..46e89cb39f 100644 --- a/esphome/components/api/homeassistant_service.h +++ b/esphome/components/api/homeassistant_service.h @@ -127,24 +127,9 @@ template class HomeAssistantServiceCallAction : public Actionservice_.value(x...); resp.set_service(StringRef(service_value)); resp.is_event = this->flags_.is_event; - for (auto &it : this->data_) { - resp.data.emplace_back(); - auto &kv = resp.data.back(); - kv.set_key(StringRef(it.key)); - kv.value = it.value.value(x...); - } - for (auto &it : this->data_template_) { - resp.data_template.emplace_back(); - auto &kv = resp.data_template.back(); - kv.set_key(StringRef(it.key)); - kv.value = it.value.value(x...); - } - for (auto &it : this->variables_) { - resp.variables.emplace_back(); - auto &kv = resp.variables.back(); - kv.set_key(StringRef(it.key)); - kv.value = it.value.value(x...); - } + this->populate_service_map(resp.data, this->data_, x...); + this->populate_service_map(resp.data_template, this->data_template_, x...); + this->populate_service_map(resp.variables, this->variables_, x...); #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES if (this->flags_.wants_status) { @@ -189,6 +174,16 @@ template class HomeAssistantServiceCallAction : public Action + static void populate_service_map(VectorType &dest, SourceType &source, Ts... x) { + dest.init(source.size()); + for (auto &it : source) { + auto &kv = dest.emplace_back(); + kv.set_key(StringRef(it.key)); + kv.value = it.value.value(x...); + } + } + APIServer *parent_; TemplatableStringValue service_{}; std::vector> data_; diff --git a/esphome/components/homeassistant/number/homeassistant_number.cpp b/esphome/components/homeassistant/number/homeassistant_number.cpp index c9fb006568..9963f3431d 100644 --- a/esphome/components/homeassistant/number/homeassistant_number.cpp +++ b/esphome/components/homeassistant/number/homeassistant_number.cpp @@ -90,13 +90,12 @@ void HomeassistantNumber::control(float value) { api::HomeassistantActionRequest resp; resp.set_service(SERVICE_NAME); - resp.data.emplace_back(); - auto &entity_id = resp.data.back(); + resp.data.init(2); + auto &entity_id = resp.data.emplace_back(); entity_id.set_key(ENTITY_ID_KEY); entity_id.value = this->entity_id_; - resp.data.emplace_back(); - auto &entity_value = resp.data.back(); + auto &entity_value = resp.data.emplace_back(); entity_value.set_key(VALUE_KEY); entity_value.value = to_string(value); diff --git a/esphome/components/homeassistant/switch/homeassistant_switch.cpp b/esphome/components/homeassistant/switch/homeassistant_switch.cpp index 8feec26fe6..27d3705fc2 100644 --- a/esphome/components/homeassistant/switch/homeassistant_switch.cpp +++ b/esphome/components/homeassistant/switch/homeassistant_switch.cpp @@ -51,8 +51,8 @@ void HomeassistantSwitch::write_state(bool state) { resp.set_service(SERVICE_OFF); } - resp.data.emplace_back(); - auto &entity_id_kv = resp.data.back(); + resp.data.init(1); + auto &entity_id_kv = resp.data.emplace_back(); entity_id_kv.set_key(ENTITY_ID_KEY); entity_id_kv.value = this->entity_id_; From 7a82379c88479ba86c19ae2328cbfe3601271a0c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 16:16:59 -1000 Subject: [PATCH 071/526] [mdns] Use FixedVector for txt_records to reduce flash usage (#11228) --- esphome/components/mdns/mdns_component.cpp | 9 ++------- esphome/components/mdns/mdns_component.h | 2 +- esphome/components/mdns/mdns_esp32.cpp | 5 +++++ esphome/components/mdns/mdns_esp8266.cpp | 5 +++++ esphome/components/mdns/mdns_libretiny.cpp | 5 +++++ esphome/components/mdns/mdns_rp2040.cpp | 5 +++++ 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index fea3ced99f..d476136554 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -83,7 +83,7 @@ void MDNSComponent::compile_records_(StaticVectorservices_ = services; + fallback_service.txt_records = {{MDNS_STR(TXT_VERSION), MDNS_STR(VALUE_VERSION)}}; #endif } diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index 62476e9504..35371fd739 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -38,7 +38,7 @@ struct MDNSService { // as defined in RFC6763 Section 7, like "_tcp" or "_udp" const MDNSString *proto; TemplatableValue port; - std::vector txt_records; + FixedVector txt_records; }; class MDNSComponent : public Component { diff --git a/esphome/components/mdns/mdns_esp32.cpp b/esphome/components/mdns/mdns_esp32.cpp index da47be7dbc..f2cb2d3ef5 100644 --- a/esphome/components/mdns/mdns_esp32.cpp +++ b/esphome/components/mdns/mdns_esp32.cpp @@ -12,8 +12,13 @@ namespace mdns { static const char *const TAG = "mdns"; void MDNSComponent::setup() { +#ifdef USE_MDNS_STORE_SERVICES + this->compile_records_(this->services_); + const auto &services = this->services_; +#else StaticVector services; this->compile_records_(services); +#endif esp_err_t err = mdns_init(); if (err != ESP_OK) { diff --git a/esphome/components/mdns/mdns_esp8266.cpp b/esphome/components/mdns/mdns_esp8266.cpp index 06503742db..25a3defa7b 100644 --- a/esphome/components/mdns/mdns_esp8266.cpp +++ b/esphome/components/mdns/mdns_esp8266.cpp @@ -12,8 +12,13 @@ namespace esphome { namespace mdns { void MDNSComponent::setup() { +#ifdef USE_MDNS_STORE_SERVICES + this->compile_records_(this->services_); + const auto &services = this->services_; +#else StaticVector services; this->compile_records_(services); +#endif MDNS.begin(this->hostname_.c_str()); diff --git a/esphome/components/mdns/mdns_libretiny.cpp b/esphome/components/mdns/mdns_libretiny.cpp index a959482ff6..a3e317a2bf 100644 --- a/esphome/components/mdns/mdns_libretiny.cpp +++ b/esphome/components/mdns/mdns_libretiny.cpp @@ -12,8 +12,13 @@ namespace esphome { namespace mdns { void MDNSComponent::setup() { +#ifdef USE_MDNS_STORE_SERVICES + this->compile_records_(this->services_); + const auto &services = this->services_; +#else StaticVector services; this->compile_records_(services); +#endif MDNS.begin(this->hostname_.c_str()); diff --git a/esphome/components/mdns/mdns_rp2040.cpp b/esphome/components/mdns/mdns_rp2040.cpp index 9dfb05bda9..791fa3934d 100644 --- a/esphome/components/mdns/mdns_rp2040.cpp +++ b/esphome/components/mdns/mdns_rp2040.cpp @@ -12,8 +12,13 @@ namespace esphome { namespace mdns { void MDNSComponent::setup() { +#ifdef USE_MDNS_STORE_SERVICES + this->compile_records_(this->services_); + const auto &services = this->services_; +#else StaticVector services; this->compile_records_(services); +#endif MDNS.begin(this->hostname_.c_str()); From 6e2088f836165da3b568697ed8a075121ae70586 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 03:12:34 +0000 Subject: [PATCH 072/526] Bump aioesphomeapi from 41.17.0 to 41.18.0 (#11247) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1abfed0324..88ecb1c20e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==41.17.0 +aioesphomeapi==41.18.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import From fedfda6c29a7e48c143a639c129e4de7a640b645 Mon Sep 17 00:00:00 2001 From: Stuart Parmenter Date: Tue, 14 Oct 2025 20:57:47 -0700 Subject: [PATCH 073/526] [core] Fix regression from #10654 (#11248) --- script/ci-custom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/ci-custom.py b/script/ci-custom.py index bc1ebda93b..6b01623d92 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -501,7 +501,7 @@ def lint_constants_usage(): continue errs.append( f"Constant {highlight(constant)} is defined in {len(uses)} files. Please move all definitions of the " - f"constant to const.py (Uses: {', '.join(uses)}) in a separate PR. " + f"constant to const.py (Uses: {', '.join(str(u) for u in uses)}) in a separate PR. " "See https://developers.esphome.io/contributing/code/#python" ) return errs From 6d5e41ef7f50618a3a2dc45abe56d575344aba83 Mon Sep 17 00:00:00 2001 From: Stuart Parmenter Date: Tue, 14 Oct 2025 21:29:41 -0700 Subject: [PATCH 074/526] [const] Add CONF_ROWS (#11249) --- esphome/components/lvgl/defines.py | 1 - esphome/components/lvgl/widgets/buttonmatrix.py | 3 +-- esphome/components/matrix_keypad/__init__.py | 3 +-- esphome/const.py | 1 + 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index baee403b57..6464824c64 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -486,7 +486,6 @@ CONF_RESUME_ON_INPUT = "resume_on_input" CONF_RIGHT_BUTTON = "right_button" CONF_ROLLOVER = "rollover" CONF_ROOT_BACK_BTN = "root_back_btn" -CONF_ROWS = "rows" CONF_SCALE_LINES = "scale_lines" CONF_SCROLLBAR_MODE = "scrollbar_mode" CONF_SELECTED_INDEX = "selected_index" diff --git a/esphome/components/lvgl/widgets/buttonmatrix.py b/esphome/components/lvgl/widgets/buttonmatrix.py index c6b6d2440f..baeb1c8e3e 100644 --- a/esphome/components/lvgl/widgets/buttonmatrix.py +++ b/esphome/components/lvgl/widgets/buttonmatrix.py @@ -2,7 +2,7 @@ from esphome import automation import esphome.codegen as cg from esphome.components.key_provider import KeyProvider import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_ITEMS, CONF_TEXT, CONF_WIDTH +from esphome.const import CONF_ID, CONF_ITEMS, CONF_ROWS, CONF_TEXT, CONF_WIDTH from esphome.cpp_generator import MockObj from ..automation import action_to_code @@ -15,7 +15,6 @@ from ..defines import ( CONF_ONE_CHECKED, CONF_PAD_COLUMN, CONF_PAD_ROW, - CONF_ROWS, CONF_SELECTED, ) from ..helpers import lvgl_components_required diff --git a/esphome/components/matrix_keypad/__init__.py b/esphome/components/matrix_keypad/__init__.py index f7a1d622a1..2e123323a0 100644 --- a/esphome/components/matrix_keypad/__init__.py +++ b/esphome/components/matrix_keypad/__init__.py @@ -2,7 +2,7 @@ from esphome import automation, pins import esphome.codegen as cg from esphome.components import key_provider import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_ON_KEY, CONF_PIN, CONF_TRIGGER_ID +from esphome.const import CONF_ID, CONF_ON_KEY, CONF_PIN, CONF_ROWS, CONF_TRIGGER_ID CODEOWNERS = ["@ssieb"] @@ -19,7 +19,6 @@ MatrixKeyTrigger = matrix_keypad_ns.class_( ) CONF_KEYPAD_ID = "keypad_id" -CONF_ROWS = "rows" CONF_COLUMNS = "columns" CONF_KEYS = "keys" CONF_DEBOUNCE_TIME = "debounce_time" diff --git a/esphome/const.py b/esphome/const.py index 086b5b4ce3..d62dc617d1 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -836,6 +836,7 @@ CONF_RMT_CHANNEL = "rmt_channel" CONF_RMT_SYMBOLS = "rmt_symbols" CONF_ROTATION = "rotation" CONF_ROW = "row" +CONF_ROWS = "rows" CONF_RS_PIN = "rs_pin" CONF_RTD_NOMINAL_RESISTANCE = "rtd_nominal_resistance" CONF_RTD_WIRES = "rtd_wires" From 1b0ca3360e881df7635376f68d21a0bc6bec9b0c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 19:49:14 -1000 Subject: [PATCH 075/526] [ci] Group all PR builds, isolate direct changes for full validation on dev (#11193) --- .github/workflows/ci.yml | 71 +++++++++++------------------ script/determine-jobs.py | 52 +++++++++++++++++---- script/list-components.py | 45 +++++++++++++++--- script/split_components_for_ci.py | 55 ++++++++++++++++++++-- script/test_build_components.py | 62 +++++++++++++++++++++---- tests/script/test_determine_jobs.py | 23 +++++++--- 6 files changed, 230 insertions(+), 78 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0363b5afdf..2b0449474b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -178,6 +178,7 @@ jobs: python-linters: ${{ steps.determine.outputs.python-linters }} changed-components: ${{ steps.determine.outputs.changed-components }} changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }} + directly-changed-components-with-tests: ${{ steps.determine.outputs.directly-changed-components-with-tests }} component-test-count: ${{ steps.determine.outputs.component-test-count }} steps: - name: Check out code from GitHub @@ -206,6 +207,7 @@ jobs: echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT echo "changed-components=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT echo "changed-components-with-tests=$(echo "$output" | jq -c '.changed_components_with_tests')" >> $GITHUB_OUTPUT + echo "directly-changed-components-with-tests=$(echo "$output" | jq -c '.directly_changed_components_with_tests')" >> $GITHUB_OUTPUT echo "component-test-count=$(echo "$output" | jq -r '.component_test_count')" >> $GITHUB_OUTPUT integration-tests: @@ -358,48 +360,13 @@ jobs: # yamllint disable-line rule:line-length if: always() - test-build-components: - name: Component test ${{ matrix.file }} - runs-on: ubuntu-24.04 - needs: - - common - - determine-jobs - if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0 && fromJSON(needs.determine-jobs.outputs.component-test-count) < 100 - strategy: - fail-fast: false - max-parallel: 2 - matrix: - file: ${{ fromJson(needs.determine-jobs.outputs.changed-components-with-tests) }} - steps: - - name: Cache apt packages - uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.5.3 - with: - packages: libsdl2-dev - version: 1.0 - - - name: Check out code from GitHub - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Restore Python - uses: ./.github/actions/restore-python - with: - python-version: ${{ env.DEFAULT_PYTHON }} - cache-key: ${{ needs.common.outputs.cache-key }} - - name: Validate config for ${{ matrix.file }} - run: | - . venv/bin/activate - python3 script/test_build_components.py -e config -c ${{ matrix.file }} - - name: Compile config for ${{ matrix.file }} - run: | - . venv/bin/activate - python3 script/test_build_components.py -e compile -c ${{ matrix.file }} - test-build-components-splitter: name: Split components for intelligent grouping (40 weighted per batch) runs-on: ubuntu-24.04 needs: - common - determine-jobs - if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) >= 100 + if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0 outputs: matrix: ${{ steps.split.outputs.components }} steps: @@ -417,9 +384,10 @@ jobs: # Use intelligent splitter that groups components with same bus configs components='${{ needs.determine-jobs.outputs.changed-components-with-tests }}' + directly_changed='${{ needs.determine-jobs.outputs.directly-changed-components-with-tests }}' echo "Splitting components intelligently..." - output=$(python3 script/split_components_for_ci.py --components "$components" --batch-size 40 --output github) + output=$(python3 script/split_components_for_ci.py --components "$components" --directly-changed "$directly_changed" --batch-size 40 --output github) echo "$output" >> $GITHUB_OUTPUT @@ -430,7 +398,7 @@ jobs: - common - determine-jobs - test-build-components-splitter - if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) >= 100 + if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0 strategy: fail-fast: false max-parallel: ${{ (github.base_ref == 'beta' || github.base_ref == 'release') && 8 || 4 }} @@ -477,18 +445,34 @@ jobs: # Convert space-separated components to comma-separated for Python script components_csv=$(echo "${{ matrix.components }}" | tr ' ' ',') - echo "Testing components: $components_csv" + # Only isolate directly changed components when targeting dev branch + # For beta/release branches, group everything for faster CI + # + # WHY ISOLATE DIRECTLY CHANGED COMPONENTS? + # - Isolated tests run WITHOUT --testing-mode, enabling full validation + # - This catches pin conflicts and other issues in directly changed code + # - Grouped tests use --testing-mode to allow config merging (disables some checks) + # - Dependencies are safe to group since they weren't modified in this PR + if [ "${{ github.base_ref }}" = "beta" ] || [ "${{ github.base_ref }}" = "release" ]; then + directly_changed_csv="" + echo "Testing components: $components_csv" + echo "Target branch: ${{ github.base_ref }} - grouping all components" + else + directly_changed_csv=$(echo '${{ needs.determine-jobs.outputs.directly-changed-components-with-tests }}' | jq -r 'join(",")') + echo "Testing components: $components_csv" + echo "Target branch: ${{ github.base_ref }} - isolating directly changed components: $directly_changed_csv" + fi echo "" - # Run config validation with grouping - python3 script/test_build_components.py -e config -c "$components_csv" -f + # Run config validation with grouping and isolation + python3 script/test_build_components.py -e config -c "$components_csv" -f --isolate "$directly_changed_csv" echo "" echo "Config validation passed! Starting compilation..." echo "" - # Run compilation with grouping - python3 script/test_build_components.py -e compile -c "$components_csv" -f + # Run compilation with grouping and isolation + python3 script/test_build_components.py -e compile -c "$components_csv" -f --isolate "$directly_changed_csv" pre-commit-ci-lite: name: pre-commit.ci lite @@ -521,7 +505,6 @@ jobs: - integration-tests - clang-tidy - determine-jobs - - test-build-components - test-build-components-splitter - test-build-components-split - pre-commit-ci-lite diff --git a/script/determine-jobs.py b/script/determine-jobs.py index a078fd8f9b..b000ecee3b 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -31,6 +31,7 @@ Options: from __future__ import annotations import argparse +from functools import cache import json import os from pathlib import Path @@ -45,7 +46,6 @@ from helpers import ( changed_files, get_all_dependencies, get_components_from_integration_fixtures, - parse_list_components_output, root_path, ) @@ -212,6 +212,24 @@ def _any_changed_file_endswith(branch: str | None, extensions: tuple[str, ...]) return any(file.endswith(extensions) for file in changed_files(branch)) +@cache +def _component_has_tests(component: str) -> bool: + """Check if a component has test files. + + Cached to avoid repeated filesystem operations for the same component. + + Args: + component: Component name to check + + Returns: + True if the component has test YAML files + """ + tests_dir = Path(root_path) / "tests" / "components" / component + if not tests_dir.exists(): + return False + return any(tests_dir.glob("test.*.yaml")) + + def main() -> None: """Main function that determines which CI jobs to run.""" parser = argparse.ArgumentParser( @@ -228,23 +246,37 @@ def main() -> None: run_clang_format = should_run_clang_format(args.branch) run_python_linters = should_run_python_linters(args.branch) - # Get changed components using list-components.py for exact compatibility + # Get both directly changed and all changed components (with dependencies) in one call script_path = Path(__file__).parent / "list-components.py" - cmd = [sys.executable, str(script_path), "--changed"] + cmd = [sys.executable, str(script_path), "--changed-with-deps"] if args.branch: cmd.extend(["-b", args.branch]) result = subprocess.run(cmd, capture_output=True, text=True, check=True) - changed_components = parse_list_components_output(result.stdout) + component_data = json.loads(result.stdout) + directly_changed_components = component_data["directly_changed"] + changed_components = component_data["all_changed"] # Filter to only components that have test files # Components without tests shouldn't generate CI test jobs - tests_dir = Path(root_path) / "tests" / "components" changed_components_with_tests = [ + component for component in changed_components if _component_has_tests(component) + ] + + # Get directly changed components with tests (for isolated testing) + # These will be tested WITHOUT --testing-mode in CI to enable full validation + # (pin conflicts, etc.) since they contain the actual changes being reviewed + directly_changed_with_tests = [ component - for component in changed_components - if (component_test_dir := tests_dir / component).exists() - and any(component_test_dir.glob("test.*.yaml")) + for component in directly_changed_components + if _component_has_tests(component) + ] + + # Get dependency-only components (for grouped testing) + dependency_only_components = [ + component + for component in changed_components_with_tests + if component not in directly_changed_components ] # Build output @@ -255,7 +287,11 @@ def main() -> None: "python_linters": run_python_linters, "changed_components": changed_components, "changed_components_with_tests": changed_components_with_tests, + "directly_changed_components_with_tests": directly_changed_with_tests, + "dependency_only_components_with_tests": dependency_only_components, "component_test_count": len(changed_components_with_tests), + "directly_changed_count": len(directly_changed_with_tests), + "dependency_only_count": len(dependency_only_components), } # Output as JSON diff --git a/script/list-components.py b/script/list-components.py index 9ab1cdd852..dffff6801a 100755 --- a/script/list-components.py +++ b/script/list-components.py @@ -185,18 +185,32 @@ def main(): "-c", "--changed", action="store_true", - help="List all components required for testing based on changes", + help="List all components required for testing based on changes (includes dependencies)", + ) + parser.add_argument( + "--changed-direct", + action="store_true", + help="List only directly changed components (without dependencies)", + ) + parser.add_argument( + "--changed-with-deps", + action="store_true", + help="Output JSON with both directly changed and all changed components", ) parser.add_argument( "-b", "--branch", help="Branch to compare changed files against" ) args = parser.parse_args() - if args.branch and not args.changed: - parser.error("--branch requires --changed") + if args.branch and not ( + args.changed or args.changed_direct or args.changed_with_deps + ): + parser.error( + "--branch requires --changed, --changed-direct, or --changed-with-deps" + ) - if args.changed: - # When --changed is passed, only get the changed files + if args.changed or args.changed_direct or args.changed_with_deps: + # When --changed* is passed, only get the changed files changed = changed_files(args.branch) # If any base test file(s) changed, there's no need to filter out components @@ -210,8 +224,25 @@ def main(): # Get all component files files = get_all_component_files() - for c in get_components(files, args.changed): - print(c) + if args.changed_with_deps: + # Return JSON with both directly changed and all changed components + import json + + directly_changed = get_components(files, False) + all_changed = get_components(files, True) + output = { + "directly_changed": directly_changed, + "all_changed": all_changed, + } + print(json.dumps(output)) + elif args.changed_direct: + # Return only directly changed components (without dependencies) + for c in get_components(files, False): + print(c) + else: + # Return all changed components (with dependencies) - default behavior + for c in get_components(files, args.changed): + print(c) if __name__ == "__main__": diff --git a/script/split_components_for_ci.py b/script/split_components_for_ci.py index ad2261499c..9730db4988 100755 --- a/script/split_components_for_ci.py +++ b/script/split_components_for_ci.py @@ -56,6 +56,7 @@ def create_intelligent_batches( components: list[str], tests_dir: Path, batch_size: int = 40, + directly_changed: set[str] | None = None, ) -> list[list[str]]: """Create batches optimized for component grouping. @@ -63,6 +64,7 @@ def create_intelligent_batches( components: List of component names to batch tests_dir: Path to tests/components directory batch_size: Target size for each batch + directly_changed: Set of directly changed components (for logging only) Returns: List of component batches (lists of component names) @@ -94,10 +96,17 @@ def create_intelligent_batches( for component in components_with_tests: # Components that can't be grouped get unique signatures - # This includes both manually curated ISOLATED_COMPONENTS and - # automatically detected non_groupable components + # This includes: + # - Manually curated ISOLATED_COMPONENTS + # - Automatically detected non_groupable components + # - Directly changed components (passed via --isolate in CI) # These can share a batch/runner but won't be grouped/merged - if component in ISOLATED_COMPONENTS or component in non_groupable: + is_isolated = ( + component in ISOLATED_COMPONENTS + or component in non_groupable + or (directly_changed and component in directly_changed) + ) + if is_isolated: signature_groups[f"isolated_{component}"].append(component) continue @@ -187,6 +196,10 @@ def main() -> int: default=Path("tests/components"), help="Path to tests/components directory", ) + parser.add_argument( + "--directly-changed", + help="JSON array of directly changed component names (for logging only)", + ) parser.add_argument( "--output", "-o", @@ -208,11 +221,21 @@ def main() -> int: print("Components must be a JSON array", file=sys.stderr) return 1 + # Parse directly changed components list from JSON (if provided) + directly_changed = None + if args.directly_changed: + try: + directly_changed = set(json.loads(args.directly_changed)) + except json.JSONDecodeError as e: + print(f"Error parsing directly-changed JSON: {e}", file=sys.stderr) + return 1 + # Create intelligent batches batches = create_intelligent_batches( components=components, tests_dir=args.tests_dir, batch_size=args.batch_size, + directly_changed=directly_changed, ) # Convert batches to space-separated strings for CI @@ -238,13 +261,37 @@ def main() -> int: isolated_count = sum( 1 for comp in all_batched_components - if comp in ISOLATED_COMPONENTS or comp in non_groupable + if comp in ISOLATED_COMPONENTS + or comp in non_groupable + or (directly_changed and comp in directly_changed) ) groupable_count = actual_components - isolated_count print("\n=== Intelligent Batch Summary ===", file=sys.stderr) print(f"Total components requested: {len(components)}", file=sys.stderr) print(f"Components with test files: {actual_components}", file=sys.stderr) + + # Show breakdown of directly changed vs dependencies + if directly_changed: + direct_count = sum( + 1 for comp in all_batched_components if comp in directly_changed + ) + dep_count = actual_components - direct_count + direct_comps = [ + comp for comp in all_batched_components if comp in directly_changed + ] + dep_comps = [ + comp for comp in all_batched_components if comp not in directly_changed + ] + print( + f" - Direct changes: {direct_count} ({', '.join(sorted(direct_comps))})", + file=sys.stderr, + ) + print( + f" - Dependencies: {dep_count} ({', '.join(sorted(dep_comps))})", + file=sys.stderr, + ) + print(f" - Groupable (weight=1): {groupable_count}", file=sys.stderr) print(f" - Isolated (weight=10): {isolated_count}", file=sys.stderr) if actual_components < len(components): diff --git a/script/test_build_components.py b/script/test_build_components.py index e27bd695c1..14fc10977c 100755 --- a/script/test_build_components.py +++ b/script/test_build_components.py @@ -365,6 +365,7 @@ def run_grouped_component_tests( build_dir: Path, esphome_command: str, continue_on_fail: bool, + additional_isolated: set[str] | None = None, ) -> tuple[set[tuple[str, str]], list[str], list[str], dict[str, str]]: """Run grouped component tests. @@ -376,6 +377,7 @@ def run_grouped_component_tests( build_dir: Path to build directory esphome_command: ESPHome command (config/compile) continue_on_fail: Whether to continue on failure + additional_isolated: Additional components to treat as isolated (not grouped) Returns: Tuple of (tested_components, passed_tests, failed_tests, failed_commands) @@ -397,6 +399,17 @@ def run_grouped_component_tests( # Track why components can't be grouped (for detailed output) non_groupable_reasons = {} + # Merge additional isolated components with predefined ones + # ISOLATED COMPONENTS are tested individually WITHOUT --testing-mode + # This is critical because: + # - Grouped tests use --testing-mode which disables pin conflict checks and other validation + # - These checks are disabled to allow config merging (multiple components in one build) + # - For directly changed components (via --isolate), we need full validation to catch issues + # - Dependencies are safe to group since they weren't modified in the PR + all_isolated = set(ISOLATED_COMPONENTS.keys()) + if additional_isolated: + all_isolated.update(additional_isolated) + # Group by (platform, bus_signature) for component, platforms in component_buses.items(): if component not in all_tests: @@ -404,7 +417,7 @@ def run_grouped_component_tests( # Skip components that must be tested in isolation # These are shown separately and should not be in non_groupable_reasons - if component in ISOLATED_COMPONENTS: + if component in all_isolated: continue # Skip base bus components (these test the bus platforms themselves) @@ -453,15 +466,28 @@ def run_grouped_component_tests( print("\nGrouping Plan:") print("-" * 80) - # Show isolated components (must test individually due to known issues) - isolated_in_tests = [c for c in ISOLATED_COMPONENTS if c in all_tests] + # Show isolated components (must test individually due to known issues or direct changes) + isolated_in_tests = [c for c in all_isolated if c in all_tests] if isolated_in_tests: - print( - f"\n⚠ {len(isolated_in_tests)} components must be tested in isolation (known build issues):" - ) - for comp in sorted(isolated_in_tests): - reason = ISOLATED_COMPONENTS[comp] - print(f" - {comp}: {reason}") + predefined_isolated = [c for c in isolated_in_tests if c in ISOLATED_COMPONENTS] + additional_in_tests = [ + c for c in isolated_in_tests if c in (additional_isolated or set()) + ] + + if predefined_isolated: + print( + f"\n⚠ {len(predefined_isolated)} components must be tested in isolation (known build issues):" + ) + for comp in sorted(predefined_isolated): + reason = ISOLATED_COMPONENTS[comp] + print(f" - {comp}: {reason}") + + if additional_in_tests: + print( + f"\n✓ {len(additional_in_tests)} components tested in isolation (directly changed in PR):" + ) + for comp in sorted(additional_in_tests): + print(f" - {comp}") # Show base bus components (test the bus platform implementations) base_bus_in_tests = [c for c in BASE_BUS_COMPONENTS if c in all_tests] @@ -733,6 +759,7 @@ def test_components( esphome_command: str, continue_on_fail: bool, enable_grouping: bool = True, + isolated_components: set[str] | None = None, ) -> int: """Test components with optional intelligent grouping. @@ -742,6 +769,10 @@ def test_components( esphome_command: ESPHome command (config/compile) continue_on_fail: Whether to continue on failure enable_grouping: Whether to enable component grouping + isolated_components: Set of component names to test in isolation (not grouped). + These are tested WITHOUT --testing-mode to enable full validation + (pin conflicts, etc). This is used in CI for directly changed components + to catch issues that would be missed with --testing-mode. Returns: Exit code (0 for success, 1 for failure) @@ -788,6 +819,7 @@ def test_components( build_dir=build_dir, esphome_command=esphome_command, continue_on_fail=continue_on_fail, + additional_isolated=isolated_components, ) # Then run individual tests for components not in groups @@ -912,18 +944,30 @@ def main() -> int: action="store_true", help="Disable component grouping (test each component individually)", ) + parser.add_argument( + "--isolate", + help="Comma-separated list of components to test in isolation (not grouped with others). " + "These are tested WITHOUT --testing-mode to enable full validation. " + "Used in CI for directly changed components to catch pin conflicts and other issues.", + ) args = parser.parse_args() # Parse component patterns component_patterns = [p.strip() for p in args.components.split(",")] + # Parse isolated components + isolated_components = None + if args.isolate: + isolated_components = {c.strip() for c in args.isolate.split(",") if c.strip()} + return test_components( component_patterns=component_patterns, platform_filter=args.target, esphome_command=args.esphome_command, continue_on_fail=args.continue_on_fail, enable_grouping=not args.no_grouping, + isolated_components=isolated_components, ) diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 5d8746f434..0559d116be 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -73,9 +73,11 @@ def test_main_all_tests_should_run( mock_should_run_clang_format.return_value = True mock_should_run_python_linters.return_value = True - # Mock list-components.py output + # Mock list-components.py output (now returns JSON with --changed-with-deps) mock_result = Mock() - mock_result.stdout = "wifi\napi\nsensor\n" + mock_result.stdout = json.dumps( + {"directly_changed": ["wifi", "api"], "all_changed": ["wifi", "api", "sensor"]} + ) mock_subprocess_run.return_value = mock_result # Run main function with mocked argv @@ -116,7 +118,7 @@ def test_main_no_tests_should_run( # Mock empty list-components.py output mock_result = Mock() - mock_result.stdout = "" + mock_result.stdout = json.dumps({"directly_changed": [], "all_changed": []}) mock_subprocess_run.return_value = mock_result # Run main function with mocked argv @@ -177,7 +179,9 @@ def test_main_with_branch_argument( # Mock list-components.py output mock_result = Mock() - mock_result.stdout = "mqtt\n" + mock_result.stdout = json.dumps( + {"directly_changed": ["mqtt"], "all_changed": ["mqtt"]} + ) mock_subprocess_run.return_value = mock_result with patch("sys.argv", ["script.py", "-b", "main"]): @@ -192,7 +196,7 @@ def test_main_with_branch_argument( # Check that list-components.py was called with branch mock_subprocess_run.assert_called_once() call_args = mock_subprocess_run.call_args[0][0] - assert "--changed" in call_args + assert "--changed-with-deps" in call_args assert "-b" in call_args assert "main" in call_args @@ -411,7 +415,12 @@ def test_main_filters_components_without_tests( # Mock list-components.py output with 3 components # wifi: has tests, sensor: has tests, airthings_ble: no tests mock_result = Mock() - mock_result.stdout = "wifi\nsensor\nairthings_ble\n" + mock_result.stdout = json.dumps( + { + "directly_changed": ["wifi", "sensor"], + "all_changed": ["wifi", "sensor", "airthings_ble"], + } + ) mock_subprocess_run.return_value = mock_result # Create test directory structure @@ -436,6 +445,8 @@ def test_main_filters_components_without_tests( patch.object(determine_jobs, "root_path", str(tmp_path)), patch("sys.argv", ["determine-jobs.py"]), ): + # Clear the cache since we're mocking root_path + determine_jobs._component_has_tests.cache_clear() determine_jobs.main() # Check output From 7f8ca5ddef7d41392ef544568bb65296741477f9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Oct 2025 19:49:48 -1000 Subject: [PATCH 076/526] [ci] Add Python 3.14 testing and streamline version matrix (#11238) --- .github/workflows/ci.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b0449474b..163e9ab9ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,8 +114,7 @@ jobs: matrix: python-version: - "3.11" - - "3.12" - - "3.13" + - "3.14" os: - ubuntu-latest - macOS-latest @@ -124,13 +123,9 @@ jobs: # Minimize CI resource usage # by only running the Python version # version used for docker images on Windows and macOS - - python-version: "3.13" + - python-version: "3.14" os: windows-latest - - python-version: "3.12" - os: windows-latest - - python-version: "3.13" - os: macOS-latest - - python-version: "3.12" + - python-version: "3.14" os: macOS-latest runs-on: ${{ matrix.os }} needs: From ded98ff705b8e13a2efaf0a3cc0e1444e330335b Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 15 Oct 2025 07:48:05 -0400 Subject: [PATCH 077/526] [esp32_hosted] Bump hosted components (#11170) Co-authored-by: J. Nick Koston --- esphome/components/esp32_hosted/__init__.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/esphome/components/esp32_hosted/__init__.py b/esphome/components/esp32_hosted/__init__.py index 9cea02c322..7e9f1b05b5 100644 --- a/esphome/components/esp32_hosted/__init__.py +++ b/esphome/components/esp32_hosted/__init__.py @@ -92,9 +92,14 @@ async def to_code(config): framework_ver: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] os.environ["ESP_IDF_VERSION"] = f"{framework_ver.major}.{framework_ver.minor}" - esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="0.10.2") - esp32.add_idf_component(name="espressif/eppp_link", ref="0.2.0") - esp32.add_idf_component(name="espressif/esp_hosted", ref="2.0.11") + if framework_ver >= cv.Version(5, 5, 0): + esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="1.1.5") + esp32.add_idf_component(name="espressif/eppp_link", ref="1.1.3") + esp32.add_idf_component(name="espressif/esp_hosted", ref="2.5.11") + else: + esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="0.13.0") + esp32.add_idf_component(name="espressif/eppp_link", ref="0.2.0") + esp32.add_idf_component(name="espressif/esp_hosted", ref="2.0.11") esp32.add_extra_script( "post", "esp32_hosted.py", From 47817485e768b97d241e92860f1bc7633c69f026 Mon Sep 17 00:00:00 2001 From: Thane Gill Date: Wed, 15 Oct 2025 04:48:26 -0700 Subject: [PATCH 078/526] [esp32] Remove kconfiglib from requirements.txt (#11210) --- requirements.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 88ecb1c20e..b6e4a189d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,10 +23,6 @@ cairosvg==2.8.2 freetype-py==2.5.1 jinja2==3.1.6 -# esp-idf requires this, but doesn't bundle it by default -# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 -kconfiglib==13.7.1 - # esp-idf >= 5.0 requires this pyparsing >= 3.0 From 42bf5840c9fabe93e58f002018040daff78fa2fb Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 15 Oct 2025 07:49:28 -0400 Subject: [PATCH 079/526] [esp32_rmt_led_strip] Don't send reset if duration is zero (#11235) --- esphome/components/esp32_rmt_led_strip/led_strip.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index fa43aa5950..2c7963b366 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp +++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp @@ -42,6 +42,11 @@ static size_t IRAM_ATTR HOT encoder_callback(const void *data, size_t size, size symbols[i] = params->bit0; } } +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) + if ((index + 1) >= size && params->reset.duration0 == 0 && params->reset.duration1 == 0) { + *done = true; + } +#endif return RMT_SYMBOLS_PER_BYTE; } From 90e8c12df1348af24882bea4c6608712f1b16991 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:45:06 -0400 Subject: [PATCH 080/526] [ci] Isolate openthread (#11259) --- script/analyze_component_buses.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/analyze_component_buses.py b/script/analyze_component_buses.py index 39e1e66c75..24854178a0 100755 --- a/script/analyze_component_buses.py +++ b/script/analyze_component_buses.py @@ -75,6 +75,8 @@ ISOLATED_COMPONENTS = { "ethernet": "Defines ethernet: which conflicts with wifi: used by most components", "ethernet_info": "Related to ethernet component which conflicts with wifi", "lvgl": "Defines multiple SDL displays on host platform that conflict when merged with other display configs", + "openthread": "Conflicts with wifi: used by most components", + "openthread_info": "Conflicts with wifi: used by most components", "matrix_keypad": "Needs isolation due to keypad", "mcp4725": "no YAML config to specify i2c bus id", "mcp47a1": "no YAML config to specify i2c bus id", From cf02a082090af739522405ff6941cab7e23fbc69 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 15 Oct 2025 14:45:33 -0400 Subject: [PATCH 081/526] [esp32] Bump IDF version to 5.5.1 and Arduino version to 3.3.2 (#9839) --- .clang-tidy.hash | 2 +- esphome/components/esp32/__init__.py | 6 ++-- esphome/components/esp32/boards.py | 28 +++++++++++++++++++ .../components/socket/bsd_sockets_impl.cpp | 2 +- esphome/core/defines.h | 2 +- platformio.ini | 8 +++--- 6 files changed, 38 insertions(+), 10 deletions(-) diff --git a/.clang-tidy.hash b/.clang-tidy.hash index 4901c0ccac..2cd4319325 100644 --- a/.clang-tidy.hash +++ b/.clang-tidy.hash @@ -1 +1 @@ -049d60eed541730efaa4c0dc5d337b4287bf29b6daa350b5dfc1f23915f1c52f +d7693a1e996cacd4a3d1c9a16336799c2a8cc3db02e4e74084151ce964581248 diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 56ab2eda88..684fff19ef 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -324,7 +324,7 @@ def _is_framework_url(source: str) -> str: # The default/recommended arduino framework version # - https://github.com/espressif/arduino-esp32/releases ARDUINO_FRAMEWORK_VERSION_LOOKUP = { - "recommended": cv.Version(3, 2, 1), + "recommended": cv.Version(3, 3, 2), "latest": cv.Version(3, 3, 2), "dev": cv.Version(3, 3, 2), } @@ -343,7 +343,7 @@ ARDUINO_PLATFORM_VERSION_LOOKUP = { # The default/recommended esp-idf framework version # - https://github.com/espressif/esp-idf/releases ESP_IDF_FRAMEWORK_VERSION_LOOKUP = { - "recommended": cv.Version(5, 4, 2), + "recommended": cv.Version(5, 5, 1), "latest": cv.Version(5, 5, 1), "dev": cv.Version(5, 5, 1), } @@ -363,7 +363,7 @@ ESP_IDF_PLATFORM_VERSION_LOOKUP = { # The platform-espressif32 version # - https://github.com/pioarduino/platform-espressif32/releases PLATFORM_VERSION_LOOKUP = { - "recommended": cv.Version(54, 3, 21, "2"), + "recommended": cv.Version(55, 3, 31, "1"), "latest": cv.Version(55, 3, 31, "1"), "dev": cv.Version(55, 3, 31, "1"), } diff --git a/esphome/components/esp32/boards.py b/esphome/components/esp32/boards.py index 5f039492c8..cbb314650a 100644 --- a/esphome/components/esp32/boards.py +++ b/esphome/components/esp32/boards.py @@ -1564,6 +1564,10 @@ BOARDS = { "name": "DFRobot Beetle ESP32-C3", "variant": VARIANT_ESP32C3, }, + "dfrobot_firebeetle2_esp32c6": { + "name": "DFRobot FireBeetle 2 ESP32-C6", + "variant": VARIANT_ESP32C6, + }, "dfrobot_firebeetle2_esp32e": { "name": "DFRobot Firebeetle 2 ESP32-E", "variant": VARIANT_ESP32, @@ -1604,6 +1608,22 @@ BOARDS = { "name": "Ai-Thinker ESP-C3-M1-I-Kit", "variant": VARIANT_ESP32C3, }, + "esp32-c5-devkitc-1": { + "name": "Espressif ESP32-C5-DevKitC-1 4MB no PSRAM", + "variant": VARIANT_ESP32C5, + }, + "esp32-c5-devkitc1-n16r4": { + "name": "Espressif ESP32-C5-DevKitC-1 N16R4 (16 MB Flash Quad, 4 MB PSRAM Quad)", + "variant": VARIANT_ESP32C5, + }, + "esp32-c5-devkitc1-n4": { + "name": "Espressif ESP32-C5-DevKitC-1 N4 (4MB no PSRAM)", + "variant": VARIANT_ESP32C5, + }, + "esp32-c5-devkitc1-n8r4": { + "name": "Espressif ESP32-C5-DevKitC-1 N8R4 (8 MB Flash Quad, 4 MB PSRAM Quad)", + "variant": VARIANT_ESP32C5, + }, "esp32-c6-devkitc-1": { "name": "Espressif ESP32-C6-DevKitC-1", "variant": VARIANT_ESP32C6, @@ -2048,6 +2068,10 @@ BOARDS = { "name": "M5Stack Station", "variant": VARIANT_ESP32, }, + "m5stack-tab5-p4": { + "name": "M5STACK Tab5 esp32-p4 Board", + "variant": VARIANT_ESP32P4, + }, "m5stack-timer-cam": { "name": "M5Stack Timer CAM", "variant": VARIANT_ESP32, @@ -2476,6 +2500,10 @@ BOARDS = { "name": "YelloByte YB-ESP32-S3-AMP (Rev.3)", "variant": VARIANT_ESP32S3, }, + "yb_esp32s3_drv": { + "name": "YelloByte YB-ESP32-S3-DRV", + "variant": VARIANT_ESP32S3, + }, "yb_esp32s3_eth": { "name": "YelloByte YB-ESP32-S3-ETH", "variant": VARIANT_ESP32S3, diff --git a/esphome/components/socket/bsd_sockets_impl.cpp b/esphome/components/socket/bsd_sockets_impl.cpp index e056696bcf..c7cca62027 100644 --- a/esphome/components/socket/bsd_sockets_impl.cpp +++ b/esphome/components/socket/bsd_sockets_impl.cpp @@ -145,7 +145,7 @@ class BSDSocketImpl : public Socket { } ssize_t sendto(const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) override { - return ::sendto(fd_, buf, len, flags, to, tolen); + return ::sendto(fd_, buf, len, flags, to, tolen); // NOLINT(readability-suspicious-call-argument) } int setblocking(bool blocking) override { diff --git a/esphome/core/defines.h b/esphome/core/defines.h index f770edaa00..1afb296fc0 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -202,7 +202,7 @@ #define USB_HOST_MAX_REQUESTS 16 #ifdef USE_ARDUINO -#define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 2, 1) +#define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 3, 2) #define USE_ETHERNET #define USE_ETHERNET_KSZ8081 #endif diff --git a/platformio.ini b/platformio.ini index 44b466a2b3..6b2a8657bb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -125,9 +125,9 @@ extra_scripts = post:esphome/components/esp8266/post_build.py.script ; This are common settings for the ESP32 (all variants) using Arduino. [common:esp32-arduino] extends = common:arduino -platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.21-2/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31-1/platform-espressif32.zip platform_packages = - pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.2.1/esp32-3.2.1.zip + pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.3.2/esp32-3.3.2.zip framework = arduino, espidf ; Arduino as an ESP-IDF component lib_deps = @@ -161,9 +161,9 @@ extra_scripts = post:esphome/components/esp32/post_build.py.script ; This are common settings for the ESP32 (all variants) using IDF. [common:esp32-idf] extends = common:idf -platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.21-2/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31-1/platform-espressif32.zip platform_packages = - pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.4.2/esp-idf-v5.4.2.zip + pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.5.1/esp-idf-v5.5.1.zip framework = espidf lib_deps = From e19a85b52346fe11bcc32943f7a90ca015de8be1 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:19:08 -0400 Subject: [PATCH 082/526] [esp32_ble] Add support for hosted BLE (#11167) --- .../bluetooth_proxy/bluetooth_proxy.h | 2 + esphome/components/esp32_ble/__init__.py | 9 ++++ esphome/components/esp32_ble/ble.cpp | 45 +++++++++++++++++++ .../components/esp32_ble/ble_advertising.h | 2 + .../esp32_ble_beacon/esp32_ble_beacon.cpp | 2 + .../esp32_ble_beacon/esp32_ble_beacon.h | 2 + .../esp32_ble_server/ble_server.cpp | 2 + .../esp32_ble_tracker/esp32_ble_tracker.cpp | 4 ++ .../bluetooth_proxy/test.esp32-p4-idf.yaml | 19 ++++++++ 9 files changed, 87 insertions(+) create mode 100644 tests/components/bluetooth_proxy/test.esp32-p4-idf.yaml diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index 1ce2321bee..a5f0fbe32f 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -16,7 +16,9 @@ #include "bluetooth_connection.h" +#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID #include +#endif #include namespace esphome::bluetooth_proxy { diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index fc8c65a2f4..246ebf0729 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -387,6 +387,15 @@ def final_validation(config): max_connections = config.get(CONF_MAX_CONNECTIONS, DEFAULT_MAX_CONNECTIONS) validate_connection_slots(max_connections) + # Check if hosted bluetooth is being used + if "esp32_hosted" in full_config: + add_idf_sdkconfig_option("CONFIG_BT_CLASSIC_ENABLED", False) + add_idf_sdkconfig_option("CONFIG_BT_BLE_ENABLED", True) + add_idf_sdkconfig_option("CONFIG_BT_BLUEDROID_ENABLED", True) + add_idf_sdkconfig_option("CONFIG_BT_CONTROLLER_DISABLED", True) + add_idf_sdkconfig_option("CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID", True) + add_idf_sdkconfig_option("CONFIG_ESP_HOSTED_BLUEDROID_HCI_VHCI", True) + # Check if BLE Server is needed has_ble_server = "esp32_ble_server" in full_config diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 9f8ca1d149..5bbd5fe9ed 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -6,7 +6,15 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID #include +#else +extern "C" { +#include +#include +#include +} +#endif #include #include #include @@ -136,6 +144,7 @@ void ESP32BLE::advertising_init_() { bool ESP32BLE::ble_setup_() { esp_err_t err; +#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID #ifdef USE_ARDUINO if (!btStart()) { ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); @@ -169,6 +178,28 @@ bool ESP32BLE::ble_setup_() { #endif esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); +#else + esp_hosted_connect_to_slave(); // NOLINT + + if (esp_hosted_bt_controller_init() != ESP_OK) { + ESP_LOGW(TAG, "esp_hosted_bt_controller_init failed"); + return false; + } + + if (esp_hosted_bt_controller_enable() != ESP_OK) { + ESP_LOGW(TAG, "esp_hosted_bt_controller_enable failed"); + return false; + } + + hosted_hci_bluedroid_open(); + + esp_bluedroid_hci_driver_operations_t operations = { + .send = hosted_hci_bluedroid_send, + .check_send_available = hosted_hci_bluedroid_check_send_available, + .register_host_callback = hosted_hci_bluedroid_register_host_callback, + }; + esp_bluedroid_attach_hci_driver(&operations); +#endif err = esp_bluedroid_init(); if (err != ESP_OK) { @@ -257,6 +288,7 @@ bool ESP32BLE::ble_dismantle_() { return false; } +#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID #ifdef USE_ARDUINO if (!btStop()) { ESP_LOGE(TAG, "btStop failed: %d", esp_bt_controller_get_status()); @@ -286,6 +318,19 @@ bool ESP32BLE::ble_dismantle_() { return false; } } +#endif +#else + if (esp_hosted_bt_controller_disable() != ESP_OK) { + ESP_LOGW(TAG, "esp_hosted_bt_controller_disable failed"); + return false; + } + + if (esp_hosted_bt_controller_deinit(false) != ESP_OK) { + ESP_LOGW(TAG, "esp_hosted_bt_controller_deinit failed"); + return false; + } + + hosted_hci_bluedroid_close(); #endif return true; } diff --git a/esphome/components/esp32_ble/ble_advertising.h b/esphome/components/esp32_ble/ble_advertising.h index 7a31d926f6..d7f1eeac9d 100644 --- a/esphome/components/esp32_ble/ble_advertising.h +++ b/esphome/components/esp32_ble/ble_advertising.h @@ -10,7 +10,9 @@ #ifdef USE_ESP32 #ifdef USE_ESP32_BLE_ADVERTISING +#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID #include +#endif #include #include diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp index af28804013..f2aa7e762e 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp @@ -3,7 +3,9 @@ #ifdef USE_ESP32 +#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID #include +#endif #include #include #include diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h index e37edf6cde..05afdc7379 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h @@ -5,7 +5,9 @@ #ifdef USE_ESP32 +#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID #include +#endif #include namespace esphome { diff --git a/esphome/components/esp32_ble_server/ble_server.cpp b/esphome/components/esp32_ble_server/ble_server.cpp index 25cc97eeaf..0e58224a5a 100644 --- a/esphome/components/esp32_ble_server/ble_server.cpp +++ b/esphome/components/esp32_ble_server/ble_server.cpp @@ -10,7 +10,9 @@ #include #include #include +#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID #include +#endif #include #include diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index d07e67825b..8577f12a92 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -7,7 +7,9 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID #include +#endif #include #include #include @@ -845,6 +847,7 @@ void ESP32BLETracker::log_unexpected_state_(const char *operation, ScannerState #ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE void ESP32BLETracker::update_coex_preference_(bool force_ble) { +#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID if (force_ble && !this->coex_prefer_ble_) { ESP_LOGD(TAG, "Setting coexistence to Bluetooth to make connection."); this->coex_prefer_ble_ = true; @@ -854,6 +857,7 @@ void ESP32BLETracker::update_coex_preference_(bool force_ble) { this->coex_prefer_ble_ = false; esp_coex_preference_set(ESP_COEX_PREFER_BALANCE); // Reset to default } +#endif // CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID } #endif diff --git a/tests/components/bluetooth_proxy/test.esp32-p4-idf.yaml b/tests/components/bluetooth_proxy/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..edb18c1ada --- /dev/null +++ b/tests/components/bluetooth_proxy/test.esp32-p4-idf.yaml @@ -0,0 +1,19 @@ +<<: !include common.yaml + +esp32_ble_tracker: + max_connections: 9 + +bluetooth_proxy: + active: true + connection_slots: 9 + +esp32_hosted: + active_high: true + variant: ESP32C6 + reset_pin: GPIO54 + cmd_pin: GPIO19 + clk_pin: GPIO18 + d0_pin: GPIO14 + d1_pin: GPIO15 + d2_pin: GPIO16 + d3_pin: GPIO17 From 5510ece6acd0db3d4415c314f64483fe55166849 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:34:01 -1000 Subject: [PATCH 083/526] Bump pylint from 4.0.0 to 4.0.1 (#11267) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index c4227fdbde..56ac775a94 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -pylint==4.0.0 +pylint==4.0.1 flake8==7.3.0 # also change in .pre-commit-config.yaml when updating ruff==0.14.0 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating From 2b3e7f38d224780ffbb6ef5b875329246ebeeb51 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 09:34:14 -1000 Subject: [PATCH 084/526] [esp32] Add option to disable libc locks in IRAM, saving ~1.3KB RAM (#10930) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/esp32/__init__.py | 10 ++++++++++ tests/components/esp32/test.esp32-idf.yaml | 1 + tests/components/esp32/test.esp32-s3-idf.yaml | 1 + 3 files changed, 12 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 684fff19ef..92f5c57638 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -544,6 +544,7 @@ CONF_ENABLE_LWIP_MDNS_QUERIES = "enable_lwip_mdns_queries" CONF_ENABLE_LWIP_BRIDGE_INTERFACE = "enable_lwip_bridge_interface" CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING = "enable_lwip_tcpip_core_locking" CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY = "enable_lwip_check_thread_safety" +CONF_DISABLE_LIBC_LOCKS_IN_IRAM = "disable_libc_locks_in_iram" def _validate_idf_component(config: ConfigType) -> ConfigType: @@ -606,6 +607,9 @@ FRAMEWORK_SCHEMA = cv.All( cv.Optional( CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY, default=True ): cv.boolean, + cv.Optional( + CONF_DISABLE_LIBC_LOCKS_IN_IRAM, default=True + ): cv.boolean, cv.Optional(CONF_EXECUTE_FROM_PSRAM): cv.boolean, } ), @@ -864,6 +868,12 @@ async def to_code(config): if advanced.get(CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY, True): add_idf_sdkconfig_option("CONFIG_LWIP_CHECK_THREAD_SAFETY", True) + # Disable placing libc locks in IRAM to save RAM + # This is safe for ESPHome since no IRAM ISRs (interrupts that run while cache is disabled) + # use libc lock APIs. Saves approximately 1.3KB (1,356 bytes) of IRAM. + if advanced.get(CONF_DISABLE_LIBC_LOCKS_IN_IRAM, True): + add_idf_sdkconfig_option("CONFIG_LIBC_LOCKS_PLACE_IN_IRAM", False) + cg.add_platformio_option("board_build.partitions", "partitions.csv") if CONF_PARTITIONS in config: add_extra_build_file( diff --git a/tests/components/esp32/test.esp32-idf.yaml b/tests/components/esp32/test.esp32-idf.yaml index ccf0b7cbd5..6338fe98dd 100644 --- a/tests/components/esp32/test.esp32-idf.yaml +++ b/tests/components/esp32/test.esp32-idf.yaml @@ -5,6 +5,7 @@ esp32: advanced: enable_lwip_mdns_queries: true enable_lwip_bridge_interface: true + disable_libc_locks_in_iram: false # Test explicit opt-out of RAM optimization wifi: ssid: MySSID diff --git a/tests/components/esp32/test.esp32-s3-idf.yaml b/tests/components/esp32/test.esp32-s3-idf.yaml index 1d5a5e52a4..4ae5e6b999 100644 --- a/tests/components/esp32/test.esp32-s3-idf.yaml +++ b/tests/components/esp32/test.esp32-s3-idf.yaml @@ -4,6 +4,7 @@ esp32: type: esp-idf advanced: execute_from_psram: true + disable_libc_locks_in_iram: true # Test default RAM optimization enabled psram: mode: octal From ae1f54d39834b85f18215b4ccbcdba6cb382472d Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 16 Oct 2025 06:09:52 +1000 Subject: [PATCH 085/526] [mipi_spi] Rotation fixes (#11226) --- esphome/components/display/display.cpp | 7 ++- esphome/components/mipi/__init__.py | 31 +++++++------ esphome/components/mipi_spi/display.py | 28 +++++++----- esphome/components/mipi_spi/mipi_spi.h | 44 +++++++++++-------- .../components/mipi_spi/models/waveshare.py | 10 +++++ tests/component_tests/mipi_spi/test_init.py | 4 +- .../components/chsc6x/test.esp32-c3-idf.yaml | 4 +- tests/components/mipi_spi/common.yaml | 2 +- .../components/mipi_spi/test.rp2040-ard.yaml | 2 +- 9 files changed, 80 insertions(+), 52 deletions(-) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index c666eee298..1451d14e2e 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -775,7 +775,7 @@ void Display::test_card() { int shift_y = (h - image_h) / 2; int line_w = (image_w - 6) / 6; int image_c = image_w / 2; - for (auto i = 0; i <= image_h; i++) { + for (auto i = 0; i != image_h; i++) { int c = esp_scale(i, image_h); this->horizontal_line(shift_x + 0, shift_y + i, line_w, r.fade_to_white(c)); this->horizontal_line(shift_x + line_w, shift_y + i, line_w, r.fade_to_black(c)); // @@ -809,8 +809,11 @@ void Display::test_card() { } } } - this->rectangle(0, 0, w, h, Color(127, 0, 127)); this->filled_rectangle(0, 0, 10, 10, Color(255, 0, 255)); + this->filled_rectangle(w - 10, 0, 10, 10, Color(255, 0, 255)); + this->filled_rectangle(0, h - 10, 10, 10, Color(255, 0, 255)); + this->filled_rectangle(w - 10, h - 10, 10, 10, Color(255, 0, 255)); + this->rectangle(0, 0, w, h, Color(255, 255, 255)); this->stop_poller(); } diff --git a/esphome/components/mipi/__init__.py b/esphome/components/mipi/__init__.py index 7e687cabaa..4dff1af62a 100644 --- a/esphome/components/mipi/__init__.py +++ b/esphome/components/mipi/__init__.py @@ -11,6 +11,7 @@ from esphome.const import ( CONF_BRIGHTNESS, CONF_COLOR_ORDER, CONF_DIMENSIONS, + CONF_DISABLED, CONF_HEIGHT, CONF_INIT_SEQUENCE, CONF_INVERT_COLORS, @@ -301,6 +302,8 @@ class DriverChip: Check if a rotation can be implemented in hardware using the MADCTL register. A rotation of 180 is always possible if x and y mirroring are supported, 90 and 270 are possible if the model supports swapping X and Y. """ + if config.get(CONF_TRANSFORM) == CONF_DISABLED: + return False transforms = self.transforms rotation = config.get(CONF_ROTATION, 0) if rotation == 0 or not transforms: @@ -358,26 +361,26 @@ class DriverChip: CONF_SWAP_XY: self.get_default(CONF_SWAP_XY), }, ) - # fill in defaults if not provided - mirror_x = transform.get(CONF_MIRROR_X, self.get_default(CONF_MIRROR_X)) - mirror_y = transform.get(CONF_MIRROR_Y, self.get_default(CONF_MIRROR_Y)) - swap_xy = transform.get(CONF_SWAP_XY, self.get_default(CONF_SWAP_XY)) - transform[CONF_MIRROR_X] = mirror_x - transform[CONF_MIRROR_Y] = mirror_y - transform[CONF_SWAP_XY] = swap_xy - + if not isinstance(transform, dict): + # Presumably disabled + return { + CONF_MIRROR_X: False, + CONF_MIRROR_Y: False, + CONF_SWAP_XY: False, + CONF_TRANSFORM: False, + } # Can we use the MADCTL register to set the rotation? if can_transform and CONF_TRANSFORM not in config: rotation = config[CONF_ROTATION] if rotation == 180: - transform[CONF_MIRROR_X] = not mirror_x - transform[CONF_MIRROR_Y] = not mirror_y + transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X] + transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y] elif rotation == 90: - transform[CONF_SWAP_XY] = not swap_xy - transform[CONF_MIRROR_X] = not mirror_x + transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY] + transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X] else: - transform[CONF_SWAP_XY] = not swap_xy - transform[CONF_MIRROR_Y] = not mirror_y + transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY] + transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y] transform[CONF_TRANSFORM] = True return transform diff --git a/esphome/components/mipi_spi/display.py b/esphome/components/mipi_spi/display.py index 52b5b86fba..891c8b42ff 100644 --- a/esphome/components/mipi_spi/display.py +++ b/esphome/components/mipi_spi/display.py @@ -37,6 +37,7 @@ from esphome.const import ( CONF_DATA_RATE, CONF_DC_PIN, CONF_DIMENSIONS, + CONF_DISABLED, CONF_ENABLE_PIN, CONF_ID, CONF_INIT_SEQUENCE, @@ -146,12 +147,15 @@ def swap_xy_schema(model): def model_schema(config): model = MODELS[config[CONF_MODEL]] bus_mode = config[CONF_BUS_MODE] - transform = cv.Schema( - { - cv.Required(CONF_MIRROR_X): cv.boolean, - cv.Required(CONF_MIRROR_Y): cv.boolean, - **swap_xy_schema(model), - } + transform = cv.Any( + cv.Schema( + { + cv.Required(CONF_MIRROR_X): cv.boolean, + cv.Required(CONF_MIRROR_Y): cv.boolean, + **swap_xy_schema(model), + } + ), + cv.one_of(CONF_DISABLED, lower=True), ) # CUSTOM model will need to provide a custom init sequence iseqconf = ( @@ -160,7 +164,11 @@ def model_schema(config): else cv.Optional(CONF_INIT_SEQUENCE) ) # Dimensions are optional if the model has a default width and the x-y transform is not overridden - is_swapped = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY) is True + transform_config = config.get(CONF_TRANSFORM, {}) + is_swapped = ( + isinstance(transform_config, dict) + and transform_config.get(CONF_SWAP_XY, False) is True + ) cv_dimensions = ( cv.Optional if model.get_default(CONF_WIDTH) and not is_swapped else cv.Required ) @@ -192,9 +200,7 @@ def model_schema(config): .extend( { cv.GenerateID(): cv.declare_id(MipiSpi), - cv_dimensions(CONF_DIMENSIONS): dimension_schema( - model.get_default(CONF_DRAW_ROUNDING, 1) - ), + cv_dimensions(CONF_DIMENSIONS): dimension_schema(1), model.option(CONF_ENABLE_PIN, cv.UNDEFINED): cv.ensure_list( pins.gpio_output_pin_schema ), @@ -400,6 +406,7 @@ def get_instance(config): offset_height, DISPLAY_ROTATIONS[rotation], frac, + config[CONF_DRAW_ROUNDING], ] ) return MipiSpiBuffer, templateargs @@ -431,7 +438,6 @@ async def to_code(config): else: config[CONF_ROTATION] = 0 cg.add(var.set_model(config[CONF_MODEL])) - cg.add(var.set_draw_rounding(config[CONF_DRAW_ROUNDING])) if enable_pin := config.get(CONF_ENABLE_PIN): enable = [await cg.gpio_pin_expression(pin) for pin in enable_pin] cg.add(var.set_enable_pins(enable)) diff --git a/esphome/components/mipi_spi/mipi_spi.h b/esphome/components/mipi_spi/mipi_spi.h index 248d5b7104..7e597d1c61 100644 --- a/esphome/components/mipi_spi/mipi_spi.h +++ b/esphome/components/mipi_spi/mipi_spi.h @@ -38,7 +38,7 @@ static constexpr uint8_t MADCTL_BGR = 0x08; // Bit 3 Blue-Green-Red pixel ord static constexpr uint8_t MADCTL_XFLIP = 0x02; // Mirror the display horizontally static constexpr uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically -static const uint8_t DELAY_FLAG = 0xFF; +static constexpr uint8_t DELAY_FLAG = 0xFF; // store a 16 bit value in a buffer, big endian. static inline void put16_be(uint8_t *buf, uint16_t value) { buf[0] = value >> 8; @@ -79,7 +79,7 @@ class MipiSpi : public display::Display, public spi::SPIDevice { public: - MipiSpi() {} + MipiSpi() = default; void update() override { this->stop_poller(); } void draw_pixel_at(int x, int y, Color color) override {} void set_model(const char *model) { this->model_ = model; } @@ -99,7 +99,6 @@ class MipiSpi : public display::Display, int get_width_internal() override { return WIDTH; } int get_height_internal() override { return HEIGHT; } void set_init_sequence(const std::vector &sequence) { this->init_sequence_ = sequence; } - void set_draw_rounding(unsigned rounding) { this->draw_rounding_ = rounding; } // reset the display, and write the init sequence void setup() override { @@ -326,6 +325,7 @@ class MipiSpi : public display::Display, /** * Writes a buffer to the display. + * @param ptr The pointer to the pixel data * @param w Width of each line in bytes * @param h Height of the buffer in rows * @param pad Padding in bytes after each line @@ -424,7 +424,6 @@ class MipiSpi : public display::Display, // other properties set by configuration bool invert_colors_{}; - unsigned draw_rounding_{2}; optional brightness_{}; const char *model_{"Unknown"}; std::vector init_sequence_{}; @@ -444,12 +443,20 @@ class MipiSpi : public display::Display, * @tparam OFFSET_WIDTH The x-offset of the display in pixels * @tparam OFFSET_HEIGHT The y-offset of the display in pixels * @tparam FRACTION The fraction of the display size to use for the buffer (e.g. 4 means a 1/4 buffer). + * @tparam ROUNDING The alignment requirement for drawing operations (e.g. 2 means that x coordinates must be even) */ template + uint16_t WIDTH, uint16_t HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT, display::DisplayRotation ROTATION, + int FRACTION, unsigned ROUNDING> class MipiSpiBuffer : public MipiSpi { public: + // these values define the buffer size needed to write in accordance with the chip pixel alignment + // requirements. If the required rounding does not divide the width and height, we round up to the next multiple and + // ignore the extra columns and rows when drawing, but use them to write to the display. + static constexpr unsigned BUFFER_WIDTH = (WIDTH + ROUNDING - 1) / ROUNDING * ROUNDING; + static constexpr unsigned BUFFER_HEIGHT = (HEIGHT + ROUNDING - 1) / ROUNDING * ROUNDING; + MipiSpiBuffer() { this->rotation_ = ROTATION; } void dump_config() override { @@ -461,15 +468,15 @@ class MipiSpiBuffer : public MipiSpirotation_, BUFFERPIXEL * 8, FRACTION, sizeof(BUFFERTYPE) * WIDTH * HEIGHT / FRACTION, - this->draw_rounding_); + this->rotation_, BUFFERPIXEL * 8, FRACTION, + sizeof(BUFFERTYPE) * BUFFER_WIDTH * BUFFER_HEIGHT / FRACTION, ROUNDING); } void setup() override { MipiSpi::setup(); RAMAllocator allocator{}; - this->buffer_ = allocator.allocate(WIDTH * HEIGHT / FRACTION); + this->buffer_ = allocator.allocate(BUFFER_WIDTH * BUFFER_HEIGHT / FRACTION); if (this->buffer_ == nullptr) { this->mark_failed("Buffer allocation failed"); } @@ -508,15 +515,14 @@ class MipiSpiBuffer : public MipiSpix_low_, this->y_low_, this->x_high_, this->y_high_); // Some chips require that the drawing window be aligned on certain boundaries - auto dr = this->draw_rounding_; - this->x_low_ = this->x_low_ / dr * dr; - this->y_low_ = this->y_low_ / dr * dr; - this->x_high_ = (this->x_high_ + dr) / dr * dr - 1; - this->y_high_ = (this->y_high_ + dr) / dr * dr - 1; + this->x_low_ = this->x_low_ / ROUNDING * ROUNDING; + this->y_low_ = this->y_low_ / ROUNDING * ROUNDING; + this->x_high_ = (this->x_high_ + ROUNDING) / ROUNDING * ROUNDING - 1; + this->y_high_ = (this->y_high_ + ROUNDING) / ROUNDING * ROUNDING - 1; int w = this->x_high_ - this->x_low_ + 1; int h = this->y_high_ - this->y_low_ + 1; this->write_to_display_(this->x_low_, this->y_low_, w, h, this->buffer_, this->x_low_, - this->y_low_ - this->start_line_, WIDTH - w); + this->y_low_ - this->start_line_, BUFFER_WIDTH - w); // invalidate watermarks this->x_low_ = WIDTH; this->y_low_ = HEIGHT; @@ -536,10 +542,10 @@ class MipiSpiBuffer : public MipiSpiget_clipping().inside(x, y)) return; - rotate_coordinates_(x, y); + rotate_coordinates(x, y); if (x < 0 || x >= WIDTH || y < this->start_line_ || y >= this->end_line_) return; - this->buffer_[(y - this->start_line_) * WIDTH + x] = convert_color_(color); + this->buffer_[(y - this->start_line_) * BUFFER_WIDTH + x] = convert_color(color); if (x < this->x_low_) { this->x_low_ = x; } @@ -560,7 +566,7 @@ class MipiSpiBuffer : public MipiSpiy_low_ = this->start_line_; this->x_high_ = WIDTH - 1; this->y_high_ = this->end_line_ - 1; - std::fill_n(this->buffer_, HEIGHT * WIDTH / FRACTION, convert_color_(color)); + std::fill_n(this->buffer_, HEIGHT * BUFFER_WIDTH / FRACTION, convert_color(color)); } int get_width() override { @@ -577,7 +583,7 @@ class MipiSpiBuffer : public MipiSpi> 3 | color.b >> 6; } else if constexpr (BUFFERPIXEL == PIXEL_MODE_16) { diff --git a/esphome/components/mipi_spi/models/waveshare.py b/esphome/components/mipi_spi/models/waveshare.py index 7a55027e58..e4e090da2e 100644 --- a/esphome/components/mipi_spi/models/waveshare.py +++ b/esphome/components/mipi_spi/models/waveshare.py @@ -3,6 +3,7 @@ import esphome.config_validation as cv from .amoled import CO5300 from .ili import ILI9488_A +from .jc import AXS15231 DriverChip( "WAVESHARE-4-TFT", @@ -152,3 +153,12 @@ CO5300.extend( cs_pin=12, reset_pin=39, ) + +AXS15231.extend( + "WAVESHARE-ESP32-S3-TOUCH-LCD-3.49", + width=172, + height=640, + data_rate="80MHz", + cs_pin=9, + reset_pin=21, +) diff --git a/tests/component_tests/mipi_spi/test_init.py b/tests/component_tests/mipi_spi/test_init.py index fbb3222812..e68f6fbfba 100644 --- a/tests/component_tests/mipi_spi/test_init.py +++ b/tests/component_tests/mipi_spi/test_init.py @@ -69,7 +69,7 @@ def run_schema_validation(config: ConfigType) -> None: { "id": "display_id", "model": "custom", - "dimensions": {"width": 320, "height": 240}, + "dimensions": {"width": 260, "height": 260}, "draw_rounding": 13, "init_sequence": [[0xA0, 0x01]], }, @@ -336,7 +336,7 @@ def test_native_generation( main_cpp = generate_main(component_fixture_path("native.yaml")) assert ( - "mipi_spi::MipiSpiBuffer()" + "mipi_spi::MipiSpiBuffer()" in main_cpp ) assert "set_init_sequence({240, 1, 8, 242" in main_cpp diff --git a/tests/components/chsc6x/test.esp32-c3-idf.yaml b/tests/components/chsc6x/test.esp32-c3-idf.yaml index f32f147a44..f0de4107d7 100644 --- a/tests/components/chsc6x/test.esp32-c3-idf.yaml +++ b/tests/components/chsc6x/test.esp32-c3-idf.yaml @@ -7,8 +7,8 @@ display: id: ili9xxx_display model: GC9A01A invert_colors: True - cs_pin: 10 - dc_pin: 6 + cs_pin: 11 + dc_pin: 7 pages: - id: page1 lambda: |- diff --git a/tests/components/mipi_spi/common.yaml b/tests/components/mipi_spi/common.yaml index 03f807f53c..692a9f436e 100644 --- a/tests/components/mipi_spi/common.yaml +++ b/tests/components/mipi_spi/common.yaml @@ -10,7 +10,7 @@ display: invert_colors: true show_test_card: true spi_mode: mode0 - draw_rounding: 8 + draw_rounding: 4 use_axis_flips: true init_sequence: - [0xd0, 1, 2, 3] diff --git a/tests/components/mipi_spi/test.rp2040-ard.yaml b/tests/components/mipi_spi/test.rp2040-ard.yaml index 380cebcde3..6336652999 100644 --- a/tests/components/mipi_spi/test.rp2040-ard.yaml +++ b/tests/components/mipi_spi/test.rp2040-ard.yaml @@ -1,7 +1,7 @@ substitutions: dc_pin: GPIO14 cs_pin: GPIO13 - enable_pin: GPIO16 + enable_pin: GPIO17 reset_pin: GPIO20 packages: From 332f52e149d66d79f64002380b2dfe6f4bc02d81 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 12:28:56 -1000 Subject: [PATCH 086/526] [api] Use FixedVector for ListEntitiesServicesResponse args (#11230) --- esphome/components/api/api.proto | 2 +- esphome/components/api/api_pb2.h | 2 +- esphome/components/api/user_services.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 34864c5ce8..c7d8fb28f0 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -866,7 +866,7 @@ message ListEntitiesServicesResponse { string name = 1; fixed32 key = 2; - repeated ListEntitiesServicesArgument args = 3; + repeated ListEntitiesServicesArgument args = 3 [(fixed_vector) = true]; } message ExecuteServiceArgument { option (ifdef) = "USE_API_SERVICES"; diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 7d6b31ca3c..20866850a9 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1263,7 +1263,7 @@ class ListEntitiesServicesResponse final : public ProtoMessage { StringRef name_ref_{}; void set_name(const StringRef &ref) { this->name_ref_ = ref; } uint32_t key{0}; - std::vector args{}; + FixedVector args{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(ProtoSize &size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP diff --git a/esphome/components/api/user_services.h b/esphome/components/api/user_services.h index 3996c921a9..29843a2f78 100644 --- a/esphome/components/api/user_services.h +++ b/esphome/components/api/user_services.h @@ -35,9 +35,9 @@ template class UserServiceBase : public UserServiceDescriptor { msg.set_name(StringRef(this->name_)); msg.key = this->key_; std::array arg_types = {to_service_arg_type()...}; + msg.args.init(sizeof...(Ts)); for (size_t i = 0; i < sizeof...(Ts); i++) { - msg.args.emplace_back(); - auto &arg = msg.args.back(); + auto &arg = msg.args.emplace_back(); arg.type = arg_types[i]; arg.set_name(StringRef(this->arg_names_[i])); } From 28454b8219701c79958b5eea2f82a3120864070c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 14:00:27 -1000 Subject: [PATCH 087/526] Bump aioesphomeapi from 41.18.0 to 42.0.0 (#11273) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b6e4a189d1..92ab24e754 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==41.18.0 +aioesphomeapi==42.0.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import From b190f37ae7082b5e3b8341ebac4f21ae55d397fd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 14:06:02 -1000 Subject: [PATCH 088/526] [ota] Fix MQTT resolution when static IP appears first in device list (#11272) --- esphome/__main__.py | 116 ++++++++++---- tests/unit_tests/test_main.py | 287 ++++++++++++++++++++++++++++++++++ 2 files changed, 370 insertions(+), 33 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 8e0c475525..89197737e3 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -117,6 +117,17 @@ class Purpose(StrEnum): LOGGING = "logging" +class PortType(StrEnum): + SERIAL = "SERIAL" + NETWORK = "NETWORK" + MQTT = "MQTT" + MQTTIP = "MQTTIP" + + +# Magic MQTT port types that require special handling +_MQTT_PORT_TYPES = frozenset({PortType.MQTT, PortType.MQTTIP}) + + def _resolve_with_cache(address: str, purpose: Purpose) -> list[str]: """Resolve an address using cache if available, otherwise return the address itself.""" if CORE.address_cache and (cached := CORE.address_cache.get_addresses(address)): @@ -280,16 +291,67 @@ def mqtt_get_ip(config: ConfigType, username: str, password: str, client_id: str return mqtt.get_esphome_device_ip(config, username, password, client_id) -_PORT_TO_PORT_TYPE = { - "MQTT": "MQTT", - "MQTTIP": "MQTTIP", -} +def _resolve_network_devices( + devices: list[str], config: ConfigType, args: ArgsProtocol +) -> list[str]: + """Resolve device list, converting MQTT magic strings to actual IP addresses. + + This function filters the devices list to: + - Replace MQTT/MQTTIP magic strings with actual IP addresses via MQTT lookup + - Deduplicate addresses while preserving order + - Only resolve MQTT once even if multiple MQTT strings are present + - If MQTT resolution fails, log a warning and continue with other devices + + Args: + devices: List of device identifiers (IPs, hostnames, or magic strings) + config: ESPHome configuration + args: Command-line arguments containing MQTT credentials + + Returns: + List of network addresses suitable for connection attempts + """ + network_devices: list[str] = [] + mqtt_resolved: bool = False + + for device in devices: + port_type = get_port_type(device) + if port_type in _MQTT_PORT_TYPES: + # Only resolve MQTT once, even if multiple MQTT entries + if not mqtt_resolved: + try: + mqtt_ips = mqtt_get_ip( + config, args.username, args.password, args.client_id + ) + network_devices.extend(mqtt_ips) + except EsphomeError as err: + _LOGGER.warning( + "MQTT IP discovery failed (%s), will try other devices if available", + err, + ) + mqtt_resolved = True + elif device not in network_devices: + # Regular network address or IP - add if not already present + network_devices.append(device) + + return network_devices -def get_port_type(port: str) -> str: +def get_port_type(port: str) -> PortType: + """Determine the type of port/device identifier. + + Returns: + PortType.SERIAL for serial ports (/dev/ttyUSB0, COM1, etc.) + PortType.MQTT for MQTT logging + PortType.MQTTIP for MQTT IP lookup + PortType.NETWORK for IP addresses, hostnames, or mDNS names + """ if port.startswith("/") or port.startswith("COM"): - return "SERIAL" - return _PORT_TO_PORT_TYPE.get(port, "NETWORK") + return PortType.SERIAL + if port == "MQTT": + return PortType.MQTT + if port == "MQTTIP": + return PortType.MQTTIP + return PortType.NETWORK def run_miniterm(config: ConfigType, port: str, args) -> int: @@ -489,7 +551,7 @@ def upload_using_platformio(config: ConfigType, port: str): def check_permissions(port: str): - if os.name == "posix" and get_port_type(port) == "SERIAL": + if os.name == "posix" and get_port_type(port) == PortType.SERIAL: # Check if we can open selected serial port if not os.access(port, os.F_OK): raise EsphomeError( @@ -517,7 +579,7 @@ def upload_program( except AttributeError: pass - if get_port_type(host) == "SERIAL": + if get_port_type(host) == PortType.SERIAL: check_permissions(host) exit_code = 1 @@ -550,11 +612,10 @@ def upload_program( else: binary = CORE.firmware_bin - # MQTT address resolution - if get_port_type(host) in ("MQTT", "MQTTIP"): - devices = mqtt_get_ip(config, args.username, args.password, args.client_id) + # Resolve MQTT magic strings to actual IP addresses + network_devices = _resolve_network_devices(devices, config, args) - return espota2.run_ota(devices, remote_port, password, binary) + return espota2.run_ota(network_devices, remote_port, password, binary) def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int | None: @@ -569,33 +630,22 @@ def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int raise EsphomeError("Logger is not configured!") port = devices[0] + port_type = get_port_type(port) - if get_port_type(port) == "SERIAL": + if port_type == PortType.SERIAL: check_permissions(port) return run_miniterm(config, port, args) - port_type = get_port_type(port) - # Check if we should use API for logging - if has_api(): - addresses_to_use: list[str] | None = None + # Resolve MQTT magic strings to actual IP addresses + if has_api() and ( + network_devices := _resolve_network_devices(devices, config, args) + ): + from esphome.components.api.client import run_logs - if port_type == "NETWORK": - # Network addresses (IPs, mDNS names, or regular DNS hostnames) can be used - # The resolve_ip_address() function in helpers.py handles all types - addresses_to_use = devices - elif port_type in ("MQTT", "MQTTIP") and has_mqtt_ip_lookup(): - # Use MQTT IP lookup for MQTT/MQTTIP types - addresses_to_use = mqtt_get_ip( - config, args.username, args.password, args.client_id - ) + return run_logs(config, network_devices) - if addresses_to_use is not None: - from esphome.components.api.client import run_logs - - return run_logs(config, addresses_to_use) - - if port_type in ("NETWORK", "MQTT") and has_mqtt_logging(): + if port_type in (PortType.NETWORK, PortType.MQTT) and has_mqtt_logging(): from esphome import mqtt return mqtt.show_logs( diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index becf911fa3..422a199496 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -1976,3 +1976,290 @@ def test_command_clean_all_args_used() -> None: # Verify the correct configuration paths were passed mock_clean_all.assert_any_call(["/path/to/config1"]) mock_clean_all.assert_any_call(["/path/to/config2", "/path/to/config3"]) + + +def test_upload_program_ota_static_ip_with_mqttip( + mock_mqtt_get_ip: Mock, + mock_run_ota: Mock, + tmp_path: Path, +) -> None: + """Test upload_program with static IP and MQTTIP (issue #11260). + + This tests the scenario where a device has manual_ip (static IP) configured + and MQTT is also configured. The devices list contains both the static IP + and "MQTTIP" magic string. This previously failed because only the first + device was checked for MQTT resolution. + """ + setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path) + + mock_mqtt_get_ip.return_value = ["192.168.2.50"] # Different subnet + mock_run_ota.return_value = (0, "192.168.1.100") + + config = { + CONF_OTA: [ + { + CONF_PLATFORM: CONF_ESPHOME, + CONF_PORT: 3232, + } + ], + CONF_MQTT: { + CONF_BROKER: "mqtt.local", + }, + } + args = MockArgs(username="user", password="pass", client_id="client") + # Simulates choose_upload_log_host returning static IP + MQTTIP + devices = ["192.168.1.100", "MQTTIP"] + + exit_code, host = upload_program(config, args, devices) + + assert exit_code == 0 + assert host == "192.168.1.100" + + # Verify MQTT was resolved + mock_mqtt_get_ip.assert_called_once_with(config, "user", "pass", "client") + + # Verify espota2.run_ota was called with both IPs + expected_firmware = ( + tmp_path / ".esphome" / "build" / "test" / ".pioenvs" / "test" / "firmware.bin" + ) + mock_run_ota.assert_called_once_with( + ["192.168.1.100", "192.168.2.50"], 3232, "", expected_firmware + ) + + +def test_upload_program_ota_multiple_mqttip_resolves_once( + mock_mqtt_get_ip: Mock, + mock_run_ota: Mock, + tmp_path: Path, +) -> None: + """Test that MQTT resolution only happens once even with multiple MQTT magic strings.""" + setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path) + + mock_mqtt_get_ip.return_value = ["192.168.2.50", "192.168.2.51"] + mock_run_ota.return_value = (0, "192.168.2.50") + + config = { + CONF_OTA: [ + { + CONF_PLATFORM: CONF_ESPHOME, + CONF_PORT: 3232, + } + ], + CONF_MQTT: { + CONF_BROKER: "mqtt.local", + }, + } + args = MockArgs(username="user", password="pass", client_id="client") + # Multiple MQTT magic strings in the list + devices = ["MQTTIP", "MQTT", "192.168.1.100"] + + exit_code, host = upload_program(config, args, devices) + + assert exit_code == 0 + assert host == "192.168.2.50" + + # Verify MQTT was only resolved once despite multiple MQTT magic strings + mock_mqtt_get_ip.assert_called_once_with(config, "user", "pass", "client") + + # Verify espota2.run_ota was called with all unique IPs + expected_firmware = ( + tmp_path / ".esphome" / "build" / "test" / ".pioenvs" / "test" / "firmware.bin" + ) + mock_run_ota.assert_called_once_with( + ["192.168.2.50", "192.168.2.51", "192.168.1.100"], 3232, "", expected_firmware + ) + + +def test_upload_program_ota_mqttip_deduplication( + mock_mqtt_get_ip: Mock, + mock_run_ota: Mock, + tmp_path: Path, +) -> None: + """Test that duplicate IPs are filtered when MQTT returns same IP as static IP.""" + setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path) + + # MQTT returns the same IP as the static IP + mock_mqtt_get_ip.return_value = ["192.168.1.100"] + mock_run_ota.return_value = (0, "192.168.1.100") + + config = { + CONF_OTA: [ + { + CONF_PLATFORM: CONF_ESPHOME, + CONF_PORT: 3232, + } + ], + CONF_MQTT: { + CONF_BROKER: "mqtt.local", + }, + } + args = MockArgs(username="user", password="pass", client_id="client") + devices = ["192.168.1.100", "MQTTIP"] + + exit_code, host = upload_program(config, args, devices) + + assert exit_code == 0 + assert host == "192.168.1.100" + + # Verify MQTT was resolved + mock_mqtt_get_ip.assert_called_once_with(config, "user", "pass", "client") + + # Verify espota2.run_ota was called with deduplicated IPs (only one instance of 192.168.1.100) + # Note: Current implementation doesn't dedupe, so we'll get the IP twice + # This test documents current behavior - deduplication could be future enhancement + mock_run_ota.assert_called_once() + call_args = mock_run_ota.call_args[0] + # Should contain both the original IP and MQTT-resolved IP (even if duplicate) + assert "192.168.1.100" in call_args[0] + + +@patch("esphome.components.api.client.run_logs") +def test_show_logs_api_static_ip_with_mqttip( + mock_run_logs: Mock, + mock_mqtt_get_ip: Mock, +) -> None: + """Test show_logs with static IP and MQTTIP (issue #11260). + + This tests the scenario where a device has manual_ip (static IP) configured + and MQTT is also configured. The devices list contains both the static IP + and "MQTTIP" magic string. + """ + setup_core( + config={ + "logger": {}, + CONF_API: {}, + CONF_MQTT: {CONF_BROKER: "mqtt.local"}, + }, + platform=PLATFORM_ESP32, + ) + mock_run_logs.return_value = 0 + mock_mqtt_get_ip.return_value = ["192.168.2.50"] + + args = MockArgs(username="user", password="pass", client_id="client") + # Simulates choose_upload_log_host returning static IP + MQTTIP + devices = ["192.168.1.100", "MQTTIP"] + + result = show_logs(CORE.config, args, devices) + + assert result == 0 + + # Verify MQTT was resolved + mock_mqtt_get_ip.assert_called_once_with(CORE.config, "user", "pass", "client") + + # Verify run_logs was called with both IPs + mock_run_logs.assert_called_once_with( + CORE.config, ["192.168.1.100", "192.168.2.50"] + ) + + +@patch("esphome.components.api.client.run_logs") +def test_show_logs_api_multiple_mqttip_resolves_once( + mock_run_logs: Mock, + mock_mqtt_get_ip: Mock, +) -> None: + """Test that MQTT resolution only happens once for show_logs with multiple MQTT magic strings.""" + setup_core( + config={ + "logger": {}, + CONF_API: {}, + CONF_MQTT: {CONF_BROKER: "mqtt.local"}, + }, + platform=PLATFORM_ESP32, + ) + mock_run_logs.return_value = 0 + mock_mqtt_get_ip.return_value = ["192.168.2.50", "192.168.2.51"] + + args = MockArgs(username="user", password="pass", client_id="client") + # Multiple MQTT magic strings in the list + devices = ["MQTTIP", "192.168.1.100", "MQTT"] + + result = show_logs(CORE.config, args, devices) + + assert result == 0 + + # Verify MQTT was only resolved once despite multiple MQTT magic strings + mock_mqtt_get_ip.assert_called_once_with(CORE.config, "user", "pass", "client") + + # Verify run_logs was called with all unique IPs (MQTT strings replaced with IPs) + # Note: "MQTT" is a different magic string from "MQTTIP", but both trigger MQTT resolution + # The _resolve_network_devices helper filters out both after first resolution + mock_run_logs.assert_called_once_with( + CORE.config, ["192.168.2.50", "192.168.2.51", "192.168.1.100"] + ) + + +def test_upload_program_ota_mqtt_timeout_fallback( + mock_mqtt_get_ip: Mock, + mock_run_ota: Mock, + tmp_path: Path, +) -> None: + """Test upload_program falls back to other devices when MQTT times out.""" + setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path) + + # MQTT times out + mock_mqtt_get_ip.side_effect = EsphomeError("Failed to find IP via MQTT") + mock_run_ota.return_value = (0, "192.168.1.100") + + config = { + CONF_OTA: [ + { + CONF_PLATFORM: CONF_ESPHOME, + CONF_PORT: 3232, + } + ], + CONF_MQTT: { + CONF_BROKER: "mqtt.local", + }, + } + args = MockArgs(username="user", password="pass", client_id="client") + # Static IP first, MQTTIP second + devices = ["192.168.1.100", "MQTTIP"] + + exit_code, host = upload_program(config, args, devices) + + # Should succeed using the static IP even though MQTT failed + assert exit_code == 0 + assert host == "192.168.1.100" + + # Verify MQTT was attempted + mock_mqtt_get_ip.assert_called_once_with(config, "user", "pass", "client") + + # Verify espota2.run_ota was called with only the static IP (MQTT failed) + expected_firmware = ( + tmp_path / ".esphome" / "build" / "test" / ".pioenvs" / "test" / "firmware.bin" + ) + mock_run_ota.assert_called_once_with(["192.168.1.100"], 3232, "", expected_firmware) + + +@patch("esphome.components.api.client.run_logs") +def test_show_logs_api_mqtt_timeout_fallback( + mock_run_logs: Mock, + mock_mqtt_get_ip: Mock, +) -> None: + """Test show_logs falls back to other devices when MQTT times out.""" + setup_core( + config={ + "logger": {}, + CONF_API: {}, + CONF_MQTT: {CONF_BROKER: "mqtt.local"}, + }, + platform=PLATFORM_ESP32, + ) + mock_run_logs.return_value = 0 + # MQTT times out + mock_mqtt_get_ip.side_effect = EsphomeError("Failed to find IP via MQTT") + + args = MockArgs(username="user", password="pass", client_id="client") + # Static IP first, MQTTIP second + devices = ["192.168.1.100", "MQTTIP"] + + result = show_logs(CORE.config, args, devices) + + # Should succeed using the static IP even though MQTT failed + assert result == 0 + + # Verify MQTT was attempted + mock_mqtt_get_ip.assert_called_once_with(CORE.config, "user", "pass", "client") + + # Verify run_logs was called with only the static IP (MQTT failed) + mock_run_logs.assert_called_once_with(CORE.config, ["192.168.1.100"]) From 3054c2bc29149cb243ec9dac8a52e7f51e96e4d9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 16 Oct 2025 13:07:37 +1300 Subject: [PATCH 089/526] [ota.esphome] Handle blank password the same as no password defined (#11271) --- esphome/__main__.py | 2 +- esphome/components/esphome/ota/__init__.py | 6 ++++-- esphome/espota2.py | 10 +++++----- tests/unit_tests/test_espota2.py | 16 ++++++++-------- tests/unit_tests/test_main.py | 6 ++++-- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 89197737e3..d9bdfb175b 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -606,7 +606,7 @@ def upload_program( from esphome import espota2 remote_port = int(ota_conf[CONF_PORT]) - password = ota_conf.get(CONF_PASSWORD, "") + password = ota_conf.get(CONF_PASSWORD) if getattr(args, "file", None) is not None: binary = Path(args.file) else: diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index e6f249e021..69a50a2de9 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -19,6 +19,7 @@ from esphome.const import ( from esphome.core import CORE, coroutine_with_priority from esphome.coroutine import CoroPriority import esphome.final_validate as fv +from esphome.types import ConfigType _LOGGER = logging.getLogger(__name__) @@ -136,11 +137,12 @@ FINAL_VALIDATE_SCHEMA = ota_esphome_final_validate @coroutine_with_priority(CoroPriority.OTA_UPDATES) -async def to_code(config): +async def to_code(config: ConfigType) -> None: var = cg.new_Pvariable(config[CONF_ID]) cg.add(var.set_port(config[CONF_PORT])) - if CONF_PASSWORD in config: + # Password could be set to an empty string and we can assume that means no password + if config.get(CONF_PASSWORD): cg.add(var.set_auth_password(config[CONF_PASSWORD])) cg.add_define("USE_OTA_PASSWORD") # Only include hash algorithms when password is configured diff --git a/esphome/espota2.py b/esphome/espota2.py index 17a1da8235..2b1b9a8328 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -242,7 +242,7 @@ def send_check( def perform_ota( - sock: socket.socket, password: str, file_handle: io.IOBase, filename: Path + sock: socket.socket, password: str | None, file_handle: io.IOBase, filename: Path ) -> None: file_contents = file_handle.read() file_size = len(file_contents) @@ -278,13 +278,13 @@ def perform_ota( def perform_auth( sock: socket.socket, - password: str, + password: str | None, hash_func: Callable[..., Any], nonce_size: int, hash_name: str, ) -> None: """Perform challenge-response authentication using specified hash algorithm.""" - if not password: + if password is None: raise OTAError("ESP requests password, but no password given!") nonce_bytes = receive_exactly( @@ -385,7 +385,7 @@ def perform_ota( def run_ota_impl_( - remote_host: str | list[str], remote_port: int, password: str, filename: Path + remote_host: str | list[str], remote_port: int, password: str | None, filename: Path ) -> tuple[int, str | None]: from esphome.core import CORE @@ -436,7 +436,7 @@ def run_ota_impl_( def run_ota( - remote_host: str | list[str], remote_port: int, password: str, filename: Path + remote_host: str | list[str], remote_port: int, password: str | None, filename: Path ) -> tuple[int, str | None]: try: return run_ota_impl_(remote_host, remote_port, password, filename) diff --git a/tests/unit_tests/test_espota2.py b/tests/unit_tests/test_espota2.py index 52c72291d6..02f965782b 100644 --- a/tests/unit_tests/test_espota2.py +++ b/tests/unit_tests/test_espota2.py @@ -287,7 +287,7 @@ def test_perform_ota_no_auth(mock_socket: Mock, mock_file: io.BytesIO) -> None: mock_socket.recv.side_effect = recv_responses - espota2.perform_ota(mock_socket, "", mock_file, "test.bin") + espota2.perform_ota(mock_socket, None, mock_file, "test.bin") # Should not send any auth-related data auth_calls = [ @@ -317,7 +317,7 @@ def test_perform_ota_with_compression(mock_socket: Mock) -> None: mock_socket.recv.side_effect = recv_responses - espota2.perform_ota(mock_socket, "", mock_file, "test.bin") + espota2.perform_ota(mock_socket, None, mock_file, "test.bin") # Verify compressed content was sent # Get the binary size that was sent (4 bytes after features) @@ -347,7 +347,7 @@ def test_perform_ota_auth_without_password(mock_socket: Mock) -> None: with pytest.raises( espota2.OTAError, match="ESP requests password, but no password given" ): - espota2.perform_ota(mock_socket, "", mock_file, "test.bin") + espota2.perform_ota(mock_socket, None, mock_file, "test.bin") @pytest.mark.usefixtures("mock_time") @@ -413,7 +413,7 @@ def test_perform_ota_sha256_auth_without_password(mock_socket: Mock) -> None: with pytest.raises( espota2.OTAError, match="ESP requests password, but no password given" ): - espota2.perform_ota(mock_socket, "", mock_file, "test.bin") + espota2.perform_ota(mock_socket, None, mock_file, "test.bin") def test_perform_ota_unexpected_auth_response(mock_socket: Mock) -> None: @@ -450,7 +450,7 @@ def test_perform_ota_unsupported_version(mock_socket: Mock) -> None: mock_socket.recv.side_effect = responses with pytest.raises(espota2.OTAError, match="Device uses unsupported OTA version"): - espota2.perform_ota(mock_socket, "", mock_file, "test.bin") + espota2.perform_ota(mock_socket, None, mock_file, "test.bin") @pytest.mark.usefixtures("mock_time") @@ -471,7 +471,7 @@ def test_perform_ota_upload_error(mock_socket: Mock, mock_file: io.BytesIO) -> N mock_socket.recv.side_effect = recv_responses with pytest.raises(espota2.OTAError, match="Error receiving acknowledge chunk OK"): - espota2.perform_ota(mock_socket, "", mock_file, "test.bin") + espota2.perform_ota(mock_socket, None, mock_file, "test.bin") @pytest.mark.usefixtures("mock_socket_constructor", "mock_resolve_ip") @@ -706,7 +706,7 @@ def test_perform_ota_version_differences( ] mock_socket.recv.side_effect = recv_responses - espota2.perform_ota(mock_socket, "", mock_file, "test.bin") + espota2.perform_ota(mock_socket, None, mock_file, "test.bin") # For v1.0, verify that we only get the expected number of recv calls # v1.0 doesn't have chunk acknowledgments, so fewer recv calls @@ -732,7 +732,7 @@ def test_perform_ota_version_differences( ] mock_socket.recv.side_effect = recv_responses_v2 - espota2.perform_ota(mock_socket, "", mock_file, "test.bin") + espota2.perform_ota(mock_socket, None, mock_file, "test.bin") # For v2.0, verify more recv calls due to chunk acknowledgments assert mock_socket.recv.call_count == 9 # v2.0 has 9 recv calls (includes chunk OK) diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index 422a199496..1782a1e9e1 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -1062,7 +1062,7 @@ def test_upload_program_ota_with_file_arg( assert exit_code == 0 assert host == "192.168.1.100" mock_run_ota.assert_called_once_with( - ["192.168.1.100"], 3232, "", Path("custom.bin") + ["192.168.1.100"], 3232, None, Path("custom.bin") ) @@ -1119,7 +1119,9 @@ def test_upload_program_ota_with_mqtt_resolution( expected_firmware = ( tmp_path / ".esphome" / "build" / "test" / ".pioenvs" / "test" / "firmware.bin" ) - mock_run_ota.assert_called_once_with(["192.168.1.100"], 3232, "", expected_firmware) + mock_run_ota.assert_called_once_with( + ["192.168.1.100"], 3232, None, expected_firmware + ) @patch("esphome.__main__.importlib.import_module") From fe4857fabbb98568e4815fd84c021dae5d5b127b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 14:28:19 -1000 Subject: [PATCH 090/526] [tests] Fix OTA password test assertions after merge collision (#11275) --- tests/unit_tests/test_main.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index 1782a1e9e1..59d0433aa4 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -2025,7 +2025,7 @@ def test_upload_program_ota_static_ip_with_mqttip( tmp_path / ".esphome" / "build" / "test" / ".pioenvs" / "test" / "firmware.bin" ) mock_run_ota.assert_called_once_with( - ["192.168.1.100", "192.168.2.50"], 3232, "", expected_firmware + ["192.168.1.100", "192.168.2.50"], 3232, None, expected_firmware ) @@ -2068,7 +2068,7 @@ def test_upload_program_ota_multiple_mqttip_resolves_once( tmp_path / ".esphome" / "build" / "test" / ".pioenvs" / "test" / "firmware.bin" ) mock_run_ota.assert_called_once_with( - ["192.168.2.50", "192.168.2.51", "192.168.1.100"], 3232, "", expected_firmware + ["192.168.2.50", "192.168.2.51", "192.168.1.100"], 3232, None, expected_firmware ) @@ -2230,7 +2230,9 @@ def test_upload_program_ota_mqtt_timeout_fallback( expected_firmware = ( tmp_path / ".esphome" / "build" / "test" / ".pioenvs" / "test" / "firmware.bin" ) - mock_run_ota.assert_called_once_with(["192.168.1.100"], 3232, "", expected_firmware) + mock_run_ota.assert_called_once_with( + ["192.168.1.100"], 3232, None, expected_firmware + ) @patch("esphome.components.api.client.run_logs") From 22056e08099582395fb2f15318593b3f5e1d9da4 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 15 Oct 2025 21:24:56 -0400 Subject: [PATCH 091/526] [wifi] Fix enterprise wifi (#11276) Co-authored-by: J. Nick Koston --- esphome/components/wifi/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index ad5698519b..1f742dc1a8 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -402,8 +402,8 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False) # Disable Enterprise WiFi support if no EAP is configured - if CORE.is_esp32 and not has_eap: - add_idf_sdkconfig_option("CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT", False) + if CORE.is_esp32: + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT", has_eap) cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE])) From 10ca86ae8dc1daa7e74794135f047e614343eee6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 16:41:25 -1000 Subject: [PATCH 092/526] [api] Use std::unique_ptr for fixed-size byte buffers in Noise protocol (#11278) --- .../components/api/api_frame_helper_noise.cpp | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/esphome/components/api/api_frame_helper_noise.cpp b/esphome/components/api/api_frame_helper_noise.cpp index 1213e65948..e952ea670b 100644 --- a/esphome/components/api/api_frame_helper_noise.cpp +++ b/esphome/components/api/api_frame_helper_noise.cpp @@ -242,7 +242,6 @@ APIError APINoiseFrameHelper::state_action_() { const std::string &name = App.get_name(); const std::string &mac = get_mac_address(); - std::vector msg; // Calculate positions and sizes size_t name_len = name.size() + 1; // including null terminator size_t mac_len = mac.size() + 1; // including null terminator @@ -250,17 +249,17 @@ APIError APINoiseFrameHelper::state_action_() { size_t mac_offset = name_offset + name_len; size_t total_size = 1 + name_len + mac_len; - msg.resize(total_size); + auto msg = std::make_unique(total_size); // chosen proto msg[0] = 0x01; // node name, terminated by null byte - std::memcpy(msg.data() + name_offset, name.c_str(), name_len); + std::memcpy(msg.get() + name_offset, name.c_str(), name_len); // node mac, terminated by null byte - std::memcpy(msg.data() + mac_offset, mac.c_str(), mac_len); + std::memcpy(msg.get() + mac_offset, mac.c_str(), mac_len); - aerr = write_frame_(msg.data(), msg.size()); + aerr = write_frame_(msg.get(), total_size); if (aerr != APIError::OK) return aerr; @@ -339,32 +338,32 @@ void APINoiseFrameHelper::send_explicit_handshake_reject_(const LogString *reaso #ifdef USE_STORE_LOG_STR_IN_FLASH // On ESP8266 with flash strings, we need to use PROGMEM-aware functions size_t reason_len = strlen_P(reinterpret_cast(reason)); - std::vector data; - data.resize(reason_len + 1); + size_t data_size = reason_len + 1; + auto data = std::make_unique(data_size); data[0] = 0x01; // failure // Copy error message from PROGMEM if (reason_len > 0) { - memcpy_P(data.data() + 1, reinterpret_cast(reason), reason_len); + memcpy_P(data.get() + 1, reinterpret_cast(reason), reason_len); } #else // Normal memory access const char *reason_str = LOG_STR_ARG(reason); size_t reason_len = strlen(reason_str); - std::vector data; - data.resize(reason_len + 1); + size_t data_size = reason_len + 1; + auto data = std::make_unique(data_size); data[0] = 0x01; // failure // Copy error message in bulk if (reason_len > 0) { - std::memcpy(data.data() + 1, reason_str, reason_len); + std::memcpy(data.get() + 1, reason_str, reason_len); } #endif // temporarily remove failed state auto orig_state = state_; state_ = State::EXPLICIT_REJECT; - write_frame_(data.data(), data.size()); + write_frame_(data.get(), data_size); state_ = orig_state; } APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) { From 708f8a95e5dfdb00be84ebc3aa524749a392dc04 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 16:48:38 -1000 Subject: [PATCH 093/526] [api] Use FixedVector for HomeAssistantServiceCallAction to reduce flash usage and avoid realloc (#11277) --- esphome/components/api/__init__.py | 17 +++++++++ .../components/api/homeassistant_service.h | 36 ++++++++++++++----- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 58828c131d..e8dacf51bc 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -380,12 +380,19 @@ async def homeassistant_service_to_code( var = cg.new_Pvariable(action_id, template_arg, serv, False) templ = await cg.templatable(config[CONF_ACTION], args, None) cg.add(var.set_service(templ)) + + # Initialize FixedVectors with exact sizes from config + cg.add(var.init_data(len(config[CONF_DATA]))) for key, value in config[CONF_DATA].items(): templ = await cg.templatable(value, args, None) cg.add(var.add_data(key, templ)) + + cg.add(var.init_data_template(len(config[CONF_DATA_TEMPLATE]))) for key, value in config[CONF_DATA_TEMPLATE].items(): templ = await cg.templatable(value, args, None) cg.add(var.add_data_template(key, templ)) + + cg.add(var.init_variables(len(config[CONF_VARIABLES]))) for key, value in config[CONF_VARIABLES].items(): templ = await cg.templatable(value, args, None) cg.add(var.add_variable(key, templ)) @@ -458,15 +465,23 @@ async def homeassistant_event_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg, serv, True) templ = await cg.templatable(config[CONF_EVENT], args, None) cg.add(var.set_service(templ)) + + # Initialize FixedVectors with exact sizes from config + cg.add(var.init_data(len(config[CONF_DATA]))) for key, value in config[CONF_DATA].items(): templ = await cg.templatable(value, args, None) cg.add(var.add_data(key, templ)) + + cg.add(var.init_data_template(len(config[CONF_DATA_TEMPLATE]))) for key, value in config[CONF_DATA_TEMPLATE].items(): templ = await cg.templatable(value, args, None) cg.add(var.add_data_template(key, templ)) + + cg.add(var.init_variables(len(config[CONF_VARIABLES]))) for key, value in config[CONF_VARIABLES].items(): templ = await cg.templatable(value, args, None) cg.add(var.add_variable(key, templ)) + return var @@ -489,6 +504,8 @@ async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, arg serv = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, serv, True) cg.add(var.set_service("esphome.tag_scanned")) + # Initialize FixedVector with exact size (1 data field) + cg.add(var.init_data(1)) templ = await cg.templatable(config[CONF_TAG], args, cg.std_string) cg.add(var.add_data("tag_id", templ)) return var diff --git a/esphome/components/api/homeassistant_service.h b/esphome/components/api/homeassistant_service.h index 46e89cb39f..4343fcd0bb 100644 --- a/esphome/components/api/homeassistant_service.h +++ b/esphome/components/api/homeassistant_service.h @@ -41,10 +41,14 @@ template class TemplatableStringValue : public TemplatableValue class TemplatableKeyValuePair { public: + // Default constructor needed for FixedVector::emplace_back() + TemplatableKeyValuePair() = default; + // Keys are always string literals from YAML dictionary keys (e.g., "code", "event") // and never templatable values or lambdas. Only the value parameter can be a lambda/template. // Using pass-by-value with std::move allows optimal performance for both lvalues and rvalues. template TemplatableKeyValuePair(std::string key, T value) : key(std::move(key)), value(value) {} + std::string key; TemplatableStringValue value; }; @@ -93,15 +97,22 @@ template class HomeAssistantServiceCallAction : public Action void set_service(T service) { this->service_ = service; } + // Initialize FixedVector members - called from Python codegen with compile-time known sizes. + // Must be called before any add_* methods; capacity must match the number of subsequent add_* calls. + void init_data(size_t count) { this->data_.init(count); } + void init_data_template(size_t count) { this->data_template_.init(count); } + void init_variables(size_t count) { this->variables_.init(count); } + // Keys are always string literals from the Python code generation (e.g., cg.add(var.add_data("tag_id", templ))). // The value parameter can be a lambda/template, but keys are never templatable. - // Using pass-by-value allows the compiler to optimize for both lvalues and rvalues. - template void add_data(std::string key, T value) { this->data_.emplace_back(std::move(key), value); } - template void add_data_template(std::string key, T value) { - this->data_template_.emplace_back(std::move(key), value); + template void add_data(K &&key, V &&value) { + this->add_kv_(this->data_, std::forward(key), std::forward(value)); } - template void add_variable(std::string key, T value) { - this->variables_.emplace_back(std::move(key), value); + template void add_data_template(K &&key, V &&value) { + this->add_kv_(this->data_template_, std::forward(key), std::forward(value)); + } + template void add_variable(K &&key, V &&value) { + this->add_kv_(this->variables_, std::forward(key), std::forward(value)); } #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES @@ -174,6 +185,13 @@ template class HomeAssistantServiceCallAction : public Action void add_kv_(FixedVector> &vec, K &&key, V &&value) { + auto &kv = vec.emplace_back(); + kv.key = std::forward(key); + kv.value = std::forward(value); + } + template static void populate_service_map(VectorType &dest, SourceType &source, Ts... x) { dest.init(source.size()); @@ -186,9 +204,9 @@ template class HomeAssistantServiceCallAction : public Action service_{}; - std::vector> data_; - std::vector> data_template_; - std::vector> variables_; + FixedVector> data_; + FixedVector> data_template_; + FixedVector> variables_; #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON TemplatableStringValue response_template_{""}; From 42f1b61e315aa557d3242d2d5f50b1edb4de220e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 16:58:58 -1000 Subject: [PATCH 094/526] [git] Automatically recover from broken git repositories in external_components (#11246) --- esphome/git.py | 150 +++++++++-- tests/unit_tests/conftest.py | 7 + tests/unit_tests/test_git.py | 499 ++++++++++++++++++++++++++++++++--- 3 files changed, 594 insertions(+), 62 deletions(-) diff --git a/esphome/git.py b/esphome/git.py index 62fe37a3fe..4ff07ffe75 100644 --- a/esphome/git.py +++ b/esphome/git.py @@ -5,6 +5,7 @@ import hashlib import logging from pathlib import Path import re +import shutil import subprocess import urllib.parse @@ -17,14 +18,60 @@ _LOGGER = logging.getLogger(__name__) NEVER_REFRESH = TimePeriodSeconds(seconds=-1) -def run_git_command(cmd, cwd=None) -> str: - _LOGGER.debug("Running git command: %s", " ".join(cmd)) +class GitException(cv.Invalid): + """Base exception for git-related errors.""" + + +class GitNotInstalledError(GitException): + """Exception raised when git is not installed on the system.""" + + +class GitCommandError(GitException): + """Exception raised when a git command fails.""" + + +class GitRepositoryError(GitException): + """Exception raised when a git repository is in an invalid state.""" + + +def run_git_command(cmd: list[str], git_dir: Path | None = None) -> str: + if git_dir is not None: + _LOGGER.debug( + "Running git command with repository isolation: %s (git_dir=%s)", + " ".join(cmd), + git_dir, + ) + else: + _LOGGER.debug("Running git command: %s", " ".join(cmd)) + + # Set up environment for repository isolation if git_dir is provided + # Force git to only operate on this specific repository by setting + # GIT_DIR and GIT_WORK_TREE. This prevents git from walking up the + # directory tree to find parent repositories when the target repo's + # .git directory is corrupt. Without this, commands like 'git stash' + # could accidentally operate on parent repositories (e.g., the main + # ESPHome repo) instead of failing, causing data loss. + env: dict[str, str] | None = None + cwd: str | None = None + if git_dir is not None: + env = { + **subprocess.os.environ, + "GIT_DIR": str(Path(git_dir) / ".git"), + "GIT_WORK_TREE": str(git_dir), + } + cwd = str(git_dir) + try: ret = subprocess.run( - cmd, cwd=cwd, capture_output=True, check=False, close_fds=False + cmd, + cwd=cwd, + capture_output=True, + check=False, + close_fds=False, + env=env, ) except FileNotFoundError as err: - raise cv.Invalid( + raise GitNotInstalledError( "git is not installed but required for external_components.\n" "Please see https://git-scm.com/book/en/v2/Getting-Started-Installing-Git for installing git" ) from err @@ -33,8 +80,8 @@ def run_git_command(cmd, cwd=None) -> str: err_str = ret.stderr.decode("utf-8") lines = [x.strip() for x in err_str.splitlines()] if lines[-1].startswith("fatal:"): - raise cv.Invalid(lines[-1][len("fatal: ") :]) - raise cv.Invalid(err_str) + raise GitCommandError(lines[-1][len("fatal: ") :]) + raise GitCommandError(err_str) return ret.stdout.decode("utf-8").strip() @@ -55,6 +102,7 @@ def clone_or_update( username: str = None, password: str = None, submodules: list[str] | None = None, + _recover_broken: bool = True, ) -> tuple[Path, Callable[[], None] | None]: key = f"{url}@{ref}" @@ -75,15 +123,15 @@ def clone_or_update( # We need to fetch the PR branch first, otherwise git will complain # about missing objects _LOGGER.info("Fetching %s", ref) - run_git_command(["git", "fetch", "--", "origin", ref], str(repo_dir)) - run_git_command(["git", "reset", "--hard", "FETCH_HEAD"], str(repo_dir)) + run_git_command(["git", "fetch", "--", "origin", ref], git_dir=repo_dir) + run_git_command(["git", "reset", "--hard", "FETCH_HEAD"], git_dir=repo_dir) if submodules is not None: _LOGGER.info( - "Initialising submodules (%s) for %s", ", ".join(submodules), key + "Initializing submodules (%s) for %s", ", ".join(submodules), key ) run_git_command( - ["git", "submodule", "update", "--init"] + submodules, str(repo_dir) + ["git", "submodule", "update", "--init"] + submodules, git_dir=repo_dir ) else: @@ -99,32 +147,82 @@ def clone_or_update( file_timestamp = Path(repo_dir / ".git" / "HEAD") age = datetime.now() - datetime.fromtimestamp(file_timestamp.stat().st_mtime) if refresh is None or age.total_seconds() > refresh.total_seconds: - old_sha = run_git_command(["git", "rev-parse", "HEAD"], str(repo_dir)) - _LOGGER.info("Updating %s", key) - _LOGGER.debug("Location: %s", repo_dir) - # Stash local changes (if any) - run_git_command( - ["git", "stash", "push", "--include-untracked"], str(repo_dir) - ) - # Fetch remote ref - cmd = ["git", "fetch", "--", "origin"] - if ref is not None: - cmd.append(ref) - run_git_command(cmd, str(repo_dir)) - # Hard reset to FETCH_HEAD (short-lived git ref corresponding to most recent fetch) - run_git_command(["git", "reset", "--hard", "FETCH_HEAD"], str(repo_dir)) + # Try to update the repository, recovering from broken state if needed + old_sha: str | None = None + try: + # First verify the repository is valid by checking HEAD + # Use git_dir parameter to prevent git from walking up to parent repos + old_sha = run_git_command( + ["git", "rev-parse", "HEAD"], git_dir=repo_dir + ) + + _LOGGER.info("Updating %s", key) + _LOGGER.debug("Location: %s", repo_dir) + + # Stash local changes (if any) + # Use git_dir to ensure this only affects the specific repo + run_git_command( + ["git", "stash", "push", "--include-untracked"], + git_dir=repo_dir, + ) + + # Fetch remote ref + cmd = ["git", "fetch", "--", "origin"] + if ref is not None: + cmd.append(ref) + run_git_command(cmd, git_dir=repo_dir) + + # Hard reset to FETCH_HEAD (short-lived git ref corresponding to most recent fetch) + run_git_command( + ["git", "reset", "--hard", "FETCH_HEAD"], + git_dir=repo_dir, + ) + except GitException as err: + # Repository is in a broken state or update failed + # Only attempt recovery once to prevent infinite recursion + if not _recover_broken: + _LOGGER.error( + "Repository %s recovery failed, cannot retry (already attempted once)", + key, + ) + raise + + _LOGGER.warning( + "Repository %s has issues (%s), attempting recovery", + key, + err, + ) + _LOGGER.info("Removing broken repository at %s", repo_dir) + shutil.rmtree(repo_dir) + _LOGGER.info("Successfully removed broken repository, re-cloning...") + + # Recursively call clone_or_update to re-clone + # Set _recover_broken=False to prevent infinite recursion + result = clone_or_update( + url=url, + ref=ref, + refresh=refresh, + domain=domain, + username=username, + password=password, + submodules=submodules, + _recover_broken=False, + ) + _LOGGER.info("Repository %s successfully recovered", key) + return result if submodules is not None: _LOGGER.info( "Updating submodules (%s) for %s", ", ".join(submodules), key ) run_git_command( - ["git", "submodule", "update", "--init"] + submodules, str(repo_dir) + ["git", "submodule", "update", "--init"] + submodules, + git_dir=repo_dir, ) def revert(): _LOGGER.info("Reverting changes to %s -> %s", key, old_sha) - run_git_command(["git", "reset", "--hard", old_sha], str(repo_dir)) + run_git_command(["git", "reset", "--hard", old_sha], git_dir=repo_dir) return repo_dir, revert diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index 932221997c..fc61841500 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -96,6 +96,13 @@ def mock_run_git_command() -> Generator[Mock, None, None]: yield mock +@pytest.fixture +def mock_subprocess_run() -> Generator[Mock, None, None]: + """Mock subprocess.run for testing.""" + with patch("subprocess.run") as mock: + yield mock + + @pytest.fixture def mock_get_idedata() -> Generator[Mock, None, None]: """Mock get_idedata for platformio_api.""" diff --git a/tests/unit_tests/test_git.py b/tests/unit_tests/test_git.py index 6a51206ec2..0411fe5e43 100644 --- a/tests/unit_tests/test_git.py +++ b/tests/unit_tests/test_git.py @@ -1,13 +1,204 @@ """Tests for git.py module.""" from datetime import datetime, timedelta -import hashlib import os from pathlib import Path +from typing import Any from unittest.mock import Mock +import pytest + from esphome import git from esphome.core import CORE, TimePeriodSeconds +from esphome.git import GitCommandError + + +def _compute_repo_dir(url: str, ref: str | None, domain: str) -> Path: + """Helper to compute the expected repo directory path using git module's logic.""" + key = f"{url}@{ref}" + return git._compute_destination_path(key, domain) + + +def _setup_old_repo(repo_dir: Path, days_old: int = 2) -> None: + """Helper to set up a git repo directory structure with an old timestamp. + + Args: + repo_dir: The repository directory path to create. + days_old: Number of days old to make the FETCH_HEAD file (default: 2). + """ + # Create repo directory + repo_dir.mkdir(parents=True) + git_dir = repo_dir / ".git" + git_dir.mkdir() + + # Create FETCH_HEAD file with old timestamp + fetch_head = git_dir / "FETCH_HEAD" + fetch_head.write_text("test") + old_time = datetime.now() - timedelta(days=days_old) + fetch_head.touch() + os.utime(fetch_head, (old_time.timestamp(), old_time.timestamp())) + + +def _get_git_command_type(cmd: list[str]) -> str | None: + """Helper to determine the type of git command from a command list. + + Args: + cmd: The git command list (e.g., ["git", "rev-parse", "HEAD"]). + + Returns: + The command type ("rev-parse", "stash", "fetch", "reset", "clone") or None. + """ + # Git commands are always in format ["git", "command", ...], so check index 1 + if len(cmd) > 1: + return cmd[1] + return None + + +def test_run_git_command_success(tmp_path: Path) -> None: + """Test that run_git_command returns output on success.""" + # Create a simple git repo to test with + repo_dir = tmp_path / "test_repo" + repo_dir.mkdir() + + # Initialize a git repo + result = git.run_git_command(["git", "init"], str(repo_dir)) + assert "Initialized empty Git repository" in result or result == "" + + # Verify we can run a command and get output + result = git.run_git_command(["git", "status", "--porcelain"], str(repo_dir)) + # Empty repo should have empty status + assert isinstance(result, str) + + +def test_run_git_command_with_git_dir_isolation( + tmp_path: Path, mock_subprocess_run: Mock +) -> None: + """Test that git_dir parameter properly isolates git operations.""" + repo_dir = tmp_path / "test_repo" + repo_dir.mkdir() + git_dir = repo_dir / ".git" + git_dir.mkdir() + + # Configure mock to return success + mock_subprocess_run.return_value = Mock( + returncode=0, + stdout=b"test output", + stderr=b"", + ) + + result = git.run_git_command( + ["git", "rev-parse", "HEAD"], + git_dir=repo_dir, + ) + + # Verify subprocess.run was called + assert mock_subprocess_run.called + call_args = mock_subprocess_run.call_args + + # Verify environment was set + env = call_args[1]["env"] + assert "GIT_DIR" in env + assert "GIT_WORK_TREE" in env + assert env["GIT_DIR"] == str(repo_dir / ".git") + assert env["GIT_WORK_TREE"] == str(repo_dir) + + assert result == "test output" + + +def test_run_git_command_raises_git_not_installed_error( + tmp_path: Path, mock_subprocess_run: Mock +) -> None: + """Test that FileNotFoundError is converted to GitNotInstalledError.""" + from esphome.git import GitNotInstalledError + + repo_dir = tmp_path / "test_repo" + + # Configure mock to raise FileNotFoundError + mock_subprocess_run.side_effect = FileNotFoundError("git not found") + + with pytest.raises(GitNotInstalledError, match="git is not installed"): + git.run_git_command(["git", "status"], git_dir=repo_dir) + + +def test_run_git_command_raises_git_command_error_on_failure( + tmp_path: Path, mock_subprocess_run: Mock +) -> None: + """Test that failed git commands raise GitCommandError.""" + repo_dir = tmp_path / "test_repo" + + # Configure mock to return non-zero exit code + mock_subprocess_run.return_value = Mock( + returncode=1, + stdout=b"", + stderr=b"fatal: not a git repository", + ) + + with pytest.raises(GitCommandError, match="not a git repository"): + git.run_git_command(["git", "status"], git_dir=repo_dir) + + +def test_run_git_command_strips_fatal_prefix( + tmp_path: Path, mock_subprocess_run: Mock +) -> None: + """Test that 'fatal: ' prefix is stripped from error messages.""" + repo_dir = tmp_path / "test_repo" + + # Configure mock to return error with "fatal: " prefix + mock_subprocess_run.return_value = Mock( + returncode=128, + stdout=b"", + stderr=b"fatal: repository not found\n", + ) + + with pytest.raises(GitCommandError) as exc_info: + git.run_git_command(["git", "clone", "invalid-url"], git_dir=repo_dir) + + # Error message should NOT include "fatal: " prefix + assert "fatal:" not in str(exc_info.value) + assert "repository not found" in str(exc_info.value) + + +def test_run_git_command_without_git_dir(mock_subprocess_run: Mock) -> None: + """Test that run_git_command works without git_dir (clone case).""" + # Configure mock to return success + mock_subprocess_run.return_value = Mock( + returncode=0, + stdout=b"Cloning into 'test_repo'...", + stderr=b"", + ) + + result = git.run_git_command(["git", "clone", "https://github.com/test/repo"]) + + # Verify subprocess.run was called + assert mock_subprocess_run.called + call_args = mock_subprocess_run.call_args + + # Verify environment does NOT have GIT_DIR or GIT_WORK_TREE set + # (it should use the default environment or None) + env = call_args[1].get("env") + if env is not None: + assert "GIT_DIR" not in env + assert "GIT_WORK_TREE" not in env + + # Verify cwd is None (default) + assert call_args[1].get("cwd") is None + + assert result == "Cloning into 'test_repo'..." + + +def test_run_git_command_without_git_dir_raises_error( + mock_subprocess_run: Mock, +) -> None: + """Test that run_git_command without git_dir can still raise errors.""" + # Configure mock to return error + mock_subprocess_run.return_value = Mock( + returncode=128, + stdout=b"", + stderr=b"fatal: repository not found\n", + ) + + with pytest.raises(GitCommandError, match="repository not found"): + git.run_git_command(["git", "clone", "https://invalid.url/repo.git"]) def test_clone_or_update_with_never_refresh( @@ -17,16 +208,10 @@ def test_clone_or_update_with_never_refresh( # Set up CORE.config_path so data_dir uses tmp_path CORE.config_path = tmp_path / "test.yaml" - # Compute the expected repo directory path url = "https://github.com/test/repo" ref = None - key = f"{url}@{ref}" domain = "test" - - # Compute hash-based directory name (matching _compute_destination_path logic) - h = hashlib.new("sha256") - h.update(key.encode()) - repo_dir = tmp_path / ".esphome" / domain / h.hexdigest()[:8] + repo_dir = _compute_repo_dir(url, ref, domain) # Create the git repo directory structure repo_dir.mkdir(parents=True) @@ -58,16 +243,10 @@ def test_clone_or_update_with_refresh_updates_old_repo( # Set up CORE.config_path so data_dir uses tmp_path CORE.config_path = tmp_path / "test.yaml" - # Compute the expected repo directory path url = "https://github.com/test/repo" ref = None - key = f"{url}@{ref}" domain = "test" - - # Compute hash-based directory name (matching _compute_destination_path logic) - h = hashlib.new("sha256") - h.update(key.encode()) - repo_dir = tmp_path / ".esphome" / domain / h.hexdigest()[:8] + repo_dir = _compute_repo_dir(url, ref, domain) # Create the git repo directory structure repo_dir.mkdir(parents=True) @@ -112,16 +291,10 @@ def test_clone_or_update_with_refresh_skips_fresh_repo( # Set up CORE.config_path so data_dir uses tmp_path CORE.config_path = tmp_path / "test.yaml" - # Compute the expected repo directory path url = "https://github.com/test/repo" ref = None - key = f"{url}@{ref}" domain = "test" - - # Compute hash-based directory name (matching _compute_destination_path logic) - h = hashlib.new("sha256") - h.update(key.encode()) - repo_dir = tmp_path / ".esphome" / domain / h.hexdigest()[:8] + repo_dir = _compute_repo_dir(url, ref, domain) # Create the git repo directory structure repo_dir.mkdir(parents=True) @@ -158,16 +331,10 @@ def test_clone_or_update_clones_missing_repo( # Set up CORE.config_path so data_dir uses tmp_path CORE.config_path = tmp_path / "test.yaml" - # Compute the expected repo directory path url = "https://github.com/test/repo" ref = None - key = f"{url}@{ref}" domain = "test" - - # Compute hash-based directory name (matching _compute_destination_path logic) - h = hashlib.new("sha256") - h.update(key.encode()) - repo_dir = tmp_path / ".esphome" / domain / h.hexdigest()[:8] + repo_dir = _compute_repo_dir(url, ref, domain) # Create base directory but NOT the repo itself base_dir = tmp_path / ".esphome" / domain @@ -200,16 +367,10 @@ def test_clone_or_update_with_none_refresh_always_updates( # Set up CORE.config_path so data_dir uses tmp_path CORE.config_path = tmp_path / "test.yaml" - # Compute the expected repo directory path url = "https://github.com/test/repo" ref = None - key = f"{url}@{ref}" domain = "test" - - # Compute hash-based directory name (matching _compute_destination_path logic) - h = hashlib.new("sha256") - h.update(key.encode()) - repo_dir = tmp_path / ".esphome" / domain / h.hexdigest()[:8] + repo_dir = _compute_repo_dir(url, ref, domain) # Create the git repo directory structure repo_dir.mkdir(parents=True) @@ -244,3 +405,269 @@ def test_clone_or_update_with_none_refresh_always_updates( if len(call[0]) > 0 and "fetch" in call[0][0] ] assert len(fetch_calls) > 0 + + +@pytest.mark.parametrize( + ("fail_command", "error_message"), + [ + ( + "rev-parse", + "ambiguous argument 'HEAD': unknown revision or path not in the working tree.", + ), + ("stash", "fatal: unable to write new index file"), + ( + "fetch", + "fatal: unable to access 'https://github.com/test/repo/': Could not resolve host", + ), + ("reset", "fatal: Could not reset index file to revision 'FETCH_HEAD'"), + ], +) +def test_clone_or_update_recovers_from_git_failures( + tmp_path: Path, mock_run_git_command: Mock, fail_command: str, error_message: str +) -> None: + """Test that repos are re-cloned when various git commands fail.""" + # Set up CORE.config_path so data_dir uses tmp_path + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + ref = "main" + domain = "test" + repo_dir = _compute_repo_dir(url, ref, domain) + + # Use helper to set up old repo + _setup_old_repo(repo_dir) + + # Track command call counts to make first call fail, subsequent calls succeed + call_counts: dict[str, int] = {} + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + # Determine which command this is + cmd_type = _get_git_command_type(cmd) + + # Track call count for this command type + if cmd_type: + call_counts[cmd_type] = call_counts.get(cmd_type, 0) + 1 + + # Fail on first call to the specified command, succeed on subsequent calls + if cmd_type == fail_command and call_counts[cmd_type] == 1: + raise GitCommandError(error_message) + + # Default successful responses + if cmd_type == "rev-parse": + return "abc123" + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + refresh = TimePeriodSeconds(days=1) + result_dir, revert = git.clone_or_update( + url=url, + ref=ref, + refresh=refresh, + domain=domain, + ) + + # Verify recovery happened + call_list = mock_run_git_command.call_args_list + + # Should have attempted the failing command + assert any(fail_command in str(c) for c in call_list) + + # Should have called clone for recovery + assert any("clone" in str(c) for c in call_list) + + # Verify the repo directory path is returned + assert result_dir == repo_dir + + +def test_clone_or_update_fails_when_recovery_also_fails( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """Test that we don't infinitely recurse when recovery also fails.""" + # Set up CORE.config_path so data_dir uses tmp_path + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + ref = "main" + domain = "test" + repo_dir = _compute_repo_dir(url, ref, domain) + + # Use helper to set up old repo + _setup_old_repo(repo_dir) + + # Mock git command to fail on clone (simulating network failure during recovery) + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + cmd_type = _get_git_command_type(cmd) + if cmd_type == "rev-parse": + # First time fails (broken repo) + raise GitCommandError( + "ambiguous argument 'HEAD': unknown revision or path not in the working tree." + ) + if cmd_type == "clone": + # Clone also fails (recovery fails) + raise GitCommandError("fatal: unable to access repository") + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + refresh = TimePeriodSeconds(days=1) + + # Should raise after one recovery attempt fails + with pytest.raises(GitCommandError, match="fatal: unable to access repository"): + git.clone_or_update( + url=url, + ref=ref, + refresh=refresh, + domain=domain, + ) + + # Verify we only tried to clone once (no infinite recursion) + call_list = mock_run_git_command.call_args_list + clone_calls = [c for c in call_list if "clone" in c[0][0]] + # Should have exactly one clone call (the recovery attempt that failed) + assert len(clone_calls) == 1 + # Should have tried rev-parse once (which failed and triggered recovery) + rev_parse_calls = [c for c in call_list if "rev-parse" in c[0][0]] + assert len(rev_parse_calls) == 1 + + +def test_clone_or_update_recover_broken_flag_prevents_second_recovery( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """Test that _recover_broken=False prevents a second recovery attempt (tests the raise path).""" + # Set up CORE.config_path so data_dir uses tmp_path + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + ref = "main" + domain = "test" + repo_dir = _compute_repo_dir(url, ref, domain) + + # Use helper to set up old repo + _setup_old_repo(repo_dir) + + # Track fetch calls to differentiate between first (in clone) and second (in recovery update) + call_counts: dict[str, int] = {} + + # Mock git command to fail on fetch during recovery's ref checkout + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + cmd_type = _get_git_command_type(cmd) + + if cmd_type: + call_counts[cmd_type] = call_counts.get(cmd_type, 0) + 1 + + # First attempt: rev-parse fails (broken repo) + if cmd_type == "rev-parse" and call_counts[cmd_type] == 1: + raise GitCommandError( + "ambiguous argument 'HEAD': unknown revision or path not in the working tree." + ) + + # Recovery: clone succeeds + if cmd_type == "clone": + return "" + + # Recovery: fetch for ref checkout fails + # This happens in the clone path when ref is not None (line 80 in git.py) + if cmd_type == "fetch" and call_counts[cmd_type] == 1: + raise GitCommandError("fatal: couldn't find remote ref main") + + # Default success + return "abc123" if cmd_type == "rev-parse" else "" + + mock_run_git_command.side_effect = git_command_side_effect + + refresh = TimePeriodSeconds(days=1) + + # Should raise on the fetch during recovery (when _recover_broken=False) + # This tests the critical "if not _recover_broken: raise" path + with pytest.raises(GitCommandError, match="fatal: couldn't find remote ref main"): + git.clone_or_update( + url=url, + ref=ref, + refresh=refresh, + domain=domain, + ) + + # Verify the sequence of events + call_list = mock_run_git_command.call_args_list + + # Should have: rev-parse (fail, triggers recovery), clone (success), + # fetch (fail during ref checkout, raises because _recover_broken=False) + rev_parse_calls = [c for c in call_list if "rev-parse" in c[0][0]] + # Should have exactly one rev-parse call that failed + assert len(rev_parse_calls) == 1 + + clone_calls = [c for c in call_list if "clone" in c[0][0]] + # Should have exactly one clone call (the recovery attempt) + assert len(clone_calls) == 1 + + fetch_calls = [c for c in call_list if "fetch" in c[0][0]] + # Should have exactly one fetch call that failed (during ref checkout in recovery) + assert len(fetch_calls) == 1 + + +def test_clone_or_update_recover_broken_flag_prevents_infinite_loop( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """Test that _recover_broken=False prevents infinite recursion when repo persists.""" + # This tests the critical "if not _recover_broken: raise" path at line 124-125 + # Set up CORE.config_path so data_dir uses tmp_path + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + ref = "main" + domain = "test" + repo_dir = _compute_repo_dir(url, ref, domain) + + # Use helper to set up old repo + _setup_old_repo(repo_dir) + + # Mock shutil.rmtree to NOT actually delete the directory + # This simulates a scenario where deletion fails (permissions, etc.) + import unittest.mock + + def mock_rmtree(path, *args, **kwargs): + # Don't actually delete - this causes the recursive call to still see the repo + pass + + # Mock git commands to always fail on stash + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + cmd_type = _get_git_command_type(cmd) + if cmd_type == "rev-parse": + return "abc123" + if cmd_type == "stash": + # Always fails + raise GitCommandError("fatal: unable to write new index file") + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + refresh = TimePeriodSeconds(days=1) + + # Mock shutil.rmtree and test + # Should raise on the second attempt when _recover_broken=False + # This hits the "if not _recover_broken: raise" path + with ( + unittest.mock.patch("esphome.git.shutil.rmtree", side_effect=mock_rmtree), + pytest.raises(GitCommandError, match="fatal: unable to write new index file"), + ): + git.clone_or_update( + url=url, + ref=ref, + refresh=refresh, + domain=domain, + ) + + # Verify the sequence: stash fails twice (once triggering recovery, once raising) + call_list = mock_run_git_command.call_args_list + stash_calls = [c for c in call_list if "stash" in c[0][0]] + # Should have exactly two stash calls + assert len(stash_calls) == 2 From 236ca12d3e98c62fbb6694f6b20047ddb814554e Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 15 Oct 2025 21:59:55 -0500 Subject: [PATCH 095/526] [api, climate, thermostat] Implement feature_flags for `climate` (#10987) Co-authored-by: J. Nick Koston --- esphome/components/api/api.proto | 11 ++- esphome/components/api/api_connection.cpp | 19 ++-- esphome/components/api/api_pb2.cpp | 2 + esphome/components/api/api_pb2.h | 3 +- esphome/components/api/api_pb2_dump.cpp | 1 + esphome/components/climate/climate.cpp | 48 +++++---- esphome/components/climate/climate_mode.h | 15 +++ esphome/components/climate/climate_traits.h | 99 +++++++++++++------ .../thermostat/thermostat_climate.cpp | 12 ++- 9 files changed, 142 insertions(+), 68 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index c7d8fb28f0..30e3cfa056 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -987,8 +987,8 @@ message ListEntitiesClimateResponse { string name = 3; reserved 4; // Deprecated: was string unique_id - bool supports_current_temperature = 5; - bool supports_two_point_target_temperature = 6; + bool supports_current_temperature = 5; // Deprecated: use feature_flags + bool supports_two_point_target_temperature = 6; // Deprecated: use feature_flags repeated ClimateMode supported_modes = 7 [(container_pointer) = "std::set"]; float visual_min_temperature = 8; float visual_max_temperature = 9; @@ -997,7 +997,7 @@ message ListEntitiesClimateResponse { // is if CLIMATE_PRESET_AWAY exists is supported_presets // Deprecated in API version 1.5 bool legacy_supports_away = 11 [deprecated=true]; - bool supports_action = 12; + bool supports_action = 12; // Deprecated: use feature_flags repeated ClimateFanMode supported_fan_modes = 13 [(container_pointer) = "std::set"]; repeated ClimateSwingMode supported_swing_modes = 14 [(container_pointer) = "std::set"]; repeated string supported_custom_fan_modes = 15 [(container_pointer) = "std::set"]; @@ -1007,11 +1007,12 @@ message ListEntitiesClimateResponse { string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON"]; EntityCategory entity_category = 20; float visual_current_temperature_step = 21; - bool supports_current_humidity = 22; - bool supports_target_humidity = 23; + bool supports_current_humidity = 22; // Deprecated: use feature_flags + bool supports_target_humidity = 23; // Deprecated: use feature_flags float visual_min_humidity = 24; float visual_max_humidity = 25; uint32 device_id = 26 [(field_ifdef) = "USE_DEVICES"]; + uint32 feature_flags = 27; } message ClimateStateResponse { option (id) = 47; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index ae03dfbb33..1f3456a205 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -27,6 +27,9 @@ #ifdef USE_BLUETOOTH_PROXY #include "esphome/components/bluetooth_proxy/bluetooth_proxy.h" #endif +#ifdef USE_CLIMATE +#include "esphome/components/climate/climate_mode.h" +#endif #ifdef USE_VOICE_ASSISTANT #include "esphome/components/voice_assistant/voice_assistant.h" #endif @@ -623,9 +626,10 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection auto traits = climate->get_traits(); resp.mode = static_cast(climate->mode); resp.action = static_cast(climate->action); - if (traits.get_supports_current_temperature()) + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) resp.current_temperature = climate->current_temperature; - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { resp.target_temperature_low = climate->target_temperature_low; resp.target_temperature_high = climate->target_temperature_high; } else { @@ -644,9 +648,9 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection } if (traits.get_supports_swing_modes()) resp.swing_mode = static_cast(climate->swing_mode); - if (traits.get_supports_current_humidity()) + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) resp.current_humidity = climate->current_humidity; - if (traits.get_supports_target_humidity()) + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) resp.target_humidity = climate->target_humidity; return fill_and_encode_entity_state(climate, resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); @@ -656,10 +660,14 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection auto *climate = static_cast(entity); ListEntitiesClimateResponse msg; auto traits = climate->get_traits(); + // Flags set for backward compatibility, deprecated in 2025.11.0 msg.supports_current_temperature = traits.get_supports_current_temperature(); msg.supports_current_humidity = traits.get_supports_current_humidity(); msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature(); msg.supports_target_humidity = traits.get_supports_target_humidity(); + msg.supports_action = traits.get_supports_action(); + // Current feature flags and other supported parameters + msg.feature_flags = traits.get_feature_flags(); msg.supported_modes = &traits.get_supported_modes_for_api_(); msg.visual_min_temperature = traits.get_visual_min_temperature(); msg.visual_max_temperature = traits.get_visual_max_temperature(); @@ -667,7 +675,6 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection msg.visual_current_temperature_step = traits.get_visual_current_temperature_step(); msg.visual_min_humidity = traits.get_visual_min_humidity(); msg.visual_max_humidity = traits.get_visual_max_humidity(); - msg.supports_action = traits.get_supports_action(); msg.supported_fan_modes = &traits.get_supported_fan_modes_for_api_(); msg.supported_custom_fan_modes = &traits.get_supported_custom_fan_modes_for_api_(); msg.supported_presets = &traits.get_supported_presets_for_api_(); @@ -1406,7 +1413,7 @@ bool APIConnection::send_hello_response(const HelloRequest &msg) { HelloResponse resp; resp.api_version_major = 1; - resp.api_version_minor = 12; + resp.api_version_minor = 13; // Send only the version string - the client only logs this for debugging and doesn't use it otherwise resp.set_server_info(ESPHOME_VERSION_REF); resp.set_name(StringRef(App.get_name())); diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 70bcf082a6..ee25d4c02d 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -1185,6 +1185,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { #ifdef USE_DEVICES buffer.encode_uint32(26, this->device_id); #endif + buffer.encode_uint32(27, this->feature_flags); } void ListEntitiesClimateResponse::calculate_size(ProtoSize &size) const { size.add_length(1, this->object_id_ref_.size()); @@ -1239,6 +1240,7 @@ void ListEntitiesClimateResponse::calculate_size(ProtoSize &size) const { #ifdef USE_DEVICES size.add_uint32(2, this->device_id); #endif + size.add_uint32(2, this->feature_flags); } void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 20866850a9..fca3f546b5 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1369,7 +1369,7 @@ class CameraImageRequest final : public ProtoDecodableMessage { class ListEntitiesClimateResponse final : public InfoResponseProtoMessage { public: static constexpr uint8_t MESSAGE_TYPE = 46; - static constexpr uint8_t ESTIMATED_SIZE = 145; + static constexpr uint8_t ESTIMATED_SIZE = 150; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_climate_response"; } #endif @@ -1390,6 +1390,7 @@ class ListEntitiesClimateResponse final : public InfoResponseProtoMessage { bool supports_target_humidity{false}; float visual_min_humidity{0.0f}; float visual_max_humidity{0.0f}; + uint32_t feature_flags{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(ProtoSize &size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP diff --git a/esphome/components/api/api_pb2_dump.cpp b/esphome/components/api/api_pb2_dump.cpp index cf732e451b..e803125f53 100644 --- a/esphome/components/api/api_pb2_dump.cpp +++ b/esphome/components/api/api_pb2_dump.cpp @@ -1292,6 +1292,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const { #ifdef USE_DEVICES dump_field(out, "device_id", this->device_id); #endif + dump_field(out, "feature_flags", this->feature_flags); } void ClimateStateResponse::dump_to(std::string &out) const { MessageDumpHelper helper(out, "ClimateStateResponse"); diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index e7a454d459..f3c93ed44e 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -96,7 +96,8 @@ void ClimateCall::validate_() { } if (this->target_temperature_.has_value()) { auto target = *this->target_temperature_; - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { ESP_LOGW(TAG, " Cannot set target temperature for climate device " "with two-point target temperature!"); this->target_temperature_.reset(); @@ -106,7 +107,8 @@ void ClimateCall::validate_() { } } if (this->target_temperature_low_.has_value() || this->target_temperature_high_.has_value()) { - if (!traits.get_supports_two_point_target_temperature()) { + if (!traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { ESP_LOGW(TAG, " Cannot set low/high target temperature for this device!"); this->target_temperature_low_.reset(); this->target_temperature_high_.reset(); @@ -350,13 +352,14 @@ void Climate::save_state_() { state.mode = this->mode; auto traits = this->get_traits(); - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { state.target_temperature_low = this->target_temperature_low; state.target_temperature_high = this->target_temperature_high; } else { state.target_temperature = this->target_temperature; } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { state.target_humidity = this->target_humidity; } if (traits.get_supports_fan_modes() && fan_mode.has_value()) { @@ -400,7 +403,7 @@ void Climate::publish_state() { auto traits = this->get_traits(); ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode))); - if (traits.get_supports_action()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { ESP_LOGD(TAG, " Action: %s", LOG_STR_ARG(climate_action_to_string(this->action))); } if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) { @@ -418,19 +421,20 @@ void Climate::publish_state() { if (traits.get_supports_swing_modes()) { ESP_LOGD(TAG, " Swing Mode: %s", LOG_STR_ARG(climate_swing_mode_to_string(this->swing_mode))); } - if (traits.get_supports_current_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { ESP_LOGD(TAG, " Current Temperature: %.2f°C", this->current_temperature); } - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { ESP_LOGD(TAG, " Target Temperature: Low: %.2f°C High: %.2f°C", this->target_temperature_low, this->target_temperature_high); } else { ESP_LOGD(TAG, " Target Temperature: %.2f°C", this->target_temperature); } - if (traits.get_supports_current_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) { ESP_LOGD(TAG, " Current Humidity: %.0f%%", this->current_humidity); } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { ESP_LOGD(TAG, " Target Humidity: %.0f%%", this->target_humidity); } @@ -485,13 +489,14 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) { auto call = climate->make_call(); auto traits = climate->get_traits(); call.set_mode(this->mode); - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { call.set_target_temperature_low(this->target_temperature_low); call.set_target_temperature_high(this->target_temperature_high); } else { call.set_target_temperature(this->target_temperature); } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { call.set_target_humidity(this->target_humidity); } if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) { @@ -508,13 +513,14 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) { void ClimateDeviceRestoreState::apply(Climate *climate) { auto traits = climate->get_traits(); climate->mode = this->mode; - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { climate->target_temperature_low = this->target_temperature_low; climate->target_temperature_high = this->target_temperature_high; } else { climate->target_temperature = this->target_temperature; } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { climate->target_humidity = this->target_humidity; } if (traits.get_supports_fan_modes() && !this->uses_custom_fan_mode) { @@ -580,28 +586,30 @@ void Climate::dump_traits_(const char *tag) { " Target: %.1f", traits.get_visual_min_temperature(), traits.get_visual_max_temperature(), traits.get_visual_target_temperature_step()); - if (traits.get_supports_current_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step()); } - if (traits.get_supports_target_humidity() || traits.get_supports_current_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY | + climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) { ESP_LOGCONFIG(tag, " - Min humidity: %.0f\n" " - Max humidity: %.0f", traits.get_visual_min_humidity(), traits.get_visual_max_humidity()); } - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { ESP_LOGCONFIG(tag, " [x] Supports two-point target temperature"); } - if (traits.get_supports_current_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { ESP_LOGCONFIG(tag, " [x] Supports current temperature"); } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { ESP_LOGCONFIG(tag, " [x] Supports target humidity"); } - if (traits.get_supports_current_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) { ESP_LOGCONFIG(tag, " [x] Supports current humidity"); } - if (traits.get_supports_action()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { ESP_LOGCONFIG(tag, " [x] Supports action"); } if (!traits.get_supported_modes().empty()) { diff --git a/esphome/components/climate/climate_mode.h b/esphome/components/climate/climate_mode.h index 80efb4c048..faec5d2537 100644 --- a/esphome/components/climate/climate_mode.h +++ b/esphome/components/climate/climate_mode.h @@ -98,6 +98,21 @@ enum ClimatePreset : uint8_t { CLIMATE_PRESET_ACTIVITY = 7, }; +enum ClimateFeature : uint32_t { + // Reporting current temperature is supported + CLIMATE_SUPPORTS_CURRENT_TEMPERATURE = 1 << 0, + // Setting two target temperatures is supported (used in conjunction with CLIMATE_MODE_HEAT_COOL) + CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE = 1 << 1, + // Single-point mode is NOT supported (UI always displays two handles, setting 'target_temperature' is not supported) + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE = 1 << 2, + // Reporting current humidity is supported + CLIMATE_SUPPORTS_CURRENT_HUMIDITY = 1 << 3, + // Setting a target humidity is supported + CLIMATE_SUPPORTS_TARGET_HUMIDITY = 1 << 4, + // Reporting current climate action is supported + CLIMATE_SUPPORTS_ACTION = 1 << 5, +}; + /// Convert the given ClimateMode to a human-readable string. const LogString *climate_mode_to_string(ClimateMode mode); diff --git a/esphome/components/climate/climate_traits.h b/esphome/components/climate/climate_traits.h index 8bd4714753..50c1e79ad2 100644 --- a/esphome/components/climate/climate_traits.h +++ b/esphome/components/climate/climate_traits.h @@ -21,48 +21,92 @@ namespace climate { * - Target Temperature * * All other properties and modes are optional and the integration must mark - * each of them as supported by setting the appropriate flag here. + * each of them as supported by setting the appropriate flag(s) here. * - * - supports current temperature - if the climate device supports reporting a current temperature - * - supports two point target temperature - if the climate device's target temperature should be - * split in target_temperature_low and target_temperature_high instead of just the single target_temperature + * - feature flags: see ClimateFeatures enum in climate_mode.h * - supports modes: * - auto mode (automatic control) * - cool mode (lowers current temperature) * - heat mode (increases current temperature) * - dry mode (removes humidity from air) * - fan mode (only turns on fan) - * - supports action - if the climate device supports reporting the active - * current action of the device with the action property. * - supports fan modes - optionally, if it has a fan which can be configured in different ways: * - on, off, auto, high, medium, low, middle, focus, diffuse, quiet * - supports swing modes - optionally, if it has a swing which can be configured in different ways: * - off, both, vertical, horizontal * * This class also contains static data for the climate device display: - * - visual min/max temperature - tells the frontend what range of temperatures the climate device - * should display (gauge min/max values) + * - visual min/max temperature/humidity - tells the frontend what range of temperature/humidity the + * climate device should display (gauge min/max values) * - temperature step - the step with which to increase/decrease target temperature. * This also affects with how many decimal places the temperature is shown */ class ClimateTraits { public: - bool get_supports_current_temperature() const { return this->supports_current_temperature_; } + /// Get/set feature flags (see ClimateFeatures enum in climate_mode.h) + uint32_t get_feature_flags() const { return this->feature_flags_; } + void add_feature_flags(uint32_t feature_flags) { this->feature_flags_ |= feature_flags; } + void clear_feature_flags(uint32_t feature_flags) { this->feature_flags_ &= ~feature_flags; } + bool has_feature_flags(uint32_t feature_flags) const { return this->feature_flags_ & feature_flags; } + void set_feature_flags(uint32_t feature_flags) { this->feature_flags_ = feature_flags; } + + ESPDEPRECATED("This method is deprecated, use get_feature_flags() instead", "2025.11.0") + bool get_supports_current_temperature() const { + return this->has_feature_flags(CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + } + ESPDEPRECATED("This method is deprecated, use add_feature_flags() instead", "2025.11.0") void set_supports_current_temperature(bool supports_current_temperature) { - this->supports_current_temperature_ = supports_current_temperature; + if (supports_current_temperature) { + this->add_feature_flags(CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + } else { + this->clear_feature_flags(CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + } } - bool get_supports_current_humidity() const { return this->supports_current_humidity_; } + ESPDEPRECATED("This method is deprecated, use get_feature_flags() instead", "2025.11.0") + bool get_supports_current_humidity() const { return this->has_feature_flags(CLIMATE_SUPPORTS_CURRENT_HUMIDITY); } + ESPDEPRECATED("This method is deprecated, use add_feature_flags() instead", "2025.11.0") void set_supports_current_humidity(bool supports_current_humidity) { - this->supports_current_humidity_ = supports_current_humidity; + if (supports_current_humidity) { + this->add_feature_flags(CLIMATE_SUPPORTS_CURRENT_HUMIDITY); + } else { + this->clear_feature_flags(CLIMATE_SUPPORTS_CURRENT_HUMIDITY); + } } - bool get_supports_two_point_target_temperature() const { return this->supports_two_point_target_temperature_; } + ESPDEPRECATED("This method is deprecated, use get_feature_flags() instead", "2025.11.0") + bool get_supports_two_point_target_temperature() const { + return this->has_feature_flags(CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE); + } + ESPDEPRECATED("This method is deprecated, use add_feature_flags() instead", "2025.11.0") void set_supports_two_point_target_temperature(bool supports_two_point_target_temperature) { - this->supports_two_point_target_temperature_ = supports_two_point_target_temperature; + if (supports_two_point_target_temperature) + // Use CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE to mimic previous behavior + { + this->add_feature_flags(CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE); + } else { + this->clear_feature_flags(CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE); + } } - bool get_supports_target_humidity() const { return this->supports_target_humidity_; } + ESPDEPRECATED("This method is deprecated, use get_feature_flags() instead", "2025.11.0") + bool get_supports_target_humidity() const { return this->has_feature_flags(CLIMATE_SUPPORTS_TARGET_HUMIDITY); } + ESPDEPRECATED("This method is deprecated, use add_feature_flags() instead", "2025.11.0") void set_supports_target_humidity(bool supports_target_humidity) { - this->supports_target_humidity_ = supports_target_humidity; + if (supports_target_humidity) { + this->add_feature_flags(CLIMATE_SUPPORTS_TARGET_HUMIDITY); + } else { + this->clear_feature_flags(CLIMATE_SUPPORTS_TARGET_HUMIDITY); + } } + ESPDEPRECATED("This method is deprecated, use get_feature_flags() instead", "2025.11.0") + bool get_supports_action() const { return this->has_feature_flags(CLIMATE_SUPPORTS_ACTION); } + ESPDEPRECATED("This method is deprecated, use add_feature_flags() instead", "2025.11.0") + void set_supports_action(bool supports_action) { + if (supports_action) { + this->add_feature_flags(CLIMATE_SUPPORTS_ACTION); + } else { + this->clear_feature_flags(CLIMATE_SUPPORTS_ACTION); + } + } + void set_supported_modes(std::set modes) { this->supported_modes_ = std::move(modes); } void add_supported_mode(ClimateMode mode) { this->supported_modes_.insert(mode); } ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") @@ -82,9 +126,6 @@ class ClimateTraits { bool supports_mode(ClimateMode mode) const { return this->supported_modes_.count(mode); } const std::set &get_supported_modes() const { return this->supported_modes_; } - void set_supports_action(bool supports_action) { this->supports_action_ = supports_action; } - bool get_supports_action() const { return this->supports_action_; } - void set_supported_fan_modes(std::set modes) { this->supported_fan_modes_ = std::move(modes); } void add_supported_fan_mode(ClimateFanMode mode) { this->supported_fan_modes_.insert(mode); } void add_supported_custom_fan_mode(const std::string &mode) { this->supported_custom_fan_modes_.insert(mode); } @@ -219,24 +260,20 @@ class ClimateTraits { } } - bool supports_current_temperature_{false}; - bool supports_current_humidity_{false}; - bool supports_two_point_target_temperature_{false}; - bool supports_target_humidity_{false}; - std::set supported_modes_ = {climate::CLIMATE_MODE_OFF}; - bool supports_action_{false}; - std::set supported_fan_modes_; - std::set supported_swing_modes_; - std::set supported_presets_; - std::set supported_custom_fan_modes_; - std::set supported_custom_presets_; - + uint32_t feature_flags_{0}; float visual_min_temperature_{10}; float visual_max_temperature_{30}; float visual_target_temperature_step_{0.1}; float visual_current_temperature_step_{0.1}; float visual_min_humidity_{30}; float visual_max_humidity_{99}; + + std::set supported_modes_ = {climate::CLIMATE_MODE_OFF}; + std::set supported_fan_modes_; + std::set supported_swing_modes_; + std::set supported_presets_; + std::set supported_custom_fan_modes_; + std::set supported_custom_presets_; }; } // namespace climate diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index e2db3ca5e1..784b3cfb50 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -241,9 +241,14 @@ void ThermostatClimate::control(const climate::ClimateCall &call) { climate::ClimateTraits ThermostatClimate::traits() { auto traits = climate::ClimateTraits(); - traits.set_supports_current_temperature(true); + + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_ACTION | climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + + if (this->supports_two_points_) + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE); + if (this->humidity_sensor_ != nullptr) - traits.set_supports_current_humidity(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY); if (this->supports_auto_) traits.add_supported_mode(climate::CLIMATE_MODE_AUTO); @@ -294,9 +299,6 @@ climate::ClimateTraits ThermostatClimate::traits() { for (auto &it : this->custom_preset_config_) { traits.add_supported_custom_preset(it.first); } - - traits.set_supports_two_point_target_temperature(this->supports_two_points_); - traits.set_supports_action(true); return traits; } From 3e1c8f37c562399640abd6206a0ac8cdbea3f4e0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 17:16:28 -1000 Subject: [PATCH 096/526] [i2s_audio] Refactor to use CORE.data instead of module-level globals (#11223) --- esphome/components/i2s_audio/__init__.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index 8ceff26d84..907429ee0e 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -143,7 +143,18 @@ def validate_mclk_divisible_by_3(config): return config -_use_legacy_driver = None +# Key for storing legacy driver setting in CORE.data +I2S_USE_LEGACY_DRIVER_KEY = "i2s_use_legacy_driver" + + +def _get_use_legacy_driver(): + """Get the legacy driver setting from CORE.data.""" + return CORE.data.get(I2S_USE_LEGACY_DRIVER_KEY) + + +def _set_use_legacy_driver(value: bool) -> None: + """Set the legacy driver setting in CORE.data.""" + CORE.data[I2S_USE_LEGACY_DRIVER_KEY] = value def i2s_audio_component_schema( @@ -209,17 +220,15 @@ async def register_i2s_audio_component(var, config): def validate_use_legacy(value): - global _use_legacy_driver # noqa: PLW0603 if CONF_USE_LEGACY in value: - if (_use_legacy_driver is not None) and ( - _use_legacy_driver != value[CONF_USE_LEGACY] - ): + existing_value = _get_use_legacy_driver() + if (existing_value is not None) and (existing_value != value[CONF_USE_LEGACY]): raise cv.Invalid( f"All i2s_audio components must set {CONF_USE_LEGACY} to the same value." ) if (not value[CONF_USE_LEGACY]) and (CORE.using_arduino): raise cv.Invalid("Arduino supports only the legacy i2s driver") - _use_legacy_driver = value[CONF_USE_LEGACY] + _set_use_legacy_driver(value[CONF_USE_LEGACY]) return value @@ -249,7 +258,8 @@ def _final_validate(_): def use_legacy(): - return not (CORE.using_esp_idf and not _use_legacy_driver) + legacy_driver = _get_use_legacy_driver() + return not (CORE.using_esp_idf and not legacy_driver) FINAL_VALIDATE_SCHEMA = _final_validate From 2b0b82b2fb66b1aefdf495f22c5c0ef92c53c28e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 17:17:16 -1000 Subject: [PATCH 097/526] [esp32_ble] Refactor to use CORE.data instead of module-level globals (#11222) --- esphome/components/esp32_ble/__init__.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 246ebf0729..411c2add71 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -108,8 +108,13 @@ class BTLoggers(Enum): """ESP32 WiFi provisioning over Bluetooth""" -# Set to track which loggers are needed by components -_required_loggers: set[BTLoggers] = set() +# Key for storing required loggers in CORE.data +ESP32_BLE_REQUIRED_LOGGERS_KEY = "esp32_ble_required_loggers" + + +def _get_required_loggers() -> set[BTLoggers]: + """Get the set of required Bluetooth loggers from CORE.data.""" + return CORE.data.setdefault(ESP32_BLE_REQUIRED_LOGGERS_KEY, set()) # Dataclass for handler registration counts @@ -170,12 +175,13 @@ def register_bt_logger(*loggers: BTLoggers) -> None: Args: *loggers: One or more BTLoggers enum members """ + required_loggers = _get_required_loggers() 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) + required_loggers.add(logger) CONF_BLE_ID = "ble_id" @@ -488,8 +494,9 @@ async def to_code(config): # Apply logger settings if log disabling is enabled if config.get(CONF_DISABLE_BT_LOGS, False): # Disable all Bluetooth loggers that are not required + required_loggers = _get_required_loggers() for logger in BTLoggers: - if logger not in _required_loggers: + if logger not in required_loggers: add_idf_sdkconfig_option(f"{logger.value}_NONE", True) # Set BLE connection establishment timeout to match aioesphomeapi/bleak-retry-connector From 18062d154f236fdc714f4a94d4bde3df068a2ccc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 17:18:30 -1000 Subject: [PATCH 098/526] [esp32_ble_tracker] Refactor to use CORE.data instead of module-level globals (#11220) --- .../components/esp32_ble_tracker/__init__.py | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 5910be67af..4e25434aad 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -60,11 +60,21 @@ class RegistrationCounts: clients: int = 0 -# Set to track which features are needed by components -_required_features: set[BLEFeatures] = set() +# CORE.data keys for state management +ESP32_BLE_TRACKER_REQUIRED_FEATURES_KEY = "esp32_ble_tracker_required_features" +ESP32_BLE_TRACKER_REGISTRATION_COUNTS_KEY = "esp32_ble_tracker_registration_counts" -# Track registration counts for StaticVector sizing -_registration_counts = RegistrationCounts() + +def _get_required_features() -> set[BLEFeatures]: + """Get the set of required BLE features from CORE.data.""" + return CORE.data.setdefault(ESP32_BLE_TRACKER_REQUIRED_FEATURES_KEY, set()) + + +def _get_registration_counts() -> RegistrationCounts: + """Get the registration counts from CORE.data.""" + return CORE.data.setdefault( + ESP32_BLE_TRACKER_REGISTRATION_COUNTS_KEY, RegistrationCounts() + ) def register_ble_features(features: set[BLEFeatures]) -> None: @@ -73,7 +83,7 @@ def register_ble_features(features: set[BLEFeatures]) -> None: Args: features: Set of BLEFeatures enum members """ - _required_features.update(features) + _get_required_features().update(features) esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker") @@ -267,15 +277,17 @@ async def to_code(config): ): register_ble_features({BLEFeatures.ESP_BT_DEVICE}) + registration_counts = _get_registration_counts() + for conf in config.get(CONF_ON_BLE_ADVERTISE, []): - _registration_counts.listeners += 1 + registration_counts.listeners += 1 trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) if CONF_MAC_ADDRESS in conf: addr_list = [it.as_hex for it in conf[CONF_MAC_ADDRESS]] cg.add(trigger.set_addresses(addr_list)) await automation.build_automation(trigger, [(ESPBTDeviceConstRef, "x")], conf) for conf in config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, []): - _registration_counts.listeners += 1 + registration_counts.listeners += 1 trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) if len(conf[CONF_SERVICE_UUID]) == len(bt_uuid16_format): cg.add(trigger.set_service_uuid16(as_hex(conf[CONF_SERVICE_UUID]))) @@ -288,7 +300,7 @@ async def to_code(config): cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) for conf in config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, []): - _registration_counts.listeners += 1 + registration_counts.listeners += 1 trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) if len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid16_format): cg.add(trigger.set_manufacturer_uuid16(as_hex(conf[CONF_MANUFACTURER_ID]))) @@ -301,7 +313,7 @@ async def to_code(config): cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) for conf in config.get(CONF_ON_SCAN_END, []): - _registration_counts.listeners += 1 + registration_counts.listeners += 1 trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) @@ -331,19 +343,21 @@ async def to_code(config): @coroutine_with_priority(CoroPriority.FINAL) async def _add_ble_features(): # Add feature-specific defines based on what's needed - if BLEFeatures.ESP_BT_DEVICE in _required_features: + required_features = _get_required_features() + if BLEFeatures.ESP_BT_DEVICE in required_features: cg.add_define("USE_ESP32_BLE_DEVICE") cg.add_define("USE_ESP32_BLE_UUID") # Add defines for StaticVector sizing based on registration counts # Only define if count > 0 to avoid allocating unnecessary memory - if _registration_counts.listeners > 0: + registration_counts = _get_registration_counts() + if registration_counts.listeners > 0: cg.add_define( - "ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT", _registration_counts.listeners + "ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT", registration_counts.listeners ) - if _registration_counts.clients > 0: + if registration_counts.clients > 0: cg.add_define( - "ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT", _registration_counts.clients + "ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT", registration_counts.clients ) @@ -395,7 +409,7 @@ async def register_ble_device( var: cg.SafeExpType, config: ConfigType ) -> cg.SafeExpType: register_ble_features({BLEFeatures.ESP_BT_DEVICE}) - _registration_counts.listeners += 1 + _get_registration_counts().listeners += 1 paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) cg.add(paren.register_listener(var)) return var @@ -403,7 +417,7 @@ async def register_ble_device( async def register_client(var: cg.SafeExpType, config: ConfigType) -> cg.SafeExpType: register_ble_features({BLEFeatures.ESP_BT_DEVICE}) - _registration_counts.clients += 1 + _get_registration_counts().clients += 1 paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) cg.add(paren.register_client(var)) return var @@ -417,7 +431,7 @@ async def register_raw_ble_device( This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice will not be compiled in if this is the only registration method used. """ - _registration_counts.listeners += 1 + _get_registration_counts().listeners += 1 paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) cg.add(paren.register_listener(var)) return var @@ -431,7 +445,7 @@ async def register_raw_client( This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice will not be compiled in if this is the only registration method used. """ - _registration_counts.clients += 1 + _get_registration_counts().clients += 1 paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) cg.add(paren.register_client(var)) return var From 6943b1d985adee3f94158b318ed166b8387bd9ff Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 17:22:08 -1000 Subject: [PATCH 099/526] [api] Use FixedVector for ExecuteServiceRequest/Argument arrays to eliminate reallocations (#11270) --- esphome/components/api/api.proto | 10 ++-- esphome/components/api/api_pb2.cpp | 16 ++++++ esphome/components/api/api_pb2.h | 12 ++-- esphome/components/api/proto.cpp | 71 ++++++++++++++++++++++-- esphome/components/api/proto.h | 24 +++++++- esphome/components/api/user_services.cpp | 8 +-- esphome/components/api/user_services.h | 2 +- script/api_protobuf/api_protobuf.py | 36 ++++++++++-- 8 files changed, 153 insertions(+), 26 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 30e3cfa056..753adc3592 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -876,10 +876,10 @@ message ExecuteServiceArgument { string string_ = 4; // ESPHome 1.14 (api v1.3) make int a signed value sint32 int_ = 5; - repeated bool bool_array = 6 [packed=false]; - repeated sint32 int_array = 7 [packed=false]; - repeated float float_array = 8 [packed=false]; - repeated string string_array = 9; + repeated bool bool_array = 6 [packed=false, (fixed_vector) = true]; + repeated sint32 int_array = 7 [packed=false, (fixed_vector) = true]; + repeated float float_array = 8 [packed=false, (fixed_vector) = true]; + repeated string string_array = 9 [(fixed_vector) = true]; } message ExecuteServiceRequest { option (id) = 42; @@ -888,7 +888,7 @@ message ExecuteServiceRequest { option (ifdef) = "USE_API_SERVICES"; fixed32 key = 1; - repeated ExecuteServiceArgument args = 2; + repeated ExecuteServiceArgument args = 2 [(fixed_vector) = true]; } // ==================== CAMERA ==================== diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index ee25d4c02d..37bcf5d8a0 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -1064,6 +1064,17 @@ bool ExecuteServiceArgument::decode_32bit(uint32_t field_id, Proto32Bit value) { } return true; } +void ExecuteServiceArgument::decode(const uint8_t *buffer, size_t length) { + uint32_t count_bool_array = ProtoDecodableMessage::count_repeated_field(buffer, length, 6); + this->bool_array.init(count_bool_array); + uint32_t count_int_array = ProtoDecodableMessage::count_repeated_field(buffer, length, 7); + this->int_array.init(count_int_array); + uint32_t count_float_array = ProtoDecodableMessage::count_repeated_field(buffer, length, 8); + this->float_array.init(count_float_array); + uint32_t count_string_array = ProtoDecodableMessage::count_repeated_field(buffer, length, 9); + this->string_array.init(count_string_array); + ProtoDecodableMessage::decode(buffer, length); +} bool ExecuteServiceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 2: @@ -1085,6 +1096,11 @@ bool ExecuteServiceRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { } return true; } +void ExecuteServiceRequest::decode(const uint8_t *buffer, size_t length) { + uint32_t count_args = ProtoDecodableMessage::count_repeated_field(buffer, length, 2); + this->args.init(count_args); + ProtoDecodableMessage::decode(buffer, length); +} #endif #ifdef USE_CAMERA void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const { diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index fca3f546b5..5603204801 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1279,10 +1279,11 @@ class ExecuteServiceArgument final : public ProtoDecodableMessage { float float_{0.0f}; std::string string_{}; int32_t int_{0}; - std::vector bool_array{}; - std::vector int_array{}; - std::vector float_array{}; - std::vector string_array{}; + FixedVector bool_array{}; + FixedVector int_array{}; + FixedVector float_array{}; + FixedVector string_array{}; + void decode(const uint8_t *buffer, size_t length) override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1300,7 +1301,8 @@ class ExecuteServiceRequest final : public ProtoDecodableMessage { const char *message_name() const override { return "execute_service_request"; } #endif uint32_t key{0}; - std::vector args{}; + FixedVector args{}; + void decode(const uint8_t *buffer, size_t length) override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif diff --git a/esphome/components/api/proto.cpp b/esphome/components/api/proto.cpp index afda5d32ba..4f0d0846d7 100644 --- a/esphome/components/api/proto.cpp +++ b/esphome/components/api/proto.cpp @@ -7,6 +7,69 @@ namespace esphome::api { static const char *const TAG = "api.proto"; +uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id) { + uint32_t count = 0; + const uint8_t *ptr = buffer; + const uint8_t *end = buffer + length; + + while (ptr < end) { + uint32_t consumed; + + // Parse field header (tag) + auto res = ProtoVarInt::parse(ptr, end - ptr, &consumed); + if (!res.has_value()) { + break; // Invalid data, stop counting + } + + uint32_t tag = res->as_uint32(); + uint32_t field_type = tag & WIRE_TYPE_MASK; + uint32_t field_id = tag >> 3; + ptr += consumed; + + // Count if this is the target field + if (field_id == target_field_id) { + count++; + } + + // Skip field data based on wire type + switch (field_type) { + case WIRE_TYPE_VARINT: { // VarInt - parse and skip + res = ProtoVarInt::parse(ptr, end - ptr, &consumed); + if (!res.has_value()) { + return count; // Invalid data, return what we have + } + ptr += consumed; + break; + } + case WIRE_TYPE_LENGTH_DELIMITED: { // Length-delimited - parse length and skip data + res = ProtoVarInt::parse(ptr, end - ptr, &consumed); + if (!res.has_value()) { + return count; + } + uint32_t field_length = res->as_uint32(); + ptr += consumed; + if (ptr + field_length > end) { + return count; // Out of bounds + } + ptr += field_length; + break; + } + case WIRE_TYPE_FIXED32: { // 32-bit - skip 4 bytes + if (ptr + 4 > end) { + return count; + } + ptr += 4; + break; + } + default: + // Unknown wire type, can't continue + return count; + } + } + + return count; +} + void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) { const uint8_t *ptr = buffer; const uint8_t *end = buffer + length; @@ -22,12 +85,12 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) { } uint32_t tag = res->as_uint32(); - uint32_t field_type = tag & 0b111; + uint32_t field_type = tag & WIRE_TYPE_MASK; uint32_t field_id = tag >> 3; ptr += consumed; switch (field_type) { - case 0: { // VarInt + case WIRE_TYPE_VARINT: { // VarInt res = ProtoVarInt::parse(ptr, end - ptr, &consumed); if (!res.has_value()) { ESP_LOGV(TAG, "Invalid VarInt at offset %ld", (long) (ptr - buffer)); @@ -39,7 +102,7 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) { ptr += consumed; break; } - case 2: { // Length-delimited + case WIRE_TYPE_LENGTH_DELIMITED: { // Length-delimited res = ProtoVarInt::parse(ptr, end - ptr, &consumed); if (!res.has_value()) { ESP_LOGV(TAG, "Invalid Length Delimited at offset %ld", (long) (ptr - buffer)); @@ -57,7 +120,7 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) { ptr += field_length; break; } - case 5: { // 32-bit + case WIRE_TYPE_FIXED32: { // 32-bit if (ptr + 4 > end) { ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at offset %ld", (long) (ptr - buffer)); return; diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index a6a09bf7c5..e7585924a5 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -15,6 +15,13 @@ namespace esphome::api { +// Protocol Buffer wire type constants +// See https://protobuf.dev/programming-guides/encoding/#structure +constexpr uint8_t WIRE_TYPE_VARINT = 0; // int32, int64, uint32, uint64, sint32, sint64, bool, enum +constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2; // string, bytes, embedded messages, packed repeated fields +constexpr uint8_t WIRE_TYPE_FIXED32 = 5; // fixed32, sfixed32, float +constexpr uint8_t WIRE_TYPE_MASK = 0b111; // Mask to extract wire type from tag + // Helper functions for ZigZag encoding/decoding inline constexpr uint32_t encode_zigzag32(int32_t value) { return (static_cast(value) << 1) ^ (static_cast(value >> 31)); @@ -241,7 +248,7 @@ class ProtoWriteBuffer { * Following https://protobuf.dev/programming-guides/encoding/#structure */ void encode_field_raw(uint32_t field_id, uint32_t type) { - uint32_t val = (field_id << 3) | (type & 0b111); + uint32_t val = (field_id << 3) | (type & WIRE_TYPE_MASK); this->encode_varint_raw(val); } void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) { @@ -354,7 +361,18 @@ class ProtoMessage { // Base class for messages that support decoding class ProtoDecodableMessage : public ProtoMessage { public: - void decode(const uint8_t *buffer, size_t length); + virtual void decode(const uint8_t *buffer, size_t length); + + /** + * Count occurrences of a repeated field in a protobuf buffer. + * This is a lightweight scan that only parses tags and skips field data. + * + * @param buffer Pointer to the protobuf buffer + * @param length Length of the buffer in bytes + * @param target_field_id The field ID to count + * @return Number of times the field appears in the buffer + */ + static uint32_t count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id); protected: virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; } @@ -482,7 +500,7 @@ class ProtoSize { * @return The number of bytes needed to encode the field ID and wire type */ static constexpr uint32_t field(uint32_t field_id, uint32_t type) { - uint32_t tag = (field_id << 3) | (type & 0b111); + uint32_t tag = (field_id << 3) | (type & WIRE_TYPE_MASK); return varint(tag); } diff --git a/esphome/components/api/user_services.cpp b/esphome/components/api/user_services.cpp index 27b30eb332..3cbf2ab5f9 100644 --- a/esphome/components/api/user_services.cpp +++ b/esphome/components/api/user_services.cpp @@ -12,16 +12,16 @@ template<> int32_t get_execute_arg_value(const ExecuteServiceArgument & template<> float get_execute_arg_value(const ExecuteServiceArgument &arg) { return arg.float_; } template<> std::string get_execute_arg_value(const ExecuteServiceArgument &arg) { return arg.string_; } template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { - return arg.bool_array; + return std::vector(arg.bool_array.begin(), arg.bool_array.end()); } template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { - return arg.int_array; + return std::vector(arg.int_array.begin(), arg.int_array.end()); } template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { - return arg.float_array; + return std::vector(arg.float_array.begin(), arg.float_array.end()); } template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { - return arg.string_array; + return std::vector(arg.string_array.begin(), arg.string_array.end()); } template<> enums::ServiceArgType to_service_arg_type() { return enums::SERVICE_ARG_TYPE_BOOL; } diff --git a/esphome/components/api/user_services.h b/esphome/components/api/user_services.h index 29843a2f78..9ca5e1093e 100644 --- a/esphome/components/api/user_services.h +++ b/esphome/components/api/user_services.h @@ -55,7 +55,7 @@ template class UserServiceBase : public UserServiceDescriptor { protected: virtual void execute(Ts... x) = 0; - template void execute_(const std::vector &args, seq type) { + template void execute_(const ArgsContainer &args, seq type) { this->execute((get_execute_arg_value(args[S]))...); } diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 9a55f1d136..4936434fc2 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -11,6 +11,7 @@ from typing import Any import aioesphomeapi.api_options_pb2 as pb import google.protobuf.descriptor_pb2 as descriptor +from google.protobuf.descriptor_pb2 import FieldDescriptorProto class WireType(IntEnum): @@ -148,7 +149,7 @@ class TypeInfo(ABC): @property def repeated(self) -> bool: """Check if the field is repeated.""" - return self._field.label == 3 + return self._field.label == FieldDescriptorProto.LABEL_REPEATED @property def wire_type(self) -> WireType: @@ -337,7 +338,7 @@ def create_field_type_info( needs_encode: bool = True, ) -> TypeInfo: """Create the appropriate TypeInfo instance for a field, handling repeated fields and custom options.""" - if field.label == 3: # repeated + if field.label == FieldDescriptorProto.LABEL_REPEATED: # Check if this repeated field has fixed_array_with_length_define option if ( fixed_size := get_field_opt(field, pb.fixed_array_with_length_define) @@ -1879,6 +1880,9 @@ def build_message_type( ) public_content.append("#endif") + # Collect fixed_vector fields for custom decode generation + fixed_vector_fields = [] + for field in desc.field: # Skip deprecated fields completely if field.options.deprecated: @@ -1887,7 +1891,7 @@ def build_message_type( # Validate that fixed_array_size is only used in encode-only messages if ( needs_decode - and field.label == 3 + and field.label == FieldDescriptorProto.LABEL_REPEATED and get_field_opt(field, pb.fixed_array_size) is not None ): raise ValueError( @@ -1900,7 +1904,7 @@ def build_message_type( # Validate that fixed_array_with_length_define is only used in encode-only messages if ( needs_decode - and field.label == 3 + and field.label == FieldDescriptorProto.LABEL_REPEATED and get_field_opt(field, pb.fixed_array_with_length_define) is not None ): raise ValueError( @@ -1910,6 +1914,14 @@ def build_message_type( f"since we cannot trust or control the number of items received from clients." ) + # Collect fixed_vector repeated fields for custom decode generation + if ( + needs_decode + and field.label == FieldDescriptorProto.LABEL_REPEATED + and get_field_opt(field, pb.fixed_vector, False) + ): + fixed_vector_fields.append((field.name, field.number)) + ti = create_field_type_info(field, needs_decode, needs_encode) # Skip field declarations for fields that are in the base class @@ -2018,6 +2030,22 @@ def build_message_type( prot = "bool decode_64bit(uint32_t field_id, Proto64Bit value) override;" protected_content.insert(0, prot) + # Generate custom decode() override for messages with FixedVector fields + if fixed_vector_fields: + # Generate the decode() implementation in cpp + o = f"void {desc.name}::decode(const uint8_t *buffer, size_t length) {{\n" + # Count and init each FixedVector field + for field_name, field_number in fixed_vector_fields: + o += f" uint32_t count_{field_name} = ProtoDecodableMessage::count_repeated_field(buffer, length, {field_number});\n" + o += f" this->{field_name}.init(count_{field_name});\n" + # Call parent decode to populate the fields + o += " ProtoDecodableMessage::decode(buffer, length);\n" + o += "}\n" + cpp += o + # Generate the decode() declaration in header (public method) + prot = "void decode(const uint8_t *buffer, size_t length) override;" + public_content.append(prot) + # Only generate encode method if this message needs encoding and has fields if needs_encode and encode: o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{" From f2e0a412db1e4477b915160a0b3b7fe3eff9e46c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 17:23:20 -1000 Subject: [PATCH 100/526] [substitutions] Fix AttributeError when using packages with substitutions (#11274) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/substitutions/__init__.py | 10 +- esphome/config.py | 9 +- esphome/config_helpers.py | 26 ++- tests/unit_tests/test_substitutions.py | 204 ++++++++++++++++++- 4 files changed, 236 insertions(+), 13 deletions(-) diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index 1a1736aed1..e6bcdc063a 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -1,7 +1,7 @@ import logging from esphome import core -from esphome.config_helpers import Extend, Remove, merge_config +from esphome.config_helpers import Extend, Remove, merge_config, merge_dicts_ordered import esphome.config_validation as cv from esphome.const import CONF_SUBSTITUTIONS, VALID_SUBSTITUTIONS_CHARACTERS from esphome.yaml_util import ESPHomeDataBase, ESPLiteralValue, make_data_base @@ -170,10 +170,10 @@ def do_substitution_pass(config, command_line_substitutions, ignore_missing=Fals return # Merge substitutions in config, overriding with substitutions coming from command line: - substitutions = { - **config.get(CONF_SUBSTITUTIONS, {}), - **(command_line_substitutions or {}), - } + # Use merge_dicts_ordered to preserve OrderedDict type for move_to_end() + substitutions = merge_dicts_ordered( + config.get(CONF_SUBSTITUTIONS, {}), command_line_substitutions or {} + ) with cv.prepend_path("substitutions"): if not isinstance(substitutions, dict): raise cv.Invalid( diff --git a/esphome/config.py b/esphome/config.py index 10a5733575..6adecb5c65 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -12,7 +12,7 @@ from typing import Any import voluptuous as vol from esphome import core, loader, pins, yaml_util -from esphome.config_helpers import Extend, Remove +from esphome.config_helpers import Extend, Remove, merge_dicts_ordered import esphome.config_validation as cv from esphome.const import ( CONF_ESPHOME, @@ -922,10 +922,9 @@ def validate_config( if CONF_SUBSTITUTIONS in config or command_line_substitutions: from esphome.components import substitutions - result[CONF_SUBSTITUTIONS] = { - **(config.get(CONF_SUBSTITUTIONS) or {}), - **command_line_substitutions, - } + result[CONF_SUBSTITUTIONS] = merge_dicts_ordered( + config.get(CONF_SUBSTITUTIONS) or {}, command_line_substitutions + ) result.add_output_path([CONF_SUBSTITUTIONS], CONF_SUBSTITUTIONS) try: substitutions.do_substitution_pass(config, command_line_substitutions) diff --git a/esphome/config_helpers.py b/esphome/config_helpers.py index 00cd8f9818..88cfa49fdc 100644 --- a/esphome/config_helpers.py +++ b/esphome/config_helpers.py @@ -10,6 +10,7 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE +from esphome.util import OrderedDict # Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum _PLATFORM_FRAMEWORK_LOOKUP = { @@ -17,6 +18,25 @@ _PLATFORM_FRAMEWORK_LOOKUP = { } +def merge_dicts_ordered(*dicts: dict) -> OrderedDict: + """Merge multiple dicts into an OrderedDict, preserving key order. + + This is a helper to ensure that dictionary merging preserves OrderedDict type, + which is important for operations like move_to_end(). + + Args: + *dicts: Variable number of dictionaries to merge (later dicts override earlier ones) + + Returns: + OrderedDict with merged contents + """ + result = OrderedDict() + for d in dicts: + if d: + result.update(d) + return result + + class Extend: def __init__(self, value): self.value = value @@ -60,7 +80,11 @@ def merge_config(full_old, full_new): if isinstance(new, dict): if not isinstance(old, dict): return new - res = old.copy() + # Preserve OrderedDict type by copying to OrderedDict if either input is OrderedDict + if isinstance(old, OrderedDict) or isinstance(new, OrderedDict): + res = OrderedDict(old) + else: + res = old.copy() for k, v in new.items(): if isinstance(v, Remove) and k in old: del res[k] diff --git a/tests/unit_tests/test_substitutions.py b/tests/unit_tests/test_substitutions.py index dd419aba9c..59396a4a83 100644 --- a/tests/unit_tests/test_substitutions.py +++ b/tests/unit_tests/test_substitutions.py @@ -2,9 +2,12 @@ import glob import logging from pathlib import Path -from esphome import yaml_util +from esphome import config as config_module, yaml_util from esphome.components import substitutions -from esphome.const import CONF_PACKAGES +from esphome.config_helpers import merge_config +from esphome.const import CONF_PACKAGES, CONF_SUBSTITUTIONS +from esphome.core import CORE +from esphome.util import OrderedDict _LOGGER = logging.getLogger(__name__) @@ -118,3 +121,200 @@ def test_substitutions_fixtures(fixture_path): if DEV_MODE: _LOGGER.error("Tests passed, but Dev mode is enabled.") assert not DEV_MODE # make sure DEV_MODE is disabled after you are finished. + + +def test_substitutions_with_command_line_maintains_ordered_dict() -> None: + """Test that substitutions remain an OrderedDict when command line substitutions are provided, + and that move_to_end() can be called successfully. + + This is a regression test for https://github.com/esphome/esphome/issues/11182 + where the config would become a regular dict and fail when move_to_end() was called. + """ + # Create an OrderedDict config with substitutions + config = OrderedDict() + config["esphome"] = {"name": "test"} + config[CONF_SUBSTITUTIONS] = {"var1": "value1", "var2": "value2"} + config["other_key"] = "other_value" + + # Command line substitutions that should override + command_line_subs = {"var2": "override", "var3": "new_value"} + + # Call do_substitution_pass with command line substitutions + substitutions.do_substitution_pass(config, command_line_subs) + + # Verify that config is still an OrderedDict + assert isinstance(config, OrderedDict), "Config should remain an OrderedDict" + + # Verify substitutions are at the beginning (move_to_end with last=False) + keys = list(config.keys()) + assert keys[0] == CONF_SUBSTITUTIONS, "Substitutions should be first key" + + # Verify substitutions were properly merged + assert config[CONF_SUBSTITUTIONS]["var1"] == "value1" + assert config[CONF_SUBSTITUTIONS]["var2"] == "override" + assert config[CONF_SUBSTITUTIONS]["var3"] == "new_value" + + # Verify config[CONF_SUBSTITUTIONS] is also an OrderedDict + assert isinstance(config[CONF_SUBSTITUTIONS], OrderedDict), ( + "Substitutions should be an OrderedDict" + ) + + +def test_substitutions_without_command_line_maintains_ordered_dict() -> None: + """Test that substitutions work correctly without command line substitutions.""" + config = OrderedDict() + config["esphome"] = {"name": "test"} + config[CONF_SUBSTITUTIONS] = {"var1": "value1"} + config["other_key"] = "other_value" + + # Call without command line substitutions + substitutions.do_substitution_pass(config, None) + + # Verify that config is still an OrderedDict + assert isinstance(config, OrderedDict), "Config should remain an OrderedDict" + + # Verify substitutions are at the beginning + keys = list(config.keys()) + assert keys[0] == CONF_SUBSTITUTIONS, "Substitutions should be first key" + + +def test_substitutions_after_merge_config_maintains_ordered_dict() -> None: + """Test that substitutions work after merge_config (packages scenario). + + This is a regression test for https://github.com/esphome/esphome/issues/11182 + where using packages would cause config to become a regular dict, breaking move_to_end(). + """ + # Simulate what happens with packages - merge two OrderedDict configs + base_config = OrderedDict() + base_config["esphome"] = {"name": "base"} + base_config[CONF_SUBSTITUTIONS] = {"var1": "value1"} + + package_config = OrderedDict() + package_config["sensor"] = [{"platform": "template"}] + package_config[CONF_SUBSTITUTIONS] = {"var2": "value2"} + + # Merge configs (simulating package merge) + merged_config = merge_config(base_config, package_config) + + # Verify merged config is still an OrderedDict + assert isinstance(merged_config, OrderedDict), ( + "Merged config should be an OrderedDict" + ) + + # Now try to run substitution pass on the merged config + substitutions.do_substitution_pass(merged_config, None) + + # Should not raise AttributeError + assert isinstance(merged_config, OrderedDict), ( + "Config should still be OrderedDict after substitution pass" + ) + keys = list(merged_config.keys()) + assert keys[0] == CONF_SUBSTITUTIONS, "Substitutions should be first key" + + +def test_validate_config_with_command_line_substitutions_maintains_ordered_dict( + tmp_path, +) -> None: + """Test that validate_config preserves OrderedDict when merging command-line substitutions. + + This tests the code path in config.py where result[CONF_SUBSTITUTIONS] is set + using merge_dicts_ordered() with command-line substitutions provided. + """ + # Create a minimal valid config + test_config = OrderedDict() + test_config["esphome"] = {"name": "test_device", "platform": "ESP32"} + test_config[CONF_SUBSTITUTIONS] = OrderedDict({"var1": "value1", "var2": "value2"}) + test_config["esp32"] = {"board": "esp32dev"} + + # Command line substitutions that should override + command_line_subs = {"var2": "override", "var3": "new_value"} + + # Set up CORE for the test with a proper Path object + test_yaml = tmp_path / "test.yaml" + test_yaml.write_text("# test config") + CORE.config_path = test_yaml + + # Call validate_config with command line substitutions + result = config_module.validate_config(test_config, command_line_subs) + + # Verify that result[CONF_SUBSTITUTIONS] is an OrderedDict + assert isinstance(result.get(CONF_SUBSTITUTIONS), OrderedDict), ( + "Result substitutions should be an OrderedDict" + ) + + # Verify substitutions were properly merged + assert result[CONF_SUBSTITUTIONS]["var1"] == "value1" + assert result[CONF_SUBSTITUTIONS]["var2"] == "override" + assert result[CONF_SUBSTITUTIONS]["var3"] == "new_value" + + +def test_validate_config_without_command_line_substitutions_maintains_ordered_dict( + tmp_path, +) -> None: + """Test that validate_config preserves OrderedDict without command-line substitutions. + + This tests the code path in config.py where result[CONF_SUBSTITUTIONS] is set + using merge_dicts_ordered() when command_line_substitutions is None. + """ + # Create a minimal valid config + test_config = OrderedDict() + test_config["esphome"] = {"name": "test_device", "platform": "ESP32"} + test_config[CONF_SUBSTITUTIONS] = OrderedDict({"var1": "value1", "var2": "value2"}) + test_config["esp32"] = {"board": "esp32dev"} + + # Set up CORE for the test with a proper Path object + test_yaml = tmp_path / "test.yaml" + test_yaml.write_text("# test config") + CORE.config_path = test_yaml + + # Call validate_config without command line substitutions + result = config_module.validate_config(test_config, None) + + # Verify that result[CONF_SUBSTITUTIONS] is an OrderedDict + assert isinstance(result.get(CONF_SUBSTITUTIONS), OrderedDict), ( + "Result substitutions should be an OrderedDict" + ) + + # Verify substitutions are unchanged + assert result[CONF_SUBSTITUTIONS]["var1"] == "value1" + assert result[CONF_SUBSTITUTIONS]["var2"] == "value2" + + +def test_merge_config_preserves_ordered_dict() -> None: + """Test that merge_config preserves OrderedDict type. + + This is a regression test to ensure merge_config doesn't lose OrderedDict type + when merging configs, which causes AttributeError on move_to_end(). + """ + # Test OrderedDict + dict = OrderedDict + od = OrderedDict([("a", 1), ("b", 2)]) + d = {"b": 20, "c": 3} + result = merge_config(od, d) + assert isinstance(result, OrderedDict), ( + "OrderedDict + dict should return OrderedDict" + ) + + # Test dict + OrderedDict = OrderedDict + d = {"a": 1, "b": 2} + od = OrderedDict([("b", 20), ("c", 3)]) + result = merge_config(d, od) + assert isinstance(result, OrderedDict), ( + "dict + OrderedDict should return OrderedDict" + ) + + # Test OrderedDict + OrderedDict = OrderedDict + od1 = OrderedDict([("a", 1), ("b", 2)]) + od2 = OrderedDict([("b", 20), ("c", 3)]) + result = merge_config(od1, od2) + assert isinstance(result, OrderedDict), ( + "OrderedDict + OrderedDict should return OrderedDict" + ) + + # Test that dict + dict still returns regular dict (no unnecessary conversion) + d1 = {"a": 1, "b": 2} + d2 = {"b": 20, "c": 3} + result = merge_config(d1, d2) + assert isinstance(result, dict), "dict + dict should return dict" + assert not isinstance(result, OrderedDict), ( + "dict + dict should not return OrderedDict" + ) From 14d76e9e4e3cc4d36ed2ba444bb03504edad573e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 17:36:03 -1000 Subject: [PATCH 101/526] [ci] Merge components with different buses to reduce CI time (#11251) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .github/workflows/ci.yml | 62 ++- esphome/components/esp8266/__init__.py | 13 +- esphome/components/esp8266/iram_fix.py.script | 44 +++ script/analyze_component_buses.py | 142 +++++++ script/list-components.py | 39 +- script/merge_component_configs.py | 136 +++++-- script/split_components_for_ci.py | 95 ++++- script/test_build_components.py | 373 +++++++++++++----- .../components/ade7880/test.esp8266-ard.yaml | 2 +- tests/components/ade7953_i2c/common.yaml | 3 + tests/components/ade7953_spi/common.yaml | 6 +- tests/components/as3935_i2c/common.yaml | 3 + tests/components/as3935_spi/common.yaml | 3 + tests/components/axs15231/common.yaml | 4 +- tests/components/bme280_i2c/common.yaml | 6 +- tests/components/bme280_spi/common.yaml | 6 +- tests/components/bmp280_i2c/common.yaml | 4 +- tests/components/bmp280_spi/common.yaml | 4 +- tests/components/bmp3xx_i2c/common.yaml | 2 + tests/components/bmp3xx_spi/common.yaml | 2 + tests/components/camera/test.esp32-idf.yaml | 2 +- .../camera_encoder/test.esp32-idf.yaml | 2 +- tests/components/chsc6x/test.esp32-idf.yaml | 2 + tests/components/ektf2232/common.yaml | 4 +- tests/components/ens160_i2c/common.yaml | 3 + tests/components/ens160_spi/common.yaml | 3 + .../esp32_camera/test.esp32-idf.yaml | 2 +- .../test.esp32-idf.yaml | 2 +- tests/components/font/common.yaml | 1 + tests/components/ft63x6/test.esp8266-ard.yaml | 2 +- tests/components/graph/common.yaml | 1 + .../graphical_display_menu/common.yaml | 4 +- tests/components/gt911/common.yaml | 4 +- tests/components/hx711/test.esp8266-ard.yaml | 4 +- tests/components/ina2xx_i2c/common.yaml | 24 +- tests/components/ina2xx_spi/common.yaml | 24 +- tests/components/lilygo_t5_47/common.yaml | 4 +- tests/components/pn532_i2c/common.yaml | 4 +- tests/components/pn532_spi/common.yaml | 4 +- tests/components/pn7160_i2c/common.yaml | 18 +- tests/components/pn7160_spi/common.yaml | 18 +- tests/components/rc522_i2c/common.yaml | 4 +- tests/components/rc522_spi/common.yaml | 6 +- .../selec_meter/test.esp8266-ard.yaml | 2 +- .../sn74hc595/test.esp8266-ard.yaml | 4 +- tests/components/ssd1306_i2c/common.yaml | 2 +- tests/components/ssd1306_spi/common.yaml | 1 + tests/components/ssd1327_i2c/common.yaml | 2 +- tests/components/ssd1327_spi/common.yaml | 1 + tests/components/st7567_i2c/common.yaml | 2 +- tests/components/st7567_spi/common.yaml | 1 + tests/components/syslog/common.yaml | 3 +- tests/components/tt21100/common.yaml | 4 +- .../components/wk2132_i2c/test.esp32-idf.yaml | 1 + .../wk2132_i2c/test.esp32-s3-idf.yaml | 1 + tests/components/wk2132_spi/common.yaml | 8 +- .../components/wk2132_spi/test.esp32-idf.yaml | 1 + .../wk2132_spi/test.esp32-s3-idf.yaml | 1 + .../components/wk2168_i2c/test.esp32-idf.yaml | 1 + .../wk2168_i2c/test.esp32-s3-idf.yaml | 1 + .../components/wk2168_spi/test.esp32-idf.yaml | 1 + .../wk2168_spi/test.esp32-s3-idf.yaml | 1 + .../components/wk2204_i2c/test.esp32-idf.yaml | 1 + .../wk2204_i2c/test.esp32-s3-idf.yaml | 1 + tests/components/wk2204_spi/common.yaml | 12 +- .../components/wk2204_spi/test.esp32-idf.yaml | 1 + .../wk2204_spi/test.esp32-s3-idf.yaml | 1 + .../components/wk2212_i2c/test.esp32-idf.yaml | 1 + .../wk2212_i2c/test.esp32-s3-idf.yaml | 1 + .../components/wk2212_spi/test.esp32-idf.yaml | 1 + .../wk2212_spi/test.esp32-s3-idf.yaml | 1 + .../build_components_base.esp32-idf.yaml | 6 +- .../{camera => i2c_camera}/esp32-idf.yaml | 11 +- .../common/uart_bridge_2/esp32-idf.yaml | 11 + .../common/uart_bridge_2/esp32-s3-idf.yaml | 11 + .../common/uart_bridge_4/esp32-idf.yaml | 11 + .../common/uart_bridge_4/esp32-s3-idf.yaml | 11 + .../partitions_testing.csv | 10 + 78 files changed, 954 insertions(+), 266 deletions(-) create mode 100644 esphome/components/esp8266/iram_fix.py.script rename tests/test_build_components/common/{camera => i2c_camera}/esp32-idf.yaml (83%) create mode 100644 tests/test_build_components/common/uart_bridge_2/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart_bridge_2/esp32-s3-idf.yaml create mode 100644 tests/test_build_components/common/uart_bridge_4/esp32-idf.yaml create mode 100644 tests/test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml create mode 100644 tests/test_build_components/partitions_testing.csv diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 163e9ab9ec..87e182fe4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -379,7 +379,16 @@ jobs: # Use intelligent splitter that groups components with same bus configs components='${{ needs.determine-jobs.outputs.changed-components-with-tests }}' - directly_changed='${{ needs.determine-jobs.outputs.directly-changed-components-with-tests }}' + + # Only isolate directly changed components when targeting dev branch + # For beta/release branches, group everything for faster CI + if [[ "${{ github.base_ref }}" == beta* ]] || [[ "${{ github.base_ref }}" == release* ]]; then + directly_changed='[]' + echo "Target branch: ${{ github.base_ref }} - grouping all components" + else + directly_changed='${{ needs.determine-jobs.outputs.directly-changed-components-with-tests }}' + echo "Target branch: ${{ github.base_ref }} - isolating directly changed components" + fi echo "Splitting components intelligently..." output=$(python3 script/split_components_for_ci.py --components "$components" --directly-changed "$directly_changed" --batch-size 40 --output github) @@ -396,7 +405,7 @@ jobs: if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0 strategy: fail-fast: false - max-parallel: ${{ (github.base_ref == 'beta' || github.base_ref == 'release') && 8 || 4 }} + max-parallel: ${{ (startsWith(github.base_ref, 'beta') || startsWith(github.base_ref, 'release')) && 8 || 4 }} matrix: components: ${{ fromJson(needs.test-build-components-splitter.outputs.matrix) }} steps: @@ -424,18 +433,31 @@ jobs: - name: Validate and compile components with intelligent grouping run: | . venv/bin/activate - # Use /mnt for build files (70GB available vs ~29GB on /) - # Bind mount PlatformIO directory to /mnt (tools, packages, build cache all go there) - sudo mkdir -p /mnt/platformio - sudo chown $USER:$USER /mnt/platformio - mkdir -p ~/.platformio - sudo mount --bind /mnt/platformio ~/.platformio - # Bind mount test build directory to /mnt - sudo mkdir -p /mnt/test_build_components_build - sudo chown $USER:$USER /mnt/test_build_components_build - mkdir -p tests/test_build_components/build - sudo mount --bind /mnt/test_build_components_build tests/test_build_components/build + # Check if /mnt has more free space than / before bind mounting + # Extract available space in KB for comparison + root_avail=$(df -k / | awk 'NR==2 {print $4}') + mnt_avail=$(df -k /mnt 2>/dev/null | awk 'NR==2 {print $4}') + + echo "Available space: / has ${root_avail}KB, /mnt has ${mnt_avail}KB" + + # Only use /mnt if it has more space than / + if [ -n "$mnt_avail" ] && [ "$mnt_avail" -gt "$root_avail" ]; then + echo "Using /mnt for build files (more space available)" + # Bind mount PlatformIO directory to /mnt (tools, packages, build cache all go there) + sudo mkdir -p /mnt/platformio + sudo chown $USER:$USER /mnt/platformio + mkdir -p ~/.platformio + sudo mount --bind /mnt/platformio ~/.platformio + + # Bind mount test build directory to /mnt + sudo mkdir -p /mnt/test_build_components_build + sudo chown $USER:$USER /mnt/test_build_components_build + mkdir -p tests/test_build_components/build + sudo mount --bind /mnt/test_build_components_build tests/test_build_components/build + else + echo "Using / for build files (more space available than /mnt or /mnt unavailable)" + fi # Convert space-separated components to comma-separated for Python script components_csv=$(echo "${{ matrix.components }}" | tr ' ' ',') @@ -448,7 +470,7 @@ jobs: # - This catches pin conflicts and other issues in directly changed code # - Grouped tests use --testing-mode to allow config merging (disables some checks) # - Dependencies are safe to group since they weren't modified in this PR - if [ "${{ github.base_ref }}" = "beta" ] || [ "${{ github.base_ref }}" = "release" ]; then + if [[ "${{ github.base_ref }}" == beta* ]] || [[ "${{ github.base_ref }}" == release* ]]; then directly_changed_csv="" echo "Testing components: $components_csv" echo "Target branch: ${{ github.base_ref }} - grouping all components" @@ -459,6 +481,11 @@ jobs: fi echo "" + # Show disk space before validation (after bind mounts setup) + echo "Disk space before config validation:" + df -h + echo "" + # Run config validation with grouping and isolation python3 script/test_build_components.py -e config -c "$components_csv" -f --isolate "$directly_changed_csv" @@ -466,6 +493,11 @@ jobs: echo "Config validation passed! Starting compilation..." echo "" + # Show disk space before compilation + echo "Disk space before compilation:" + df -h + echo "" + # Run compilation with grouping and isolation python3 script/test_build_components.py -e compile -c "$components_csv" -f --isolate "$directly_changed_csv" @@ -474,7 +506,7 @@ jobs: runs-on: ubuntu-latest needs: - common - if: github.event_name == 'pull_request' && github.base_ref != 'beta' && github.base_ref != 'release' + if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release') steps: - name: Check out code from GitHub uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 8a7fbbcb0a..9d8e6b7d1e 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -190,7 +190,7 @@ async def to_code(config): cg.add_define("ESPHOME_VARIANT", "ESP8266") cg.add_define(ThreadModel.SINGLE) - cg.add_platformio_option("extra_scripts", ["post:post_build.py"]) + cg.add_platformio_option("extra_scripts", ["pre:iram_fix.py", "post:post_build.py"]) conf = config[CONF_FRAMEWORK] cg.add_platformio_option("framework", "arduino") @@ -230,6 +230,12 @@ async def to_code(config): # For cases where nullptrs can be handled, use nothrow: `new (std::nothrow) T;` cg.add_build_flag("-DNEW_OOM_ABORT") + # In testing mode, fake a larger IRAM to allow linking grouped component tests + # Real ESP8266 hardware only has 32KB IRAM, but for CI testing we pretend it has 2MB + # This is done via a pre-build script that generates a custom linker script + if CORE.testing_mode: + cg.add_build_flag("-DESPHOME_TESTING_MODE") + cg.add_platformio_option("board_build.flash_mode", config[CONF_BOARD_FLASH_MODE]) ver: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] @@ -265,3 +271,8 @@ def copy_files(): post_build_file, CORE.relative_build_path("post_build.py"), ) + iram_fix_file = dir / "iram_fix.py.script" + copy_file_if_changed( + iram_fix_file, + CORE.relative_build_path("iram_fix.py"), + ) diff --git a/esphome/components/esp8266/iram_fix.py.script b/esphome/components/esp8266/iram_fix.py.script new file mode 100644 index 0000000000..96bddc2ced --- /dev/null +++ b/esphome/components/esp8266/iram_fix.py.script @@ -0,0 +1,44 @@ +import os +import re + +# pylint: disable=E0602 +Import("env") # noqa + + +def patch_linker_script_after_preprocess(source, target, env): + """Patch the local linker script after PlatformIO preprocesses it.""" + # Check if we're in testing mode by looking for the define + build_flags = env.get("BUILD_FLAGS", []) + testing_mode = any("-DESPHOME_TESTING_MODE" in flag for flag in build_flags) + + if not testing_mode: + return + + # Get the local linker script path + build_dir = env.subst("$BUILD_DIR") + local_ld = os.path.join(build_dir, "ld", "local.eagle.app.v6.common.ld") + + if not os.path.exists(local_ld): + return + + # Read the linker script + with open(local_ld, "r") as f: + content = f.read() + + # Replace IRAM size from 0x8000 (32KB) to 0x200000 (2MB) + # The line looks like: iram1_0_seg : org = 0x40100000, len = 0x8000 + updated = re.sub( + r"(iram1_0_seg\s*:\s*org\s*=\s*0x40100000\s*,\s*len\s*=\s*)0x8000", + r"\g<1>0x200000", + content, + ) + + if updated != content: + with open(local_ld, "w") as f: + f.write(updated) + print("ESPHome: Patched IRAM size to 2MB for testing mode") + + +# Hook into the build process right before linking +# This runs after PlatformIO has already preprocessed the linker scripts +env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", patch_linker_script_after_preprocess) diff --git a/script/analyze_component_buses.py b/script/analyze_component_buses.py index 24854178a0..fc7d021cbe 100755 --- a/script/analyze_component_buses.py +++ b/script/analyze_component_buses.py @@ -56,6 +56,10 @@ DIRECT_BUS_TYPES = ("i2c", "spi", "uart", "modbus") # These components can be merged with any other group NO_BUSES_SIGNATURE = "no_buses" +# Prefix for isolated component signatures +# Isolated components have unique signatures and cannot be merged with others +ISOLATED_SIGNATURE_PREFIX = "isolated_" + # Base bus components - these ARE the bus implementations and should not # be flagged as needing migration since they are the platform/base components BASE_BUS_COMPONENTS = { @@ -75,6 +79,7 @@ ISOLATED_COMPONENTS = { "ethernet": "Defines ethernet: which conflicts with wifi: used by most components", "ethernet_info": "Related to ethernet component which conflicts with wifi", "lvgl": "Defines multiple SDL displays on host platform that conflict when merged with other display configs", + "mapping": "Uses dict format for image/display sections incompatible with standard list format - ESPHome merge_config cannot handle", "openthread": "Conflicts with wifi: used by most components", "openthread_info": "Conflicts with wifi: used by most components", "matrix_keypad": "Needs isolation due to keypad", @@ -368,6 +373,143 @@ def analyze_all_components( return components, non_groupable, direct_bus_components +@lru_cache(maxsize=256) +def _get_bus_configs(buses: tuple[str, ...]) -> frozenset[tuple[str, str]]: + """Map bus type to set of configs for that type. + + Args: + buses: Tuple of bus package names (e.g., ("uart_9600", "i2c")) + + Returns: + Frozenset of (base_type, full_config) tuples + Example: frozenset({("uart", "uart_9600"), ("i2c", "i2c")}) + """ + # Split on underscore to get base type: "uart_9600" -> "uart", "i2c" -> "i2c" + return frozenset((bus.split("_", 1)[0], bus) for bus in buses) + + +@lru_cache(maxsize=1024) +def are_buses_compatible(buses1: tuple[str, ...], buses2: tuple[str, ...]) -> bool: + """Check if two bus tuples are compatible for merging. + + Two bus lists are compatible if they don't have conflicting configurations + for the same bus type. For example: + - ("ble", "uart") and ("i2c",) are compatible (different buses) + - ("uart_9600",) and ("uart_19200",) are NOT compatible (same bus, different configs) + - ("uart_9600",) and ("uart_9600",) are compatible (same bus, same config) + + Args: + buses1: First tuple of bus package names + buses2: Second tuple of bus package names + + Returns: + True if buses can be merged without conflicts + """ + configs1 = _get_bus_configs(buses1) + configs2 = _get_bus_configs(buses2) + + # Group configs by base type + bus_types1: dict[str, set[str]] = {} + for base_type, full_config in configs1: + if base_type not in bus_types1: + bus_types1[base_type] = set() + bus_types1[base_type].add(full_config) + + bus_types2: dict[str, set[str]] = {} + for base_type, full_config in configs2: + if base_type not in bus_types2: + bus_types2[base_type] = set() + bus_types2[base_type].add(full_config) + + # Check for conflicts: same bus type with different configs + for bus_type, configs in bus_types1.items(): + if bus_type not in bus_types2: + continue # No conflict - different bus types + # Same bus type - check if configs match + if configs != bus_types2[bus_type]: + return False # Conflict - same bus type, different configs + + return True # No conflicts found + + +def merge_compatible_bus_groups( + grouped_components: dict[tuple[str, str], list[str]], +) -> dict[tuple[str, str], list[str]]: + """Merge groups with compatible (non-conflicting) buses. + + This function takes groups keyed by (platform, bus_signature) and merges + groups that share the same platform and have compatible bus configurations. + Two groups can be merged if their buses don't conflict - meaning they don't + have different configurations for the same bus type. + + For example: + - ["ble"] + ["uart"] = compatible (different buses) + - ["uart_9600"] + ["uart_19200"] = incompatible (same bus, different configs) + - ["uart_9600"] + ["uart_9600"] = compatible (same bus, same config) + + Args: + grouped_components: Dictionary mapping (platform, signature) to list of component names + + Returns: + Dictionary with same structure but with compatible groups merged + """ + merged_groups: dict[tuple[str, str], list[str]] = {} + processed_keys: set[tuple[str, str]] = set() + + for (platform1, sig1), comps1 in sorted(grouped_components.items()): + if (platform1, sig1) in processed_keys: + continue + + # Skip NO_BUSES_SIGNATURE - kept separate for flexible batch distribution + # These components have no bus requirements and can be added to any batch + # as "fillers" for load balancing across CI runners + if sig1 == NO_BUSES_SIGNATURE: + merged_groups[(platform1, sig1)] = comps1 + processed_keys.add((platform1, sig1)) + continue + + # Skip isolated components - they can't be merged with others + if sig1.startswith(ISOLATED_SIGNATURE_PREFIX): + merged_groups[(platform1, sig1)] = comps1 + processed_keys.add((platform1, sig1)) + continue + + # Start with this group's components + merged_comps: list[str] = list(comps1) + merged_sig: str = sig1 + processed_keys.add((platform1, sig1)) + + # Get buses for this group as tuple for caching + buses1: tuple[str, ...] = tuple(sorted(sig1.split("+"))) + + # Try to merge with other groups on same platform + for (platform2, sig2), comps2 in sorted(grouped_components.items()): + if (platform2, sig2) in processed_keys: + continue + if platform2 != platform1: + continue # Different platforms can't be merged + if sig2 == NO_BUSES_SIGNATURE: + continue # Keep separate for flexible batch distribution + if sig2.startswith(ISOLATED_SIGNATURE_PREFIX): + continue # Isolated components can't be merged + + # Check if buses are compatible + buses2: tuple[str, ...] = tuple(sorted(sig2.split("+"))) + if are_buses_compatible(buses1, buses2): + # Compatible! Merge this group + merged_comps.extend(comps2) + processed_keys.add((platform2, sig2)) + # Update merged signature to include all unique buses + all_buses: set[str] = set(buses1) | set(buses2) + merged_sig = "+".join(sorted(all_buses)) + buses1 = tuple(sorted(all_buses)) # Update for next iteration + + # Store merged group + merged_groups[(platform1, merged_sig)] = merged_comps + + return merged_groups + + def create_grouping_signature( platform_buses: dict[str, list[str]], platform: str ) -> str: diff --git a/script/list-components.py b/script/list-components.py index dffff6801a..9abb2bc345 100755 --- a/script/list-components.py +++ b/script/list-components.py @@ -185,17 +185,20 @@ def main(): "-c", "--changed", action="store_true", - help="List all components required for testing based on changes (includes dependencies)", + help="List all components with dependencies (used by clang-tidy). " + "When base test infrastructure changes, returns ALL components.", ) parser.add_argument( "--changed-direct", action="store_true", - help="List only directly changed components (without dependencies)", + help="List only directly changed components, ignoring infrastructure changes " + "(used by CI for isolation decisions)", ) parser.add_argument( "--changed-with-deps", action="store_true", - help="Output JSON with both directly changed and all changed components", + help="Output JSON with both directly changed and all changed components " + "(with dependencies), ignoring infrastructure changes (used by CI for test determination)", ) parser.add_argument( "-b", "--branch", help="Branch to compare changed files against" @@ -213,12 +216,34 @@ def main(): # When --changed* is passed, only get the changed files changed = changed_files(args.branch) - # If any base test file(s) changed, there's no need to filter out components - if any("tests/test_build_components" in file for file in changed): - # Need to get all component files + # If any base test file(s) changed, we need to check all components + # BUT only for --changed (used by clang-tidy for comprehensive checking) + # NOT for --changed-direct or --changed-with-deps (used by CI for targeted testing) + # + # Flag usage: + # - --changed: Used by clang-tidy (script/helpers.py get_changed_components) + # Returns: All components with dependencies when base test files change + # Reason: Test infrastructure changes may affect any component + # + # - --changed-direct: Used by CI isolation (script/determine-jobs.py) + # Returns: Only components with actual code changes (not infrastructure) + # Reason: Only directly changed components need isolated testing + # + # - --changed-with-deps: Used by CI test determination (script/determine-jobs.py) + # Returns: Components with code changes + their dependencies (not infrastructure) + # Reason: CI needs to test changed components and their dependents + base_test_changed = any( + "tests/test_build_components" in file for file in changed + ) + + if base_test_changed and not args.changed_direct and not args.changed_with_deps: + # Base test infrastructure changed - load all component files + # This is for --changed (clang-tidy) which needs comprehensive checking files = get_all_component_files() else: - # Only look at changed component files + # Only look at changed component files (ignore infrastructure changes) + # For --changed-direct: only actual component code changes matter (for isolation) + # For --changed-with-deps: only actual component code changes matter (for testing) files = [f for f in changed if filter_component_files(f)] else: # Get all component files diff --git a/script/merge_component_configs.py b/script/merge_component_configs.py index a19b65038c..59774edba9 100755 --- a/script/merge_component_configs.py +++ b/script/merge_component_configs.py @@ -16,6 +16,7 @@ The merger handles: from __future__ import annotations import argparse +from functools import lru_cache from pathlib import Path import re import sys @@ -28,6 +29,10 @@ from esphome import yaml_util from esphome.config_helpers import merge_config from script.analyze_component_buses import PACKAGE_DEPENDENCIES, get_common_bus_packages +# Prefix for dependency markers in package tracking +# Used to mark packages that are included transitively (e.g., uart via modbus) +DEPENDENCY_MARKER_PREFIX = "_dep_" + def load_yaml_file(yaml_file: Path) -> dict: """Load YAML file using ESPHome's YAML loader. @@ -44,6 +49,34 @@ def load_yaml_file(yaml_file: Path) -> dict: return yaml_util.load_yaml(yaml_file) +@lru_cache(maxsize=256) +def get_component_packages( + component_name: str, platform: str, tests_dir_str: str +) -> dict: + """Get packages dict from a component's test file with caching. + + This function is cached to avoid re-loading and re-parsing the same file + multiple times when extracting packages during cross-bus merging. + + Args: + component_name: Name of the component + platform: Platform name (e.g., "esp32-idf") + tests_dir_str: String path to tests/components directory (must be string for cache hashability) + + Returns: + Dictionary with 'packages' key containing the raw packages dict from the YAML, + or empty dict if no packages section exists + """ + tests_dir = Path(tests_dir_str) + test_file = tests_dir / component_name / f"test.{platform}.yaml" + comp_data = load_yaml_file(test_file) + + if "packages" not in comp_data or not isinstance(comp_data["packages"], dict): + return {} + + return comp_data["packages"] + + def extract_packages_from_yaml(data: dict) -> dict[str, str]: """Extract COMMON BUS package includes from parsed YAML. @@ -82,7 +115,7 @@ def extract_packages_from_yaml(data: dict) -> dict[str, str]: if dep not in common_bus_packages: continue # Mark as included via dependency - packages[f"_dep_{dep}"] = f"(included via {name})" + packages[f"{DEPENDENCY_MARKER_PREFIX}{dep}"] = f"(included via {name})" return packages @@ -195,6 +228,9 @@ def merge_component_configs( # Start with empty config merged_config_data = {} + # Convert tests_dir to string for caching + tests_dir_str = str(tests_dir) + # Process each component for comp_name in component_names: comp_dir = tests_dir / comp_name @@ -206,26 +242,29 @@ def merge_component_configs( # Load the component's test file comp_data = load_yaml_file(test_file) - # Validate packages are compatible - # Components with no packages (no_buses) can merge with any group + # Merge packages from all components (cross-bus merging) + # Components can have different packages (e.g., one with ble, another with uart) + # as long as they don't conflict (checked by are_buses_compatible before calling this) comp_packages = extract_packages_from_yaml(comp_data) if all_packages is None: - # First component - set the baseline - all_packages = comp_packages - elif not comp_packages: - # This component has no packages (no_buses) - it can merge with any group - pass - elif not all_packages: - # Previous components had no packages, but this one does - adopt these packages - all_packages = comp_packages - elif comp_packages != all_packages: - # Both have packages but they differ - this is an error - raise ValueError( - f"Component {comp_name} has different packages than previous components. " - f"Expected: {all_packages}, Got: {comp_packages}. " - f"All components must use the same common bus configs to be merged." - ) + # First component - initialize package dict + all_packages = comp_packages if comp_packages else {} + elif comp_packages: + # Merge packages - combine all unique package types + # If both have the same package type, verify they're identical + for pkg_name, pkg_config in comp_packages.items(): + if pkg_name in all_packages: + # Same package type - verify config matches + if all_packages[pkg_name] != pkg_config: + raise ValueError( + f"Component {comp_name} has conflicting config for package '{pkg_name}'. " + f"Expected: {all_packages[pkg_name]}, Got: {pkg_config}. " + f"Components with conflicting bus configs cannot be merged." + ) + else: + # New package type - add it + all_packages[pkg_name] = pkg_config # Handle $component_dir by replacing with absolute path # This allows components that use local file references to be grouped @@ -287,26 +326,51 @@ def merge_component_configs( # merge_config handles list merging with ID-based deduplication automatically merged_config_data = merge_config(merged_config_data, comp_data) - # Add packages back (only once, since they're identical) - # IMPORTANT: Only re-add common bus packages (spi, i2c, uart, etc.) + # Add merged packages back (union of all component packages) + # IMPORTANT: Only include common bus packages (spi, i2c, uart, etc.) # Do NOT re-add component-specific packages as they contain unprefixed $component_dir refs if all_packages: - first_comp_data = load_yaml_file( - tests_dir / component_names[0] / f"test.{platform}.yaml" - ) - if "packages" in first_comp_data and isinstance( - first_comp_data["packages"], dict - ): - # Filter to only include common bus packages - # Only dict format can contain common bus packages - common_bus_packages = get_common_bus_packages() - filtered_packages = { - name: value - for name, value in first_comp_data["packages"].items() - if name in common_bus_packages - } - if filtered_packages: - merged_config_data["packages"] = filtered_packages + # Build packages dict from merged all_packages + # all_packages is a dict mapping package_name -> str(package_value) + # We need to reconstruct the actual package values by loading them from any component + # Since packages with the same name must have identical configs (verified above), + # we can load the package value from the first component that has each package + common_bus_packages = get_common_bus_packages() + merged_packages: dict[str, Any] = {} + + # Collect packages that are included as dependencies + # If modbus is present, uart is included via modbus.packages.uart + packages_to_skip: set[str] = set() + for pkg_name in all_packages: + if pkg_name.startswith(DEPENDENCY_MARKER_PREFIX): + # Extract the actual package name (remove _dep_ prefix) + dep_name = pkg_name[len(DEPENDENCY_MARKER_PREFIX) :] + packages_to_skip.add(dep_name) + + for pkg_name in all_packages: + # Skip dependency markers + if pkg_name.startswith(DEPENDENCY_MARKER_PREFIX): + continue + # Skip non-common-bus packages + if pkg_name not in common_bus_packages: + continue + # Skip packages that are included as dependencies of other packages + # This prevents duplicate definitions (e.g., uart via modbus + uart separately) + if pkg_name in packages_to_skip: + continue + + # Find a component that has this package and extract its value + # Uses cached lookup to avoid re-loading the same files + for comp_name in component_names: + comp_packages = get_component_packages( + comp_name, platform, tests_dir_str + ) + if pkg_name in comp_packages: + merged_packages[pkg_name] = comp_packages[pkg_name] + break + + if merged_packages: + merged_config_data["packages"] = merged_packages # Deduplicate items with same ID (keeps first occurrence) merged_config_data = deduplicate_by_id(merged_config_data) diff --git a/script/split_components_for_ci.py b/script/split_components_for_ci.py index 9730db4988..dff46d3619 100755 --- a/script/split_components_for_ci.py +++ b/script/split_components_for_ci.py @@ -22,9 +22,11 @@ sys.path.insert(0, str(Path(__file__).parent.parent)) from script.analyze_component_buses import ( ISOLATED_COMPONENTS, + ISOLATED_SIGNATURE_PREFIX, NO_BUSES_SIGNATURE, analyze_all_components, create_grouping_signature, + merge_compatible_bus_groups, ) # Weighting for batch creation @@ -33,6 +35,10 @@ from script.analyze_component_buses import ( ISOLATED_WEIGHT = 10 GROUPABLE_WEIGHT = 1 +# Platform used for batching (platform-agnostic batching) +# Batches are split across CI runners and each runner tests all platforms +ALL_PLATFORMS = "all" + def has_test_files(component_name: str, tests_dir: Path) -> bool: """Check if a component has test files. @@ -57,7 +63,7 @@ def create_intelligent_batches( tests_dir: Path, batch_size: int = 40, directly_changed: set[str] | None = None, -) -> list[list[str]]: +) -> tuple[list[list[str]], dict[tuple[str, str], list[str]]]: """Create batches optimized for component grouping. Args: @@ -67,7 +73,9 @@ def create_intelligent_batches( directly_changed: Set of directly changed components (for logging only) Returns: - List of component batches (lists of component names) + Tuple of (batches, signature_groups) where: + - batches: List of component batches (lists of component names) + - signature_groups: Dict mapping (platform, signature) to component lists """ # Filter out components without test files # Platform components like 'climate' and 'climate_ir' don't have test files @@ -91,8 +99,9 @@ def create_intelligent_batches( # Group components by their bus signature ONLY (ignore platform) # All platforms will be tested by test_build_components.py for each batch - # Key: signature, Value: list of components - signature_groups: dict[str, list[str]] = defaultdict(list) + # Key: (platform, signature), Value: list of components + # We use ALL_PLATFORMS since batching is platform-agnostic + signature_groups: dict[tuple[str, str], list[str]] = defaultdict(list) for component in components_with_tests: # Components that can't be grouped get unique signatures @@ -107,7 +116,9 @@ def create_intelligent_batches( or (directly_changed and component in directly_changed) ) if is_isolated: - signature_groups[f"isolated_{component}"].append(component) + signature_groups[ + (ALL_PLATFORMS, f"{ISOLATED_SIGNATURE_PREFIX}{component}") + ].append(component) continue # Get signature from any platform (they should all have the same buses) @@ -117,11 +128,17 @@ def create_intelligent_batches( if buses: signature = create_grouping_signature({platform: buses}, platform) # Group by signature only - platform doesn't matter for batching - signature_groups[signature].append(component) + # Use ALL_PLATFORMS since we're batching across all platforms + signature_groups[(ALL_PLATFORMS, signature)].append(component) break # Only use first platform for grouping else: # No buses found for any platform - can be grouped together - signature_groups[NO_BUSES_SIGNATURE].append(component) + signature_groups[(ALL_PLATFORMS, NO_BUSES_SIGNATURE)].append(component) + + # Merge compatible bus groups (cross-bus optimization) + # This allows components with different buses (ble + uart) to be batched together + # improving the efficiency of test_build_components.py grouping + signature_groups = merge_compatible_bus_groups(signature_groups) # Create batches by keeping signature groups together # Components with the same signature stay in the same batches @@ -132,8 +149,8 @@ def create_intelligent_batches( # 2. Sort groupable signatures by size (largest first) # 3. "no_buses" components CAN be grouped together def sort_key(item): - signature, components = item - is_isolated = signature.startswith("isolated_") + (_platform, signature), components = item + is_isolated = signature.startswith(ISOLATED_SIGNATURE_PREFIX) # Put "isolated_*" last (1), groupable first (0) # Within each category, sort by size (largest first) return (is_isolated, -len(components)) @@ -149,8 +166,8 @@ def create_intelligent_batches( current_batch = [] current_weight = 0 - for signature, group_components in sorted_groups: - is_isolated = signature.startswith("isolated_") + for (_platform, signature), group_components in sorted_groups: + is_isolated = signature.startswith(ISOLATED_SIGNATURE_PREFIX) weight_per_component = ISOLATED_WEIGHT if is_isolated else GROUPABLE_WEIGHT for component in group_components: @@ -169,7 +186,7 @@ def create_intelligent_batches( if current_batch: batches.append(current_batch) - return batches + return batches, signature_groups def main() -> int: @@ -231,7 +248,7 @@ def main() -> int: return 1 # Create intelligent batches - batches = create_intelligent_batches( + batches, signature_groups = create_intelligent_batches( components=components, tests_dir=args.tests_dir, batch_size=args.batch_size, @@ -256,6 +273,58 @@ def main() -> int: # Re-analyze to get isolated component counts for summary _, non_groupable, _ = analyze_all_components(args.tests_dir) + # Show grouping details + print("\n=== Component Grouping Details ===", file=sys.stderr) + # Sort groups by signature for readability + groupable_groups = [] + isolated_groups = [] + for (platform, signature), group_comps in sorted(signature_groups.items()): + if signature.startswith(ISOLATED_SIGNATURE_PREFIX): + isolated_groups.append((signature, group_comps)) + else: + groupable_groups.append((signature, group_comps)) + + if groupable_groups: + print( + f"\nGroupable signatures ({len(groupable_groups)} merged groups after cross-bus optimization):", + file=sys.stderr, + ) + for signature, group_comps in sorted( + groupable_groups, key=lambda x: (-len(x[1]), x[0]) + ): + # Check if this is a merged signature (contains +) + is_merged = "+" in signature and signature != NO_BUSES_SIGNATURE + # Special handling for no_buses components + if signature == NO_BUSES_SIGNATURE: + print( + f" [{signature}]: {len(group_comps)} components (used as fillers across batches)", + file=sys.stderr, + ) + else: + merge_indicator = " [MERGED]" if is_merged else "" + print( + f" [{signature}]{merge_indicator}: {len(group_comps)} components", + file=sys.stderr, + ) + # Show first few components as examples + examples = ", ".join(sorted(group_comps)[:8]) + if len(group_comps) > 8: + examples += f", ... (+{len(group_comps) - 8} more)" + print(f" → {examples}", file=sys.stderr) + + if isolated_groups: + print( + f"\nIsolated components ({len(isolated_groups)} components - tested individually):", + file=sys.stderr, + ) + isolated_names = sorted( + [comp for _, comps in isolated_groups for comp in comps] + ) + # Group isolated components for compact display + for i in range(0, len(isolated_names), 10): + chunk = isolated_names[i : i + 10] + print(f" {', '.join(chunk)}", file=sys.stderr) + # Count isolated vs groupable components all_batched_components = [comp for batch in batches for comp in batch] isolated_count = sum( diff --git a/script/test_build_components.py b/script/test_build_components.py index 14fc10977c..c98d425447 100755 --- a/script/test_build_components.py +++ b/script/test_build_components.py @@ -17,11 +17,13 @@ from __future__ import annotations import argparse from collections import defaultdict +from dataclasses import dataclass import hashlib import os from pathlib import Path import subprocess import sys +import time # Add esphome to path sys.path.insert(0, str(Path(__file__).parent.parent)) @@ -34,32 +36,49 @@ from script.analyze_component_buses import ( analyze_all_components, create_grouping_signature, is_platform_component, + merge_compatible_bus_groups, uses_local_file_references, ) from script.merge_component_configs import merge_component_configs -# Platform-specific maximum group sizes -# ESP8266 has limited IRAM and can't handle large component groups -PLATFORM_MAX_GROUP_SIZE = { - "esp8266-ard": 10, # ESP8266 Arduino has limited IRAM - "esp8266-idf": 10, # ESP8266 IDF also has limited IRAM - # BK72xx now uses BK7252 board (1.62MB flash vs 1.03MB) - no limit needed - # Other platforms can handle larger groups -} + +@dataclass +class TestResult: + """Store information about a single test run.""" + + test_id: str + components: list[str] + platform: str + success: bool + duration: float + command: str = "" + test_type: str = "compile" # "config" or "compile" def show_disk_space_if_ci(esphome_command: str) -> None: """Show disk space usage if running in CI during compile. + Only shows output during compilation (not config validation) since + disk space is only relevant when actually building firmware. + Args: esphome_command: The esphome command being run (config/compile/clean) """ - if os.environ.get("GITHUB_ACTIONS") and esphome_command == "compile": - print("\n" + "=" * 80) - print("Disk Space After Build:") - print("=" * 80) - subprocess.run(["df", "-h"], check=False) - print("=" * 80 + "\n") + # Only show disk space during compilation in CI + # Config validation doesn't build anything so disk space isn't relevant + if not os.environ.get("GITHUB_ACTIONS"): + return + if esphome_command != "compile": + return + + print("\n" + "=" * 80) + print("Disk Space After Build:") + print("=" * 80) + # Use sys.stdout.flush() to ensure output appears immediately + sys.stdout.flush() + subprocess.run(["df", "-h"], check=False, stdout=sys.stdout, stderr=sys.stderr) + print("=" * 80 + "\n") + sys.stdout.flush() def find_component_tests( @@ -128,6 +147,140 @@ def get_platform_base_files(base_dir: Path) -> dict[str, list[Path]]: return dict(platform_files) +def group_components_by_platform( + failed_results: list[TestResult], +) -> dict[tuple[str, str], list[str]]: + """Group failed components by platform and test type for simplified reproduction commands. + + Args: + failed_results: List of failed test results + + Returns: + Dictionary mapping (platform, test_type) to list of component names + """ + platform_components: dict[tuple[str, str], list[str]] = {} + for result in failed_results: + key = (result.platform, result.test_type) + if key not in platform_components: + platform_components[key] = [] + platform_components[key].extend(result.components) + + # Remove duplicates and sort for each platform + return { + key: sorted(set(components)) for key, components in platform_components.items() + } + + +def format_github_summary(test_results: list[TestResult]) -> str: + """Format test results as GitHub Actions job summary markdown. + + Args: + test_results: List of all test results + + Returns: + Markdown formatted summary string + """ + # Separate results into passed and failed + passed_results = [r for r in test_results if r.success] + failed_results = [r for r in test_results if not r.success] + + lines = [] + + # Header with emoji based on success/failure + if failed_results: + lines.append("## :x: Component Tests Failed\n") + else: + lines.append("## :white_check_mark: Component Tests Passed\n") + + # Summary statistics + total_time = sum(r.duration for r in test_results) + # Determine test type from results (all should be the same) + test_type = test_results[0].test_type if test_results else "unknown" + lines.append( + f"**Results:** {len(passed_results)} passed, {len(failed_results)} failed\n" + ) + lines.append(f"**Total time:** {total_time:.1f}s\n") + lines.append(f"**Test type:** `{test_type}`\n") + + # Show failed tests if any + if failed_results: + lines.append("### Failed Tests\n") + lines.append("| Test | Components | Platform | Duration |\n") + lines.append("|------|-----------|----------|----------|\n") + for result in failed_results: + components_str = ", ".join(result.components) + lines.append( + f"| `{result.test_id}` | {components_str} | {result.platform} | {result.duration:.1f}s |\n" + ) + lines.append("\n") + + # Show simplified commands to reproduce failures + # Group all failed components by platform for a single command per platform + lines.append("
\n") + lines.append("Commands to reproduce failures\n\n") + lines.append("```bash\n") + + # Generate one command per platform and test type + platform_components = group_components_by_platform(failed_results) + for platform, test_type in sorted(platform_components.keys()): + components_csv = ",".join(platform_components[(platform, test_type)]) + lines.append( + f"script/test_build_components.py -c {components_csv} -t {platform} -e {test_type}\n" + ) + + lines.append("```\n") + lines.append("
\n") + + # Show passed tests + if passed_results: + lines.append("### Passed Tests\n\n") + lines.append(f"{len(passed_results)} tests passed successfully\n") + + # Separate grouped and individual tests + grouped_results = [r for r in passed_results if len(r.components) > 1] + individual_results = [r for r in passed_results if len(r.components) == 1] + + if grouped_results: + lines.append("#### Grouped Tests\n") + lines.append("| Components | Platform | Count | Duration |\n") + lines.append("|-----------|----------|-------|----------|\n") + for result in grouped_results: + components_str = ", ".join(result.components) + lines.append( + f"| {components_str} | {result.platform} | {len(result.components)} | {result.duration:.1f}s |\n" + ) + lines.append("\n") + + if individual_results: + lines.append("#### Individual Tests\n") + # Show first 10 individual tests with timing + if len(individual_results) <= 10: + lines.extend( + f"- `{result.test_id}` - {result.duration:.1f}s\n" + for result in individual_results + ) + else: + lines.extend( + f"- `{result.test_id}` - {result.duration:.1f}s\n" + for result in individual_results[:10] + ) + lines.append(f"\n...and {len(individual_results) - 10} more\n") + lines.append("\n") + + return "".join(lines) + + +def write_github_summary(test_results: list[TestResult]) -> None: + """Write GitHub Actions job summary with test results and timing. + + Args: + test_results: List of all test results + """ + summary_content = format_github_summary(test_results) + with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as f: + f.write(summary_content) + + def extract_platform_with_version(base_file: Path) -> str: """Extract platform with version from base filename. @@ -151,7 +304,7 @@ def run_esphome_test( esphome_command: str, continue_on_fail: bool, use_testing_mode: bool = False, -) -> tuple[bool, str]: +) -> TestResult: """Run esphome test for a single component. Args: @@ -166,7 +319,7 @@ def run_esphome_test( use_testing_mode: Whether to use --testing-mode flag Returns: - Tuple of (success status, command string) + TestResult object with test details and timing """ test_name = test_file.stem.split(".")[0] @@ -221,9 +374,13 @@ def run_esphome_test( if use_testing_mode: print(" (using --testing-mode)") + start_time = time.time() + test_id = f"{component}.{test_name}.{platform_with_version}" + try: result = subprocess.run(cmd, check=False) success = result.returncode == 0 + duration = time.time() - start_time # Show disk space after build in CI during compile show_disk_space_if_ci(esphome_command) @@ -236,12 +393,30 @@ def run_esphome_test( print(cmd_str) print() raise subprocess.CalledProcessError(result.returncode, cmd) - return success, cmd_str + + return TestResult( + test_id=test_id, + components=[component], + platform=platform_with_version, + success=success, + duration=duration, + command=cmd_str, + test_type=esphome_command, + ) except subprocess.CalledProcessError: + duration = time.time() - start_time # Re-raise if we're not continuing on fail if not continue_on_fail: raise - return False, cmd_str + return TestResult( + test_id=test_id, + components=[component], + platform=platform_with_version, + success=False, + duration=duration, + command=cmd_str, + test_type=esphome_command, + ) def run_grouped_test( @@ -253,7 +428,7 @@ def run_grouped_test( tests_dir: Path, esphome_command: str, continue_on_fail: bool, -) -> tuple[bool, str]: +) -> TestResult: """Run esphome test for a group of components with shared bus configs. Args: @@ -267,7 +442,7 @@ def run_grouped_test( continue_on_fail: Whether to continue on failure Returns: - Tuple of (success status, command string) + TestResult object with test details and timing """ # Create merged config group_name = "_".join(components[:3]) # Use first 3 components for name @@ -294,8 +469,17 @@ def run_grouped_test( print(f"Error merging configs for {components}: {e}") if not continue_on_fail: raise - # Return empty command string since we failed before building the command - return False, f"# Failed during config merge: {e}" + # Return TestResult for merge failure + test_id = f"GROUPED[{','.join(components)}].{platform_with_version}" + return TestResult( + test_id=test_id, + components=components, + platform=platform_with_version, + success=False, + duration=0.0, + command=f"# Failed during config merge: {e}", + test_type=esphome_command, + ) # Create test file that includes merged config output_file = build_dir / f"test_{group_name}.{platform_with_version}.yaml" @@ -334,9 +518,13 @@ def run_grouped_test( print(f"> [GROUPED: {components_str}] [{platform_with_version}]") print(" (using --testing-mode)") + start_time = time.time() + test_id = f"GROUPED[{','.join(components)}].{platform_with_version}" + try: result = subprocess.run(cmd, check=False) success = result.returncode == 0 + duration = time.time() - start_time # Show disk space after build in CI during compile show_disk_space_if_ci(esphome_command) @@ -349,12 +537,30 @@ def run_grouped_test( print(cmd_str) print() raise subprocess.CalledProcessError(result.returncode, cmd) - return success, cmd_str + + return TestResult( + test_id=test_id, + components=components, + platform=platform_with_version, + success=success, + duration=duration, + command=cmd_str, + test_type=esphome_command, + ) except subprocess.CalledProcessError: + duration = time.time() - start_time # Re-raise if we're not continuing on fail if not continue_on_fail: raise - return False, cmd_str + return TestResult( + test_id=test_id, + components=components, + platform=platform_with_version, + success=False, + duration=duration, + command=cmd_str, + test_type=esphome_command, + ) def run_grouped_component_tests( @@ -366,7 +572,7 @@ def run_grouped_component_tests( esphome_command: str, continue_on_fail: bool, additional_isolated: set[str] | None = None, -) -> tuple[set[tuple[str, str]], list[str], list[str], dict[str, str]]: +) -> tuple[set[tuple[str, str]], list[TestResult]]: """Run grouped component tests. Args: @@ -380,12 +586,10 @@ def run_grouped_component_tests( additional_isolated: Additional components to treat as isolated (not grouped) Returns: - Tuple of (tested_components, passed_tests, failed_tests, failed_commands) + Tuple of (tested_components, test_results) """ tested_components = set() - passed_tests = [] - failed_tests = [] - failed_commands = {} # Map test_id to command string + test_results = [] # Group components by platform and bus signature grouped_components: dict[tuple[str, str], list[str]] = defaultdict(list) @@ -462,6 +666,11 @@ def run_grouped_component_tests( if signature: grouped_components[(platform, signature)].append(component) + # Merge groups with compatible buses (cross-bus grouping optimization) + # This allows mixing components with different buses (e.g., ble + uart) + # as long as they don't have conflicting configurations for the same bus type + grouped_components = merge_compatible_bus_groups(grouped_components) + # Print detailed grouping plan print("\nGrouping Plan:") print("-" * 80) @@ -560,28 +769,6 @@ def run_grouped_component_tests( # No other groups for this platform - keep no_buses components together grouped_components[(platform, NO_BUSES_SIGNATURE)] = no_buses_comps - # Split groups that exceed platform-specific maximum sizes - # ESP8266 has limited IRAM and can't handle large component groups - split_groups = {} - for (platform, signature), components in list(grouped_components.items()): - max_size = PLATFORM_MAX_GROUP_SIZE.get(platform) - if max_size and len(components) > max_size: - # Split this group into smaller groups - print( - f"\n ℹ️ Splitting {platform} group (signature: {signature}) " - f"from {len(components)} to max {max_size} components per group" - ) - # Remove original group - del grouped_components[(platform, signature)] - # Create split groups - for i in range(0, len(components), max_size): - split_components = components[i : i + max_size] - # Create unique signature for each split group - split_signature = f"{signature}_split{i // max_size + 1}" - split_groups[(platform, split_signature)] = split_components - # Add split groups back - grouped_components.update(split_groups) - groups_to_test = [] individual_tests = set() # Use set to avoid duplicates @@ -672,7 +859,7 @@ def run_grouped_component_tests( continue # Run grouped test - success, cmd_str = run_grouped_test( + test_result = run_grouped_test( components=components_to_group, platform=platform, platform_with_version=platform_with_version, @@ -687,17 +874,10 @@ def run_grouped_component_tests( for comp in components_to_group: tested_components.add((comp, platform_with_version)) - # Record result for each component - show all components in grouped tests - test_id = ( - f"GROUPED[{','.join(components_to_group)}].{platform_with_version}" - ) - if success: - passed_tests.append(test_id) - else: - failed_tests.append(test_id) - failed_commands[test_id] = cmd_str + # Store test result + test_results.append(test_result) - return tested_components, passed_tests, failed_tests, failed_commands + return tested_components, test_results def run_individual_component_test( @@ -710,9 +890,7 @@ def run_individual_component_test( esphome_command: str, continue_on_fail: bool, tested_components: set[tuple[str, str]], - passed_tests: list[str], - failed_tests: list[str], - failed_commands: dict[str, str], + test_results: list[TestResult], ) -> None: """Run an individual component test if not already tested in a group. @@ -726,16 +904,13 @@ def run_individual_component_test( esphome_command: ESPHome command continue_on_fail: Whether to continue on failure tested_components: Set of already tested components - passed_tests: List to append passed test IDs - failed_tests: List to append failed test IDs - failed_commands: Dict to store failed test commands + test_results: List to append test results """ # Skip if already tested in a group if (component, platform_with_version) in tested_components: return - test_name = test_file.stem.split(".")[0] - success, cmd_str = run_esphome_test( + test_result = run_esphome_test( component=component, test_file=test_file, platform=platform, @@ -745,12 +920,7 @@ def run_individual_component_test( esphome_command=esphome_command, continue_on_fail=continue_on_fail, ) - test_id = f"{component}.{test_name}.{platform_with_version}" - if success: - passed_tests.append(test_id) - else: - failed_tests.append(test_id) - failed_commands[test_id] = cmd_str + test_results.append(test_result) def test_components( @@ -799,19 +969,12 @@ def test_components( print(f"Found {len(all_tests)} components to test") # Run tests - failed_tests = [] - passed_tests = [] + test_results = [] tested_components = set() # Track which components were tested in groups - failed_commands = {} # Track commands for failed tests # First, run grouped tests if grouping is enabled if enable_grouping: - ( - tested_components, - passed_tests, - failed_tests, - failed_commands, - ) = run_grouped_component_tests( + tested_components, grouped_results = run_grouped_component_tests( all_tests=all_tests, platform_filter=platform_filter, platform_bases=platform_bases, @@ -821,6 +984,7 @@ def test_components( continue_on_fail=continue_on_fail, additional_isolated=isolated_components, ) + test_results.extend(grouped_results) # Then run individual tests for components not in groups for component, test_files in sorted(all_tests.items()): @@ -846,9 +1010,7 @@ def test_components( esphome_command=esphome_command, continue_on_fail=continue_on_fail, tested_components=tested_components, - passed_tests=passed_tests, - failed_tests=failed_tests, - failed_commands=failed_commands, + test_results=test_results, ) else: # Platform-specific test @@ -880,31 +1042,40 @@ def test_components( esphome_command=esphome_command, continue_on_fail=continue_on_fail, tested_components=tested_components, - passed_tests=passed_tests, - failed_tests=failed_tests, - failed_commands=failed_commands, + test_results=test_results, ) + # Separate results into passed and failed + passed_results = [r for r in test_results if r.success] + failed_results = [r for r in test_results if not r.success] + # Print summary print("\n" + "=" * 80) - print(f"Test Summary: {len(passed_tests)} passed, {len(failed_tests)} failed") + print(f"Test Summary: {len(passed_results)} passed, {len(failed_results)} failed") print("=" * 80) - if failed_tests: + if failed_results: print("\nFailed tests:") - for test in failed_tests: - print(f" - {test}") + for result in failed_results: + print(f" - {result.test_id}") - # Print failed commands at the end for easy copy-paste from CI logs + # Print simplified commands grouped by platform and test type for easy copy-paste print("\n" + "=" * 80) - print("Failed test commands (copy-paste to reproduce locally):") + print("Commands to reproduce failures (copy-paste to reproduce locally):") print("=" * 80) - for test in failed_tests: - if test in failed_commands: - print(f"\n# {test}") - print(failed_commands[test]) + platform_components = group_components_by_platform(failed_results) + for platform, test_type in sorted(platform_components.keys()): + components_csv = ",".join(platform_components[(platform, test_type)]) + print( + f"script/test_build_components.py -c {components_csv} -t {platform} -e {test_type}" + ) print() + # Write GitHub Actions job summary if in CI + if os.environ.get("GITHUB_STEP_SUMMARY"): + write_github_summary(test_results) + + if failed_results: return 1 return 0 diff --git a/tests/components/ade7880/test.esp8266-ard.yaml b/tests/components/ade7880/test.esp8266-ard.yaml index 81a04d0724..8b5e47f0b5 100644 --- a/tests/components/ade7880/test.esp8266-ard.yaml +++ b/tests/components/ade7880/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - irq0_pin: GPIO13 + irq0_pin: GPIO0 irq1_pin: GPIO15 reset_pin: GPIO16 diff --git a/tests/components/ade7953_i2c/common.yaml b/tests/components/ade7953_i2c/common.yaml index 8b2a9588fe..5253759888 100644 --- a/tests/components/ade7953_i2c/common.yaml +++ b/tests/components/ade7953_i2c/common.yaml @@ -4,10 +4,13 @@ sensor: irq_pin: ${irq_pin} voltage: name: ADE7953 Voltage + id: ade7953_i2c_voltage current_a: name: ADE7953 Current A + id: ade7953_i2c_current_a current_b: name: ADE7953 Current B + id: ade7953_i2c_current_b power_factor_a: name: ADE7953 Power Factor A power_factor_b: diff --git a/tests/components/ade7953_spi/common.yaml b/tests/components/ade7953_spi/common.yaml index 30b5258a2a..f66ab697a1 100644 --- a/tests/components/ade7953_spi/common.yaml +++ b/tests/components/ade7953_spi/common.yaml @@ -4,13 +4,13 @@ sensor: irq_pin: ${irq_pin} voltage: name: ADE7953 Voltage - id: ade7953_voltage + id: ade7953_spi_voltage current_a: name: ADE7953 Current A - id: ade7953_current_a + id: ade7953_spi_current_a current_b: name: ADE7953 Current B - id: ade7953_current_b + id: ade7953_spi_current_b power_factor_a: name: ADE7953 Power Factor A power_factor_b: diff --git a/tests/components/as3935_i2c/common.yaml b/tests/components/as3935_i2c/common.yaml index a758bb7f56..d659486e83 100644 --- a/tests/components/as3935_i2c/common.yaml +++ b/tests/components/as3935_i2c/common.yaml @@ -1,13 +1,16 @@ as3935_i2c: + id: as3935_i2c_id i2c_id: i2c_bus irq_pin: ${irq_pin} binary_sensor: - platform: as3935 + as3935_id: as3935_i2c_id name: Storm Alert sensor: - platform: as3935 + as3935_id: as3935_i2c_id lightning_energy: name: Lightning Energy distance: diff --git a/tests/components/as3935_spi/common.yaml b/tests/components/as3935_spi/common.yaml index 5898d5d365..d2942dc01d 100644 --- a/tests/components/as3935_spi/common.yaml +++ b/tests/components/as3935_spi/common.yaml @@ -1,13 +1,16 @@ as3935_spi: + id: as3935_spi_id cs_pin: ${cs_pin} irq_pin: ${irq_pin} binary_sensor: - platform: as3935 + as3935_id: as3935_spi_id name: Storm Alert sensor: - platform: as3935 + as3935_id: as3935_spi_id lightning_energy: name: Lightning Energy distance: diff --git a/tests/components/axs15231/common.yaml b/tests/components/axs15231/common.yaml index 3f07af80ea..d4fd3becbb 100644 --- a/tests/components/axs15231/common.yaml +++ b/tests/components/axs15231/common.yaml @@ -1,7 +1,7 @@ display: - platform: ssd1306_i2c i2c_id: i2c_bus - id: ssd1306_display + id: ssd1306_i2c_display model: SSD1306_128X64 reset_pin: 19 pages: @@ -13,6 +13,6 @@ touchscreen: - platform: axs15231 i2c_id: i2c_bus id: axs15231_touchscreen - display: ssd1306_display + display: ssd1306_i2c_display interrupt_pin: 20 reset_pin: 18 diff --git a/tests/components/bme280_i2c/common.yaml b/tests/components/bme280_i2c/common.yaml index e6d41d209c..a31a6f9a6c 100644 --- a/tests/components/bme280_i2c/common.yaml +++ b/tests/components/bme280_i2c/common.yaml @@ -3,12 +3,12 @@ sensor: i2c_id: i2c_bus address: 0x76 temperature: - id: bme280_temperature + id: bme280_i2c_temperature name: BME280 Temperature humidity: - id: bme280_humidity + id: bme280_i2c_humidity name: BME280 Humidity pressure: - id: bme280_pressure + id: bme280_i2c_pressure name: BME280 Pressure update_interval: 15s diff --git a/tests/components/bme280_spi/common.yaml b/tests/components/bme280_spi/common.yaml index 9a50b410fb..d97b475f0e 100644 --- a/tests/components/bme280_spi/common.yaml +++ b/tests/components/bme280_spi/common.yaml @@ -2,12 +2,12 @@ sensor: - platform: bme280_spi cs_pin: ${cs_pin} temperature: - id: bme280_temperature + id: bme280_spi_temperature name: BME280 Temperature humidity: - id: bme280_humidity + id: bme280_spi_humidity name: BME280 Humidity pressure: - id: bme280_pressure + id: bme280_spi_pressure name: BME280 Pressure update_interval: 15s diff --git a/tests/components/bmp280_i2c/common.yaml b/tests/components/bmp280_i2c/common.yaml index 785343de7d..77a9db7fc5 100644 --- a/tests/components/bmp280_i2c/common.yaml +++ b/tests/components/bmp280_i2c/common.yaml @@ -3,10 +3,10 @@ sensor: i2c_id: i2c_bus address: 0x77 temperature: - id: bmp280_temperature + id: bmp280_i2c_temperature name: Outside Temperature pressure: name: Outside Pressure - id: bmp280_pressure + id: bmp280_i2c_pressure iir_filter: 16x update_interval: 15s diff --git a/tests/components/bmp280_spi/common.yaml b/tests/components/bmp280_spi/common.yaml index fa88967ca4..1be54f6b74 100644 --- a/tests/components/bmp280_spi/common.yaml +++ b/tests/components/bmp280_spi/common.yaml @@ -2,10 +2,10 @@ sensor: - platform: bmp280_spi cs_pin: ${cs_pin} temperature: - id: bmp280_temperature + id: bmp280_spi_temperature name: Outside Temperature pressure: name: Outside Pressure - id: bmp280_pressure + id: bmp280_spi_pressure iir_filter: 16x update_interval: 15s diff --git a/tests/components/bmp3xx_i2c/common.yaml b/tests/components/bmp3xx_i2c/common.yaml index ebc4921b84..e651072f25 100644 --- a/tests/components/bmp3xx_i2c/common.yaml +++ b/tests/components/bmp3xx_i2c/common.yaml @@ -3,8 +3,10 @@ sensor: i2c_id: i2c_bus address: 0x77 temperature: + id: bmp3xx_i2c_temperature name: BMP Temperature oversampling: 16x pressure: + id: bmp3xx_i2c_pressure name: BMP Pressure iir_filter: 2X diff --git a/tests/components/bmp3xx_spi/common.yaml b/tests/components/bmp3xx_spi/common.yaml index d6acef1833..b59d46c967 100644 --- a/tests/components/bmp3xx_spi/common.yaml +++ b/tests/components/bmp3xx_spi/common.yaml @@ -2,8 +2,10 @@ sensor: - platform: bmp3xx_spi cs_pin: ${cs_pin} temperature: + id: bmp3xx_spi_temperature name: BMP Temperature oversampling: 16x pressure: + id: bmp3xx_spi_pressure name: BMP Pressure iir_filter: 2X diff --git a/tests/components/camera/test.esp32-idf.yaml b/tests/components/camera/test.esp32-idf.yaml index 3d93dd6418..ef8f74d4eb 100644 --- a/tests/components/camera/test.esp32-idf.yaml +++ b/tests/components/camera/test.esp32-idf.yaml @@ -1,4 +1,4 @@ packages: - camera: !include ../../test_build_components/common/camera/esp32-idf.yaml + i2c_camera: !include ../../test_build_components/common/i2c_camera/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/camera_encoder/test.esp32-idf.yaml b/tests/components/camera_encoder/test.esp32-idf.yaml index 3d93dd6418..ef8f74d4eb 100644 --- a/tests/components/camera_encoder/test.esp32-idf.yaml +++ b/tests/components/camera_encoder/test.esp32-idf.yaml @@ -1,4 +1,4 @@ packages: - camera: !include ../../test_build_components/common/camera/esp32-idf.yaml + i2c_camera: !include ../../test_build_components/common/i2c_camera/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/chsc6x/test.esp32-idf.yaml b/tests/components/chsc6x/test.esp32-idf.yaml index ea3686d8bd..fa7c72150e 100644 --- a/tests/components/chsc6x/test.esp32-idf.yaml +++ b/tests/components/chsc6x/test.esp32-idf.yaml @@ -4,6 +4,7 @@ packages: display: - platform: ili9xxx + spi_id: spi_bus id: ili9xxx_display model: GC9A01A invert_colors: True @@ -16,5 +17,6 @@ display: touchscreen: - platform: chsc6x + i2c_id: i2c_bus display: ili9xxx_display interrupt_pin: 20 diff --git a/tests/components/ektf2232/common.yaml b/tests/components/ektf2232/common.yaml index 8ab57be46f..1c4d768b08 100644 --- a/tests/components/ektf2232/common.yaml +++ b/tests/components/ektf2232/common.yaml @@ -1,7 +1,7 @@ display: - platform: ssd1306_i2c i2c_id: i2c_bus - id: ssd1306_display + id: ssd1306_i2c_display model: SSD1306_128X64 reset_pin: ${display_reset_pin} pages: @@ -15,7 +15,7 @@ touchscreen: id: ektf2232_touchscreen interrupt_pin: ${interrupt_pin} reset_pin: ${touch_reset_pin} - display: ssd1306_display + display: ssd1306_i2c_display on_touch: - logger.log: format: Touch at (%d, %d) diff --git a/tests/components/ens160_i2c/common.yaml b/tests/components/ens160_i2c/common.yaml index 685c8d3fee..1da2bacec2 100644 --- a/tests/components/ens160_i2c/common.yaml +++ b/tests/components/ens160_i2c/common.yaml @@ -3,8 +3,11 @@ sensor: i2c_id: i2c_bus address: 0x53 eco2: + id: ens160_i2c_eco2 name: "ENS160 eCO2" tvoc: + id: ens160_i2c_tvoc name: "ENS160 Total Volatile Organic Compounds" aqi: + id: ens160_i2c_aqi name: "ENS160 Air Quality Index" diff --git a/tests/components/ens160_spi/common.yaml b/tests/components/ens160_spi/common.yaml index 53000a5e96..46a3f40b40 100644 --- a/tests/components/ens160_spi/common.yaml +++ b/tests/components/ens160_spi/common.yaml @@ -2,8 +2,11 @@ sensor: - platform: ens160_spi cs_pin: ${cs_pin} eco2: + id: ens160_spi_eco2 name: "ENS160 eCO2" tvoc: + id: ens160_spi_tvoc name: "ENS160 Total Volatile Organic Compounds" aqi: + id: ens160_spi_aqi name: "ENS160 Air Quality Index" diff --git a/tests/components/esp32_camera/test.esp32-idf.yaml b/tests/components/esp32_camera/test.esp32-idf.yaml index 3d93dd6418..ef8f74d4eb 100644 --- a/tests/components/esp32_camera/test.esp32-idf.yaml +++ b/tests/components/esp32_camera/test.esp32-idf.yaml @@ -1,4 +1,4 @@ packages: - camera: !include ../../test_build_components/common/camera/esp32-idf.yaml + i2c_camera: !include ../../test_build_components/common/i2c_camera/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/esp32_camera_web_server/test.esp32-idf.yaml b/tests/components/esp32_camera_web_server/test.esp32-idf.yaml index 3d93dd6418..ef8f74d4eb 100644 --- a/tests/components/esp32_camera_web_server/test.esp32-idf.yaml +++ b/tests/components/esp32_camera_web_server/test.esp32-idf.yaml @@ -1,4 +1,4 @@ packages: - camera: !include ../../test_build_components/common/camera/esp32-idf.yaml + i2c_camera: !include ../../test_build_components/common/i2c_camera/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/font/common.yaml b/tests/components/font/common.yaml index 2d2e970536..6ba52e3d97 100644 --- a/tests/components/font/common.yaml +++ b/tests/components/font/common.yaml @@ -49,6 +49,7 @@ font: display: - platform: ssd1306_i2c + i2c_id: i2c_bus id: ssd1306_display model: SSD1306_128X64 reset_pin: ${display_reset_pin} diff --git a/tests/components/ft63x6/test.esp8266-ard.yaml b/tests/components/ft63x6/test.esp8266-ard.yaml index d6b6903029..3ac5c645e3 100644 --- a/tests/components/ft63x6/test.esp8266-ard.yaml +++ b/tests/components/ft63x6/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - interrupt_pin: GPIO12 + interrupt_pin: GPIO0 reset_pin: GPIO16 packages: diff --git a/tests/components/graph/common.yaml b/tests/components/graph/common.yaml index 578de5a60c..11e2a16ca1 100644 --- a/tests/components/graph/common.yaml +++ b/tests/components/graph/common.yaml @@ -11,6 +11,7 @@ graph: display: - platform: ssd1306_i2c + i2c_id: i2c_bus id: ssd1306_display model: SSD1306_128X64 reset_pin: ${reset_pin} diff --git a/tests/components/graphical_display_menu/common.yaml b/tests/components/graphical_display_menu/common.yaml index 0b8f20d64b..6cee2af232 100644 --- a/tests/components/graphical_display_menu/common.yaml +++ b/tests/components/graphical_display_menu/common.yaml @@ -1,6 +1,6 @@ display: - platform: ssd1306_i2c - id: ssd1306_display + id: ssd1306_i2c_display model: SSD1306_128X64 reset_pin: ${reset_pin} pages: @@ -36,7 +36,7 @@ switch: graphical_display_menu: id: test_graphical_display_menu - display: ssd1306_display + display: ssd1306_i2c_display font: roboto active: false mode: rotary diff --git a/tests/components/gt911/common.yaml b/tests/components/gt911/common.yaml index 5f9748afb6..ff464cda24 100644 --- a/tests/components/gt911/common.yaml +++ b/tests/components/gt911/common.yaml @@ -1,7 +1,7 @@ display: - platform: ssd1306_i2c i2c_id: i2c_bus - id: ssd1306_display + id: ssd1306_i2c_display model: SSD1306_128X64 reset_pin: ${display_reset_pin} pages: @@ -13,7 +13,7 @@ touchscreen: - platform: gt911 i2c_id: i2c_bus id: gt911_touchscreen - display: ssd1306_display + display: ssd1306_i2c_display interrupt_pin: ${interrupt_pin} reset_pin: ${reset_pin} diff --git a/tests/components/hx711/test.esp8266-ard.yaml b/tests/components/hx711/test.esp8266-ard.yaml index defef165e3..e7c017ed99 100644 --- a/tests/components/hx711/test.esp8266-ard.yaml +++ b/tests/components/hx711/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - clk_pin: GPIO4 - dout_pin: GPIO5 + clk_pin: GPIO0 + dout_pin: GPIO2 <<: !include common.yaml diff --git a/tests/components/ina2xx_i2c/common.yaml b/tests/components/ina2xx_i2c/common.yaml index 7d586f136e..2416ad6daf 100644 --- a/tests/components/ina2xx_i2c/common.yaml +++ b/tests/components/ina2xx_i2c/common.yaml @@ -7,9 +7,21 @@ sensor: max_current: 40 A adc_range: 1 temperature_coefficient: 50 - shunt_voltage: "INA2xx Shunt Voltage" - bus_voltage: "INA2xx Bus Voltage" - current: "INA2xx Current" - power: "INA2xx Power" - energy: "INA2xx Energy" - charge: "INA2xx Charge" + shunt_voltage: + id: ina2xx_i2c_shunt_voltage + name: "INA2xx Shunt Voltage" + bus_voltage: + id: ina2xx_i2c_bus_voltage + name: "INA2xx Bus Voltage" + current: + id: ina2xx_i2c_current + name: "INA2xx Current" + power: + id: ina2xx_i2c_power + name: "INA2xx Power" + energy: + id: ina2xx_i2c_energy + name: "INA2xx Energy" + charge: + id: ina2xx_i2c_charge + name: "INA2xx Charge" diff --git a/tests/components/ina2xx_spi/common.yaml b/tests/components/ina2xx_spi/common.yaml index d9b2300e26..8de77eba26 100644 --- a/tests/components/ina2xx_spi/common.yaml +++ b/tests/components/ina2xx_spi/common.yaml @@ -6,9 +6,21 @@ sensor: max_current: 40 A adc_range: 1 temperature_coefficient: 50 - shunt_voltage: "INA2xx Shunt Voltage" - bus_voltage: "INA2xx Bus Voltage" - current: "INA2xx Current" - power: "INA2xx Power" - energy: "INA2xx Energy" - charge: "INA2xx Charge" + shunt_voltage: + id: ina2xx_spi_shunt_voltage + name: "INA2xx Shunt Voltage" + bus_voltage: + id: ina2xx_spi_bus_voltage + name: "INA2xx Bus Voltage" + current: + id: ina2xx_spi_current + name: "INA2xx Current" + power: + id: ina2xx_spi_power + name: "INA2xx Power" + energy: + id: ina2xx_spi_energy + name: "INA2xx Energy" + charge: + id: ina2xx_spi_charge + name: "INA2xx Charge" diff --git a/tests/components/lilygo_t5_47/common.yaml b/tests/components/lilygo_t5_47/common.yaml index 7079139ec7..18f1ba10ae 100644 --- a/tests/components/lilygo_t5_47/common.yaml +++ b/tests/components/lilygo_t5_47/common.yaml @@ -1,7 +1,7 @@ display: - platform: ssd1306_i2c i2c_id: i2c_bus - id: ssd1306_display + id: ssd1306_i2c_display model: SSD1306_128X64 reset_pin: ${reset_pin} pages: @@ -14,7 +14,7 @@ touchscreen: i2c_id: i2c_bus id: lilygo_touchscreen interrupt_pin: ${interrupt_pin} - display: ssd1306_display + display: ssd1306_i2c_display on_touch: - logger.log: format: Touch at (%d, %d) diff --git a/tests/components/pn532_i2c/common.yaml b/tests/components/pn532_i2c/common.yaml index f328cd40ee..947e1151aa 100644 --- a/tests/components/pn532_i2c/common.yaml +++ b/tests/components/pn532_i2c/common.yaml @@ -1,9 +1,9 @@ pn532_i2c: i2c_id: i2c_bus - id: pn532_nfcc + id: pn532_nfcc_i2c binary_sensor: - platform: pn532 - pn532_id: pn532_nfcc + pn532_id: pn532_nfcc_i2c name: PN532 NFC Tag uid: 74-10-37-94 diff --git a/tests/components/pn532_spi/common.yaml b/tests/components/pn532_spi/common.yaml index e749a9896a..f9149af35f 100644 --- a/tests/components/pn532_spi/common.yaml +++ b/tests/components/pn532_spi/common.yaml @@ -1,9 +1,9 @@ pn532_spi: - id: pn532_nfcc + id: pn532_nfcc_spi cs_pin: ${cs_pin} binary_sensor: - platform: pn532 - pn532_id: pn532_nfcc + pn532_id: pn532_nfcc_spi name: PN532 NFC Tag uid: 74-10-37-94 diff --git a/tests/components/pn7160_i2c/common.yaml b/tests/components/pn7160_i2c/common.yaml index 9807bff0f0..fa9a876d1c 100644 --- a/tests/components/pn7160_i2c/common.yaml +++ b/tests/components/pn7160_i2c/common.yaml @@ -1,23 +1,23 @@ esphome: on_boot: then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 + - tag.set_clean_mode: nfcc_pn7160_i2c + - tag.set_format_mode: nfcc_pn7160_i2c + - tag.set_read_mode: nfcc_pn7160_i2c - tag.set_write_message: message: https://www.home-assistant.io/tag/pulse include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 + - tag.set_write_mode: nfcc_pn7160_i2c - tag.set_emulation_message: message: https://www.home-assistant.io/tag/pulse include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 + - tag.emulation_off: nfcc_pn7160_i2c + - tag.emulation_on: nfcc_pn7160_i2c + - tag.polling_off: nfcc_pn7160_i2c + - tag.polling_on: nfcc_pn7160_i2c pn7150_i2c: - id: nfcc_pn7160 + id: nfcc_pn7160_i2c i2c_id: i2c_bus irq_pin: ${irq_pin} ven_pin: ${ven_pin} diff --git a/tests/components/pn7160_spi/common.yaml b/tests/components/pn7160_spi/common.yaml index d467eb093f..53b37b38f4 100644 --- a/tests/components/pn7160_spi/common.yaml +++ b/tests/components/pn7160_spi/common.yaml @@ -1,23 +1,23 @@ esphome: on_boot: then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 + - tag.set_clean_mode: nfcc_pn7160_spi + - tag.set_format_mode: nfcc_pn7160_spi + - tag.set_read_mode: nfcc_pn7160_spi - tag.set_write_message: message: https://www.home-assistant.io/tag/pulse include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 + - tag.set_write_mode: nfcc_pn7160_spi - tag.set_emulation_message: message: https://www.home-assistant.io/tag/pulse include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 + - tag.emulation_off: nfcc_pn7160_spi + - tag.emulation_on: nfcc_pn7160_spi + - tag.polling_off: nfcc_pn7160_spi + - tag.polling_on: nfcc_pn7160_spi pn7160_spi: - id: nfcc_pn7160 + id: nfcc_pn7160_spi cs_pin: ${cs_pin} irq_pin: ${irq_pin} ven_pin: ${ven_pin} diff --git a/tests/components/rc522_i2c/common.yaml b/tests/components/rc522_i2c/common.yaml index 65b92a3e78..0e624351d4 100644 --- a/tests/components/rc522_i2c/common.yaml +++ b/tests/components/rc522_i2c/common.yaml @@ -1,5 +1,5 @@ rc522_i2c: - - id: rc522_nfcc + - id: rc522_nfcc_i2c i2c_id: i2c_bus update_interval: 1s on_tag: @@ -8,6 +8,6 @@ rc522_i2c: binary_sensor: - platform: rc522 - rc522_id: rc522_nfcc + rc522_id: rc522_nfcc_i2c name: RC522 NFC Tag uid: 74-10-37-94 diff --git a/tests/components/rc522_spi/common.yaml b/tests/components/rc522_spi/common.yaml index 4ce1d6584b..c4830850de 100644 --- a/tests/components/rc522_spi/common.yaml +++ b/tests/components/rc522_spi/common.yaml @@ -1,9 +1,9 @@ rc522_spi: - id: rc522_nfcc + id: rc522_nfcc_spi cs_pin: ${cs_pin} binary_sensor: - platform: rc522 - rc522_id: rc522_nfcc - name: PN532 NFC Tag + rc522_id: rc522_nfcc_spi + name: RC522 NFC Tag uid: 74-10-37-94 diff --git a/tests/components/selec_meter/test.esp8266-ard.yaml b/tests/components/selec_meter/test.esp8266-ard.yaml index 48a7307795..6daa08c22b 100644 --- a/tests/components/selec_meter/test.esp8266-ard.yaml +++ b/tests/components/selec_meter/test.esp8266-ard.yaml @@ -1,7 +1,7 @@ substitutions: tx_pin: GPIO0 rx_pin: GPIO2 - flow_control_pin: GPIO4 + flow_control_pin: GPIO15 packages: modbus: !include ../../test_build_components/common/modbus/esp8266-ard.yaml diff --git a/tests/components/sn74hc595/test.esp8266-ard.yaml b/tests/components/sn74hc595/test.esp8266-ard.yaml index cc011e01d4..e0de8bb0a3 100644 --- a/tests/components/sn74hc595/test.esp8266-ard.yaml +++ b/tests/components/sn74hc595/test.esp8266-ard.yaml @@ -2,8 +2,8 @@ packages: spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 + clock_pin: GPIO15 + data_pin: GPIO16 latch_pin1: GPIO2 oe_pin1: GPIO0 latch_pin2: GPIO3 diff --git a/tests/components/ssd1306_i2c/common.yaml b/tests/components/ssd1306_i2c/common.yaml index e876fcb36a..09eb569a8e 100644 --- a/tests/components/ssd1306_i2c/common.yaml +++ b/tests/components/ssd1306_i2c/common.yaml @@ -4,7 +4,7 @@ display: model: SSD1306_128X64 reset_pin: ${reset_pin} address: 0x3C - id: display1 + id: ssd1306_i2c_display contrast: 60% pages: - id: ssd1306_i2c_page1 diff --git a/tests/components/ssd1306_spi/common.yaml b/tests/components/ssd1306_spi/common.yaml index 2a2adb4146..0297abc192 100644 --- a/tests/components/ssd1306_spi/common.yaml +++ b/tests/components/ssd1306_spi/common.yaml @@ -1,5 +1,6 @@ display: - platform: ssd1306_spi + id: ssd1306_spi_display model: SSD1306 128x64 cs_pin: ${cs_pin} dc_pin: ${dc_pin} diff --git a/tests/components/ssd1327_i2c/common.yaml b/tests/components/ssd1327_i2c/common.yaml index c90e9678dd..c5f2123d9a 100644 --- a/tests/components/ssd1327_i2c/common.yaml +++ b/tests/components/ssd1327_i2c/common.yaml @@ -4,7 +4,7 @@ display: model: SSD1327_128x128 reset_pin: ${reset_pin} address: 0x3C - id: display1 + id: ssd1327_i2c_display pages: - id: ssd1327_i2c_page1 lambda: |- diff --git a/tests/components/ssd1327_spi/common.yaml b/tests/components/ssd1327_spi/common.yaml index 1aa4fb5a1c..b46e61e080 100644 --- a/tests/components/ssd1327_spi/common.yaml +++ b/tests/components/ssd1327_spi/common.yaml @@ -1,5 +1,6 @@ display: - platform: ssd1327_spi + id: ssd1327_spi_display model: SSD1327 128x128 cs_pin: ${cs_pin} dc_pin: ${dc_pin} diff --git a/tests/components/st7567_i2c/common.yaml b/tests/components/st7567_i2c/common.yaml index 9a4cd79faa..c81d6825e3 100644 --- a/tests/components/st7567_i2c/common.yaml +++ b/tests/components/st7567_i2c/common.yaml @@ -3,7 +3,7 @@ display: i2c_id: i2c_bus reset_pin: ${reset_pin} address: 0x3C - id: display1 + id: st7567_i2c_display pages: - id: st7567_i2c_page1 lambda: |- diff --git a/tests/components/st7567_spi/common.yaml b/tests/components/st7567_spi/common.yaml index b5a4074e13..25a8932ee1 100644 --- a/tests/components/st7567_spi/common.yaml +++ b/tests/components/st7567_spi/common.yaml @@ -1,5 +1,6 @@ display: - platform: st7567_spi + id: st7567_spi_display cs_pin: ${cs_pin} dc_pin: ${dc_pin} reset_pin: ${reset_pin} diff --git a/tests/components/syslog/common.yaml b/tests/components/syslog/common.yaml index cd6e63c9ec..daa913f009 100644 --- a/tests/components/syslog/common.yaml +++ b/tests/components/syslog/common.yaml @@ -6,7 +6,8 @@ udp: addresses: ["239.0.60.53"] time: - platform: host + - platform: host + id: host_time syslog: port: 514 diff --git a/tests/components/tt21100/common.yaml b/tests/components/tt21100/common.yaml index bd1830ea8b..56089aed1e 100644 --- a/tests/components/tt21100/common.yaml +++ b/tests/components/tt21100/common.yaml @@ -1,7 +1,7 @@ display: - platform: ssd1306_i2c i2c_id: i2c_bus - id: ssd1306_display + id: ssd1306_i2c_display model: SSD1306_128X64 reset_pin: ${disp_reset_pin} pages: @@ -13,7 +13,7 @@ touchscreen: - platform: tt21100 i2c_id: i2c_bus id: tt21100_touchscreen - display: ssd1306_display + display: ssd1306_i2c_display interrupt_pin: ${interrupt_pin} reset_pin: ${reset_pin} diff --git a/tests/components/wk2132_i2c/test.esp32-idf.yaml b/tests/components/wk2132_i2c/test.esp32-idf.yaml index b47e39c389..6b748a8f20 100644 --- a/tests/components/wk2132_i2c/test.esp32-idf.yaml +++ b/tests/components/wk2132_i2c/test.esp32-idf.yaml @@ -1,4 +1,5 @@ packages: i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + uart_bridge_2: !include ../../test_build_components/common/uart_bridge_2/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml index e9d826aa7c..d7b149a6fd 100644 --- a/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml +++ b/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml @@ -4,5 +4,6 @@ substitutions: packages: i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml + uart_bridge_2: !include ../../test_build_components/common/uart_bridge_2/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2132_spi/common.yaml b/tests/components/wk2132_spi/common.yaml index a762c10c92..18294974b9 100644 --- a/tests/components/wk2132_spi/common.yaml +++ b/tests/components/wk2132_spi/common.yaml @@ -1,20 +1,20 @@ wk2132_spi: - - id: wk2132_spi_id + - id: wk2132_spi_bridge cs_pin: ${cs_pin} crystal: 11059200 data_rate: 1MHz uart: - - id: wk2132_spi_id0 + - id: wk2132_spi_uart0 channel: 0 baud_rate: 115200 stop_bits: 1 parity: none - - id: wk2132_spi_id1 + - id: wk2132_spi_uart1 channel: 1 baud_rate: 9600 # Ensures a sensor doesn't break validation sensor: - platform: a02yyuw - uart_id: wk2132_spi_id1 + uart_id: wk2132_spi_uart1 id: distance_sensor diff --git a/tests/components/wk2132_spi/test.esp32-idf.yaml b/tests/components/wk2132_spi/test.esp32-idf.yaml index a3352cf880..9202a691ba 100644 --- a/tests/components/wk2132_spi/test.esp32-idf.yaml +++ b/tests/components/wk2132_spi/test.esp32-idf.yaml @@ -3,5 +3,6 @@ substitutions: packages: spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + uart_bridge_2: !include ../../test_build_components/common/uart_bridge_2/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2132_spi/test.esp32-s3-idf.yaml b/tests/components/wk2132_spi/test.esp32-s3-idf.yaml index 6a60c90fb2..9c7d36996e 100644 --- a/tests/components/wk2132_spi/test.esp32-s3-idf.yaml +++ b/tests/components/wk2132_spi/test.esp32-s3-idf.yaml @@ -6,5 +6,6 @@ substitutions: packages: spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + uart_bridge_2: !include ../../test_build_components/common/uart_bridge_2/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2168_i2c/test.esp32-idf.yaml b/tests/components/wk2168_i2c/test.esp32-idf.yaml index b47e39c389..9d9f0d4931 100644 --- a/tests/components/wk2168_i2c/test.esp32-idf.yaml +++ b/tests/components/wk2168_i2c/test.esp32-idf.yaml @@ -1,4 +1,5 @@ packages: i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml index e9d826aa7c..115812be97 100644 --- a/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml +++ b/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml @@ -4,5 +4,6 @@ substitutions: packages: i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2168_spi/test.esp32-idf.yaml b/tests/components/wk2168_spi/test.esp32-idf.yaml index a3352cf880..2b56a46b70 100644 --- a/tests/components/wk2168_spi/test.esp32-idf.yaml +++ b/tests/components/wk2168_spi/test.esp32-idf.yaml @@ -3,5 +3,6 @@ substitutions: packages: spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2168_spi/test.esp32-s3-idf.yaml b/tests/components/wk2168_spi/test.esp32-s3-idf.yaml index 6a60c90fb2..374fe64d16 100644 --- a/tests/components/wk2168_spi/test.esp32-s3-idf.yaml +++ b/tests/components/wk2168_spi/test.esp32-s3-idf.yaml @@ -6,5 +6,6 @@ substitutions: packages: spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2204_i2c/test.esp32-idf.yaml b/tests/components/wk2204_i2c/test.esp32-idf.yaml index b47e39c389..9d9f0d4931 100644 --- a/tests/components/wk2204_i2c/test.esp32-idf.yaml +++ b/tests/components/wk2204_i2c/test.esp32-idf.yaml @@ -1,4 +1,5 @@ packages: i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml index e9d826aa7c..115812be97 100644 --- a/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml +++ b/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml @@ -4,5 +4,6 @@ substitutions: packages: i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2204_spi/common.yaml b/tests/components/wk2204_spi/common.yaml index 939c54cc40..0b62a7a009 100644 --- a/tests/components/wk2204_spi/common.yaml +++ b/tests/components/wk2204_spi/common.yaml @@ -1,28 +1,28 @@ wk2204_spi: - - id: wk2204_spi_id + - id: wk2204_spi_bridge cs_pin: ${cs_pin} crystal: 11059200 data_rate: 1MHz uart: - - id: wk2204_spi_id0 + - id: wk2204_spi_uart0 channel: 0 baud_rate: 115200 stop_bits: 1 parity: none - - id: wk2204_spi_id1 + - id: wk2204_spi_uart1 channel: 1 baud_rate: 921600 - - id: wk2204_spi_id2 + - id: wk2204_spi_uart2 channel: 2 baud_rate: 115200 stop_bits: 1 parity: none - - id: wk2204_spi_id3 + - id: wk2204_spi_uart3 channel: 3 baud_rate: 9600 # Ensures a sensor doesn't break validation sensor: - platform: a02yyuw - uart_id: wk2204_spi_id3 + uart_id: wk2204_spi_uart3 id: distance_sensor diff --git a/tests/components/wk2204_spi/test.esp32-idf.yaml b/tests/components/wk2204_spi/test.esp32-idf.yaml index a3352cf880..2b56a46b70 100644 --- a/tests/components/wk2204_spi/test.esp32-idf.yaml +++ b/tests/components/wk2204_spi/test.esp32-idf.yaml @@ -3,5 +3,6 @@ substitutions: packages: spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2204_spi/test.esp32-s3-idf.yaml b/tests/components/wk2204_spi/test.esp32-s3-idf.yaml index 6a60c90fb2..374fe64d16 100644 --- a/tests/components/wk2204_spi/test.esp32-s3-idf.yaml +++ b/tests/components/wk2204_spi/test.esp32-s3-idf.yaml @@ -6,5 +6,6 @@ substitutions: packages: spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2212_i2c/test.esp32-idf.yaml b/tests/components/wk2212_i2c/test.esp32-idf.yaml index b47e39c389..9d9f0d4931 100644 --- a/tests/components/wk2212_i2c/test.esp32-idf.yaml +++ b/tests/components/wk2212_i2c/test.esp32-idf.yaml @@ -1,4 +1,5 @@ packages: i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml index e9d826aa7c..115812be97 100644 --- a/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml +++ b/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml @@ -4,5 +4,6 @@ substitutions: packages: i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2212_spi/test.esp32-idf.yaml b/tests/components/wk2212_spi/test.esp32-idf.yaml index a3352cf880..2b56a46b70 100644 --- a/tests/components/wk2212_spi/test.esp32-idf.yaml +++ b/tests/components/wk2212_spi/test.esp32-idf.yaml @@ -3,5 +3,6 @@ substitutions: packages: spi: !include ../../test_build_components/common/spi/esp32-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/wk2212_spi/test.esp32-s3-idf.yaml b/tests/components/wk2212_spi/test.esp32-s3-idf.yaml index 6a60c90fb2..374fe64d16 100644 --- a/tests/components/wk2212_spi/test.esp32-s3-idf.yaml +++ b/tests/components/wk2212_spi/test.esp32-s3-idf.yaml @@ -6,5 +6,6 @@ substitutions: packages: spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml <<: !include common.yaml diff --git a/tests/test_build_components/build_components_base.esp32-idf.yaml b/tests/test_build_components/build_components_base.esp32-idf.yaml index a62a995e68..dcb951c1ed 100644 --- a/tests/test_build_components/build_components_base.esp32-idf.yaml +++ b/tests/test_build_components/build_components_base.esp32-idf.yaml @@ -3,9 +3,13 @@ esphome: friendly_name: $component_name esp32: - board: nodemcu-32s + # Use board with 8MB flash for testing large component groups + board: esp32-pico-devkitm-2 framework: type: esp-idf + # Use custom partition table with larger app partitions (3MB each) + # Default IDF partitions only allow 1.75MB which is too small for grouped tests + partitions: ../partitions_testing.csv logger: level: VERY_VERBOSE diff --git a/tests/test_build_components/common/camera/esp32-idf.yaml b/tests/test_build_components/common/i2c_camera/esp32-idf.yaml similarity index 83% rename from tests/test_build_components/common/camera/esp32-idf.yaml rename to tests/test_build_components/common/i2c_camera/esp32-idf.yaml index 64f75c699a..a6e7c264cb 100644 --- a/tests/test_build_components/common/camera/esp32-idf.yaml +++ b/tests/test_build_components/common/i2c_camera/esp32-idf.yaml @@ -1,3 +1,10 @@ +# I2C bus for camera sensor +i2c: + - id: i2c_camera_bus + sda: 25 + scl: 23 + frequency: 400kHz + esp32_camera: name: ESP32 Camera data_pins: @@ -15,9 +22,7 @@ esp32_camera: external_clock: pin: 27 frequency: 20MHz - i2c_pins: - sda: 25 - scl: 23 + i2c_id: i2c_camera_bus reset_pin: 15 power_down_pin: 1 resolution: 640x480 diff --git a/tests/test_build_components/common/uart_bridge_2/esp32-idf.yaml b/tests/test_build_components/common/uart_bridge_2/esp32-idf.yaml new file mode 100644 index 0000000000..ff8a2f8d13 --- /dev/null +++ b/tests/test_build_components/common/uart_bridge_2/esp32-idf.yaml @@ -0,0 +1,11 @@ +# Common configuration for 2-channel UART bridge/expander chips +# Used by components like wk2132 that create 2 UART channels +# Defines standardized UART IDs: uart_id_0, uart_id_1 + +substitutions: + # These will be overridden by component-specific values + uart_bridge_address: "0x70" + +# Note: The actual UART instances are created by the bridge component +# This package just ensures all bridge components use the same ID naming convention +# so they can be grouped together without conflicts diff --git a/tests/test_build_components/common/uart_bridge_2/esp32-s3-idf.yaml b/tests/test_build_components/common/uart_bridge_2/esp32-s3-idf.yaml new file mode 100644 index 0000000000..ff8a2f8d13 --- /dev/null +++ b/tests/test_build_components/common/uart_bridge_2/esp32-s3-idf.yaml @@ -0,0 +1,11 @@ +# Common configuration for 2-channel UART bridge/expander chips +# Used by components like wk2132 that create 2 UART channels +# Defines standardized UART IDs: uart_id_0, uart_id_1 + +substitutions: + # These will be overridden by component-specific values + uart_bridge_address: "0x70" + +# Note: The actual UART instances are created by the bridge component +# This package just ensures all bridge components use the same ID naming convention +# so they can be grouped together without conflicts diff --git a/tests/test_build_components/common/uart_bridge_4/esp32-idf.yaml b/tests/test_build_components/common/uart_bridge_4/esp32-idf.yaml new file mode 100644 index 0000000000..bb88037947 --- /dev/null +++ b/tests/test_build_components/common/uart_bridge_4/esp32-idf.yaml @@ -0,0 +1,11 @@ +# Common configuration for 4-channel UART bridge/expander chips +# Used by components like wk2168, wk2204, wk2212 that create 4 UART channels +# Defines standardized UART IDs: uart_id_0, uart_id_1, uart_id_2, uart_id_3 + +substitutions: + # These will be overridden by component-specific values + uart_bridge_address: "0x70" + +# Note: The actual UART instances are created by the bridge component +# This package just ensures all bridge components use the same ID naming convention +# so they can be grouped together without conflicts diff --git a/tests/test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml b/tests/test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml new file mode 100644 index 0000000000..bb88037947 --- /dev/null +++ b/tests/test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml @@ -0,0 +1,11 @@ +# Common configuration for 4-channel UART bridge/expander chips +# Used by components like wk2168, wk2204, wk2212 that create 4 UART channels +# Defines standardized UART IDs: uart_id_0, uart_id_1, uart_id_2, uart_id_3 + +substitutions: + # These will be overridden by component-specific values + uart_bridge_address: "0x70" + +# Note: The actual UART instances are created by the bridge component +# This package just ensures all bridge components use the same ID naming convention +# so they can be grouped together without conflicts diff --git a/tests/test_build_components/partitions_testing.csv b/tests/test_build_components/partitions_testing.csv new file mode 100644 index 0000000000..0ca8c24e05 --- /dev/null +++ b/tests/test_build_components/partitions_testing.csv @@ -0,0 +1,10 @@ +# ESP-IDF Partition Table for ESPHome Component Testing +# Single app partition to maximize space for large component group testing +# Fits in 4MB flash +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x4000, +otadata, data, ota, , 0x2000, +phy_init, data, phy, , 0x1000, +factory, app, factory, 0x10000, 0x300000, +nvs_key, data, nvs_keys,, 0x1000, +coredump, data, coredump,, 0xEB000, From 7e2ccb7bc3e056e5ed4f31d7cd41e2411b72977e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 15 Oct 2025 23:45:42 -1000 Subject: [PATCH 102/526] [datetime] Fix DateTimeStateTrigger compilation when time component is not used (#11287) --- esphome/components/datetime/datetime_base.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/esphome/components/datetime/datetime_base.h b/esphome/components/datetime/datetime_base.h index b7645f5539..b5f54ac96f 100644 --- a/esphome/components/datetime/datetime_base.h +++ b/esphome/components/datetime/datetime_base.h @@ -30,14 +30,12 @@ class DateTimeBase : public EntityBase { #endif }; -#ifdef USE_TIME class DateTimeStateTrigger : public Trigger { public: explicit DateTimeStateTrigger(DateTimeBase *parent) { parent->add_on_state_callback([this, parent]() { this->trigger(parent->state_as_esptime()); }); } }; -#endif } // namespace datetime } // namespace esphome From 364e5ffd79d776f5040ab5b7d64c3a6f87febb85 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 16 Oct 2025 11:28:52 -0400 Subject: [PATCH 103/526] [core] Add ESP32 ROM functions to reserved ids (#11293) --- esphome/config_validation.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index ebfedf2017..e2f0b835c9 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -244,6 +244,20 @@ RESERVED_IDS = [ "uart0", "uart1", "uart2", + # ESP32 ROM functions + "crc16_be", + "crc16_le", + "crc32_be", + "crc32_le", + "crc8_be", + "crc8_le", + "dbg_state", + "debug_timer", + "one_bits", + "recv_packet", + "send_packet", + "check_pos", + "software_reset", ] From 5d3574c81fa82f45d53fcdca25c3af2e0c682217 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 16 Oct 2025 11:29:05 -0400 Subject: [PATCH 104/526] [htu21d] Revert register address change (#11291) --- esphome/components/htu21d/htu21d.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/htu21d/htu21d.cpp b/esphome/components/htu21d/htu21d.cpp index a7aae16f17..c5d91d3dd0 100644 --- a/esphome/components/htu21d/htu21d.cpp +++ b/esphome/components/htu21d/htu21d.cpp @@ -9,8 +9,8 @@ static const char *const TAG = "htu21d"; static const uint8_t HTU21D_ADDRESS = 0x40; static const uint8_t HTU21D_REGISTER_RESET = 0xFE; -static const uint8_t HTU21D_REGISTER_TEMPERATURE = 0xE3; -static const uint8_t HTU21D_REGISTER_HUMIDITY = 0xE5; +static const uint8_t HTU21D_REGISTER_TEMPERATURE = 0xF3; +static const uint8_t HTU21D_REGISTER_HUMIDITY = 0xF5; static const uint8_t HTU21D_WRITERHT_REG_CMD = 0xE6; /**< Write RH/T User Register 1 */ static const uint8_t HTU21D_REGISTER_STATUS = 0xE7; static const uint8_t HTU21D_WRITEHEATER_REG_CMD = 0x51; /**< Write Heater Control Register */ From b8353b3117de858f3f969be7995f9f2549420c69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 20:52:22 +0000 Subject: [PATCH 105/526] Bump ruff from 0.14.0 to 0.14.1 (#11303) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston --- .pre-commit-config.yaml | 2 +- requirements_test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 521aaf9cc8..9e0e71d388 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ ci: repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.14.0 + rev: v0.14.1 hooks: # Run the linter. - id: ruff diff --git a/requirements_test.txt b/requirements_test.txt index 56ac775a94..4c60a31d7f 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ pylint==4.0.1 flake8==7.3.0 # also change in .pre-commit-config.yaml when updating -ruff==0.14.0 # also change in .pre-commit-config.yaml when updating +ruff==0.14.1 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating pre-commit From cd1215347e0f879c9418e449bb184be3956c0c62 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:55:30 -0400 Subject: [PATCH 106/526] [esp32] Reduce tx power on Arduino (#11304) --- esphome/components/esp32/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 92f5c57638..deb80deaf9 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -805,6 +805,7 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_AUTOSTART_ARDUINO", True) add_idf_sdkconfig_option("CONFIG_MBEDTLS_PSK_MODES", True) add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True) + add_idf_sdkconfig_option("CONFIG_ESP_PHY_REDUCE_TX_POWER", True) cg.add_build_flag("-Wno-nonnull-compare") From 62f73c768e8e61da2265962e824732d0d15cdbea Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 16 Oct 2025 17:43:15 -0400 Subject: [PATCH 107/526] [esp32] Reduce tx power on IDF in the event of a brownout (#11306) --- esphome/components/esp32/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index deb80deaf9..b7dd25e0d8 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -805,7 +805,6 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_AUTOSTART_ARDUINO", True) add_idf_sdkconfig_option("CONFIG_MBEDTLS_PSK_MODES", True) add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True) - add_idf_sdkconfig_option("CONFIG_ESP_PHY_REDUCE_TX_POWER", True) cg.add_build_flag("-Wno-nonnull-compare") @@ -829,6 +828,9 @@ async def to_code(config): # Disable dynamic log level control to save memory add_idf_sdkconfig_option("CONFIG_LOG_DYNAMIC_LEVEL_CONTROL", False) + # Reduce PHY TX power in the event of a brownout + add_idf_sdkconfig_option("CONFIG_ESP_PHY_REDUCE_TX_POWER", True) + # Set default CPU frequency add_idf_sdkconfig_option( f"CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_{config[CONF_CPU_FREQUENCY][:-3]}", True From c6c202e4f75ad03fd756133f194855edefc93984 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 16 Oct 2025 21:57:19 -0400 Subject: [PATCH 108/526] [ina2xx_base] add device reset-on-boot disablement option (#10787) Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/ina2xx_base/__init__.py | 3 +++ esphome/components/ina2xx_base/ina2xx_base.cpp | 7 ++++++- esphome/components/ina2xx_base/ina2xx_base.h | 2 ++ tests/components/ina2xx_i2c/common.yaml | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/esphome/components/ina2xx_base/__init__.py b/esphome/components/ina2xx_base/__init__.py index fef88e72e9..ce68ad2726 100644 --- a/esphome/components/ina2xx_base/__init__.py +++ b/esphome/components/ina2xx_base/__init__.py @@ -35,6 +35,7 @@ CONF_CHARGE = "charge" CONF_CHARGE_COULOMBS = "charge_coulombs" CONF_ENERGY_JOULES = "energy_joules" CONF_TEMPERATURE_COEFFICIENT = "temperature_coefficient" +CONF_RESET_ON_BOOT = "reset_on_boot" UNIT_AMPERE_HOURS = "Ah" UNIT_COULOMB = "C" UNIT_JOULE = "J" @@ -113,6 +114,7 @@ INA2XX_SCHEMA = cv.Schema( cv.Optional(CONF_TEMPERATURE_COEFFICIENT, default=0): cv.int_range( min=0, max=16383 ), + cv.Optional(CONF_RESET_ON_BOOT, default=True): cv.boolean, cv.Optional(CONF_SHUNT_VOLTAGE): cv.maybe_simple_value( sensor.sensor_schema( unit_of_measurement=UNIT_MILLIVOLT, @@ -206,6 +208,7 @@ async def setup_ina2xx(var, config): cg.add(var.set_adc_range(config[CONF_ADC_RANGE])) cg.add(var.set_adc_avg_samples(config[CONF_ADC_AVERAGING])) cg.add(var.set_shunt_tempco(config[CONF_TEMPERATURE_COEFFICIENT])) + cg.add(var.set_reset_on_boot(config[CONF_RESET_ON_BOOT])) adc_time_config = config[CONF_ADC_TIME] if isinstance(adc_time_config, dict): diff --git a/esphome/components/ina2xx_base/ina2xx_base.cpp b/esphome/components/ina2xx_base/ina2xx_base.cpp index 35a94e3989..4ab02703e8 100644 --- a/esphome/components/ina2xx_base/ina2xx_base.cpp +++ b/esphome/components/ina2xx_base/ina2xx_base.cpp @@ -257,7 +257,12 @@ bool INA2XX::reset_energy_counters() { bool INA2XX::reset_config_() { ESP_LOGV(TAG, "Reset"); ConfigurationRegister cfg{0}; - cfg.RST = true; + if (!this->reset_on_boot_) { + ESP_LOGI(TAG, "Skipping on-boot device reset"); + cfg.RST = false; + } else { + cfg.RST = true; + } return this->write_unsigned_16_(RegisterMap::REG_CONFIG, cfg.raw_u16); } diff --git a/esphome/components/ina2xx_base/ina2xx_base.h b/esphome/components/ina2xx_base/ina2xx_base.h index 261c5321bf..ba0999b28e 100644 --- a/esphome/components/ina2xx_base/ina2xx_base.h +++ b/esphome/components/ina2xx_base/ina2xx_base.h @@ -127,6 +127,7 @@ class INA2XX : public PollingComponent { void set_adc_time_die_temperature(AdcTime time) { this->adc_time_die_temperature_ = time; } void set_adc_avg_samples(AdcAvgSamples samples) { this->adc_avg_samples_ = samples; } void set_shunt_tempco(uint16_t coeff) { this->shunt_tempco_ppm_c_ = coeff; } + void set_reset_on_boot(bool reset) { this->reset_on_boot_ = reset; } void set_shunt_voltage_sensor(sensor::Sensor *sensor) { this->shunt_voltage_sensor_ = sensor; } void set_bus_voltage_sensor(sensor::Sensor *sensor) { this->bus_voltage_sensor_ = sensor; } @@ -172,6 +173,7 @@ class INA2XX : public PollingComponent { AdcTime adc_time_die_temperature_{AdcTime::ADC_TIME_4120US}; AdcAvgSamples adc_avg_samples_{AdcAvgSamples::ADC_AVG_SAMPLES_128}; uint16_t shunt_tempco_ppm_c_{0}; + bool reset_on_boot_{true}; // // Calculated coefficients diff --git a/tests/components/ina2xx_i2c/common.yaml b/tests/components/ina2xx_i2c/common.yaml index 2416ad6daf..748ab94c98 100644 --- a/tests/components/ina2xx_i2c/common.yaml +++ b/tests/components/ina2xx_i2c/common.yaml @@ -7,6 +7,7 @@ sensor: max_current: 40 A adc_range: 1 temperature_coefficient: 50 + reset_on_boot: true shunt_voltage: id: ina2xx_i2c_shunt_voltage name: "INA2xx Shunt Voltage" From 9646653e57053d16d5e1d1d4a2cda90c838d6e43 Mon Sep 17 00:00:00 2001 From: Daniel Stiner Date: Fri, 17 Oct 2025 09:02:28 +0200 Subject: [PATCH 109/526] [const] Add CONF_OPENTHREAD (#11318) --- esphome/const.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/const.py b/esphome/const.py index d62dc617d1..f3f177b3ae 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -696,6 +696,7 @@ CONF_OPEN_DRAIN = "open_drain" CONF_OPEN_DRAIN_INTERRUPT = "open_drain_interrupt" CONF_OPEN_DURATION = "open_duration" CONF_OPEN_ENDSTOP = "open_endstop" +CONF_OPENTHREAD = "openthread" CONF_OPERATION = "operation" CONF_OPTIMISTIC = "optimistic" CONF_OPTION = "option" From bdfbac0301b633ad234fc8696c77033715aceffd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 16 Oct 2025 21:20:00 -1000 Subject: [PATCH 110/526] [tests] Fix ESP32-C3 component test binary size by using larger partition table (#11319) --- .../build_components_base.esp32-c3-idf.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_build_components/build_components_base.esp32-c3-idf.yaml b/tests/test_build_components/build_components_base.esp32-c3-idf.yaml index 18584497f4..73a85467d3 100644 --- a/tests/test_build_components/build_components_base.esp32-c3-idf.yaml +++ b/tests/test_build_components/build_components_base.esp32-c3-idf.yaml @@ -6,6 +6,9 @@ esp32: board: lolin_c3_mini framework: type: esp-idf + # Use custom partition table with larger app partition (3MB) + # Default IDF partitions only allow 1.75MB which is too small for grouped tests + partitions: ../partitions_testing.csv logger: level: VERY_VERBOSE From 39e23c323d2144163e62d3290ee9c644341422bf Mon Sep 17 00:00:00 2001 From: esphomebot Date: Fri, 17 Oct 2025 20:49:10 +1300 Subject: [PATCH 111/526] Synchronise Device Classes from Home Assistant (#11285) --- esphome/components/number/__init__.py | 2 ++ esphome/components/sensor/__init__.py | 2 ++ esphome/const.py | 1 + 3 files changed, 5 insertions(+) diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index 76a7b05ea1..230c3aa0c1 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -66,6 +66,7 @@ from esphome.const import ( DEVICE_CLASS_SPEED, DEVICE_CLASS_SULPHUR_DIOXIDE, DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_TEMPERATURE_DELTA, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLTAGE, @@ -130,6 +131,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_SPEED, DEVICE_CLASS_SULPHUR_DIOXIDE, DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_TEMPERATURE_DELTA, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLTAGE, diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 2b99f68ac0..bf13217787 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -89,6 +89,7 @@ from esphome.const import ( DEVICE_CLASS_SPEED, DEVICE_CLASS_SULPHUR_DIOXIDE, DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_TEMPERATURE_DELTA, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, @@ -157,6 +158,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_SPEED, DEVICE_CLASS_SULPHUR_DIOXIDE, DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_TEMPERATURE_DELTA, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, diff --git a/esphome/const.py b/esphome/const.py index f3f177b3ae..ce1c033e41 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1300,6 +1300,7 @@ DEVICE_CLASS_SULPHUR_DIOXIDE = "sulphur_dioxide" DEVICE_CLASS_SWITCH = "switch" DEVICE_CLASS_TAMPER = "tamper" DEVICE_CLASS_TEMPERATURE = "temperature" +DEVICE_CLASS_TEMPERATURE_DELTA = "temperature_delta" DEVICE_CLASS_TIMESTAMP = "timestamp" DEVICE_CLASS_UPDATE = "update" DEVICE_CLASS_VIBRATION = "vibration" From 661e9f9991ba1460bba3c915973944a686393320 Mon Sep 17 00:00:00 2001 From: exotime Date: Fri, 17 Oct 2025 17:33:50 +0900 Subject: [PATCH 112/526] [toshiba] Add support for RAS-2819T air conditioner (#9490) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Keith Burzinski --- esphome/components/toshiba/climate.py | 1 + esphome/components/toshiba/toshiba.cpp | 653 +++++++++++++++++- esphome/components/toshiba/toshiba.h | 26 +- tests/components/toshiba/common_ras2819t.yaml | 13 + .../toshiba/test_ras2819t.esp32-ard.yaml | 5 + .../toshiba/test_ras2819t.esp32-c3-ard.yaml | 5 + .../toshiba/test_ras2819t.esp32-c3-idf.yaml | 5 + .../toshiba/test_ras2819t.esp32-idf.yaml | 5 + .../toshiba/test_ras2819t.esp8266-ard.yaml | 5 + 9 files changed, 689 insertions(+), 29 deletions(-) create mode 100644 tests/components/toshiba/common_ras2819t.yaml create mode 100644 tests/components/toshiba/test_ras2819t.esp32-ard.yaml create mode 100644 tests/components/toshiba/test_ras2819t.esp32-c3-ard.yaml create mode 100644 tests/components/toshiba/test_ras2819t.esp32-c3-idf.yaml create mode 100644 tests/components/toshiba/test_ras2819t.esp32-idf.yaml create mode 100644 tests/components/toshiba/test_ras2819t.esp8266-ard.yaml diff --git a/esphome/components/toshiba/climate.py b/esphome/components/toshiba/climate.py index b8e390dd66..bdb17923fa 100644 --- a/esphome/components/toshiba/climate.py +++ b/esphome/components/toshiba/climate.py @@ -14,6 +14,7 @@ MODELS = { "GENERIC": Model.MODEL_GENERIC, "RAC-PT1411HWRU-C": Model.MODEL_RAC_PT1411HWRU_C, "RAC-PT1411HWRU-F": Model.MODEL_RAC_PT1411HWRU_F, + "RAS-2819T": Model.MODEL_RAS_2819T, } CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(ToshibaClimate).extend( diff --git a/esphome/components/toshiba/toshiba.cpp b/esphome/components/toshiba/toshiba.cpp index ff4241a81f..36e5a21ffa 100644 --- a/esphome/components/toshiba/toshiba.cpp +++ b/esphome/components/toshiba/toshiba.cpp @@ -1,4 +1,5 @@ #include "toshiba.h" +#include "esphome/components/remote_base/toshiba_ac_protocol.h" #include @@ -97,6 +98,282 @@ const std::vector RAC_PT1411HWRU_TEMPERATURE_F{0x10, 0x30, 0x00, 0x20, 0x22, 0x06, 0x26, 0x07, 0x05, 0x25, 0x04, 0x24, 0x0C, 0x2C, 0x0D, 0x2D, 0x09, 0x08, 0x28, 0x0A, 0x2A, 0x0B}; +// RAS-2819T protocol constants +const uint16_t RAS_2819T_HEADER1 = 0xC23D; +const uint8_t RAS_2819T_HEADER2 = 0xD5; +const uint8_t RAS_2819T_MESSAGE_LENGTH = 6; + +// RAS-2819T fan speed codes for rc_code_1 (bytes 2-3) +const uint16_t RAS_2819T_FAN_AUTO = 0xBF40; +const uint16_t RAS_2819T_FAN_QUIET = 0xFF00; +const uint16_t RAS_2819T_FAN_LOW = 0x9F60; +const uint16_t RAS_2819T_FAN_MEDIUM = 0x5FA0; +const uint16_t RAS_2819T_FAN_HIGH = 0x3FC0; + +// RAS-2819T fan speed codes for rc_code_2 (byte 1) +const uint8_t RAS_2819T_FAN2_AUTO = 0x66; +const uint8_t RAS_2819T_FAN2_QUIET = 0x01; +const uint8_t RAS_2819T_FAN2_LOW = 0x28; +const uint8_t RAS_2819T_FAN2_MEDIUM = 0x3C; +const uint8_t RAS_2819T_FAN2_HIGH = 0x50; + +// RAS-2819T second packet suffix bytes for rc_code_2 (bytes 3-5) +// These are fixed patterns, not actual checksums +struct Ras2819tPacketSuffix { + uint8_t byte3; + uint8_t byte4; + uint8_t byte5; +}; +const Ras2819tPacketSuffix RAS_2819T_SUFFIX_AUTO{0x00, 0x02, 0x3D}; +const Ras2819tPacketSuffix RAS_2819T_SUFFIX_QUIET{0x00, 0x02, 0xD8}; +const Ras2819tPacketSuffix RAS_2819T_SUFFIX_LOW{0x00, 0x02, 0xFF}; +const Ras2819tPacketSuffix RAS_2819T_SUFFIX_MEDIUM{0x00, 0x02, 0x13}; +const Ras2819tPacketSuffix RAS_2819T_SUFFIX_HIGH{0x00, 0x02, 0x27}; + +// RAS-2819T swing toggle command +const uint64_t RAS_2819T_SWING_TOGGLE = 0xC23D6B94E01F; + +// RAS-2819T single-packet commands +const uint64_t RAS_2819T_POWER_OFF_COMMAND = 0xC23D7B84E01F; + +// RAS-2819T known valid command patterns for validation +const std::array RAS_2819T_VALID_SINGLE_COMMANDS = { + RAS_2819T_POWER_OFF_COMMAND, // Power off + RAS_2819T_SWING_TOGGLE, // Swing toggle +}; + +const uint16_t RAS_2819T_VALID_HEADER1 = 0xC23D; +const uint8_t RAS_2819T_VALID_HEADER2 = 0xD5; + +const uint8_t RAS_2819T_DRY_BYTE2 = 0x1F; +const uint8_t RAS_2819T_DRY_BYTE3 = 0xE0; +const uint8_t RAS_2819T_DRY_TEMP_OFFSET = 0x24; + +const uint8_t RAS_2819T_AUTO_BYTE2 = 0x1F; +const uint8_t RAS_2819T_AUTO_BYTE3 = 0xE0; +const uint8_t RAS_2819T_AUTO_TEMP_OFFSET = 0x08; + +const uint8_t RAS_2819T_FAN_ONLY_TEMP = 0xE4; +const uint8_t RAS_2819T_FAN_ONLY_TEMP_INV = 0x1B; + +const uint8_t RAS_2819T_HEAT_TEMP_OFFSET = 0x0C; + +// RAS-2819T second packet fixed values +const uint8_t RAS_2819T_AUTO_DRY_FAN_BYTE = 0x65; +const uint8_t RAS_2819T_AUTO_DRY_SUFFIX = 0x3A; +const uint8_t RAS_2819T_HEAT_SUFFIX = 0x3B; + +// RAS-2819T temperature codes for 18-30°C +static const uint8_t RAS_2819T_TEMP_CODES[] = { + 0x10, // 18°C + 0x30, // 19°C + 0x20, // 20°C + 0x60, // 21°C + 0x70, // 22°C + 0x50, // 23°C + 0x40, // 24°C + 0xC0, // 25°C + 0xD0, // 26°C + 0x90, // 27°C + 0x80, // 28°C + 0xA0, // 29°C + 0xB0 // 30°C +}; + +// Helper functions for RAS-2819T protocol +// +// ===== RAS-2819T PROTOCOL DOCUMENTATION ===== +// +// The RAS-2819T uses a two-packet IR protocol with some exceptions for simple commands. +// +// PACKET STRUCTURE: +// All packets are 6 bytes (48 bits) transmitted with standard Toshiba timing. +// +// TWO-PACKET COMMANDS (Mode/Temperature/Fan changes): +// +// First Packet (rc_code_1): [C2 3D] [FAN_HI FAN_LO] [TEMP] [~TEMP] +// Byte 0-1: Header (always 0xC23D) +// Byte 2-3: Fan speed encoding (varies by mode, see fan tables below) +// Byte 4: Temperature + mode encoding +// Byte 5: Bitwise complement of temperature byte +// +// Second Packet (rc_code_2): [D5] [FAN2] [00] [SUF1] [SUF2] [SUF3] +// Byte 0: Header (always 0xD5) +// Byte 1: Fan speed secondary encoding +// Byte 2: Always 0x00 +// Byte 3-5: Fixed suffix pattern (depends on fan speed and mode) +// +// TEMPERATURE ENCODING: +// Base temp codes: 18°C=0x10, 19°C=0x30, 20°C=0x20, 21°C=0x60, 22°C=0x70, +// 23°C=0x50, 24°C=0x40, 25°C=0xC0, 26°C=0xD0, 27°C=0x90, +// 28°C=0x80, 29°C=0xA0, 30°C=0xB0 +// Mode offsets added to base temp: +// COOL: No offset +// HEAT: +0x0C (e.g., 24°C heat = 0x40 | 0x0C = 0x4C) +// AUTO: +0x08 (e.g., 24°C auto = 0x40 | 0x08 = 0x48) +// DRY: +0x24 (e.g., 24°C dry = 0x40 | 0x24 = 0x64) +// +// FAN SPEED ENCODING (First packet bytes 2-3): +// AUTO: 0xBF40, QUIET: 0xFF00, LOW: 0x9F60, MEDIUM: 0x5FA0, HIGH: 0x3FC0 +// Special cases: AUTO/DRY modes use 0x1FE0 instead +// +// SINGLE-PACKET COMMANDS: +// Power Off: 0xC23D7B84E01F (6 bytes, no second packet) +// Swing Toggle: 0xC23D6B94E01F (6 bytes, no second packet) +// +// MODE DETECTION (from first packet): +// - Check bytes 2-3: if 0x7B84 → OFF mode +// - Check bytes 2-3: if 0x1FE0 → AUTO/DRY/low-temp-COOL (distinguish by temp code) +// - Otherwise: COOL/HEAT/FAN_ONLY (distinguish by temp code and byte 5) + +/** + * Get fan speed encoding for RAS-2819T first packet (rc_code_1, bytes 2-3) + */ +static uint16_t get_ras_2819t_fan_code(climate::ClimateFanMode fan_mode) { + switch (fan_mode) { + case climate::CLIMATE_FAN_QUIET: + return RAS_2819T_FAN_QUIET; + case climate::CLIMATE_FAN_LOW: + return RAS_2819T_FAN_LOW; + case climate::CLIMATE_FAN_MEDIUM: + return RAS_2819T_FAN_MEDIUM; + case climate::CLIMATE_FAN_HIGH: + return RAS_2819T_FAN_HIGH; + case climate::CLIMATE_FAN_AUTO: + default: + return RAS_2819T_FAN_AUTO; + } +} + +/** + * Get fan speed encoding for RAS-2819T rc_code_2 packet (second packet) + */ +struct Ras2819tSecondPacketCodes { + uint8_t fan_byte; + Ras2819tPacketSuffix suffix; +}; + +static Ras2819tSecondPacketCodes get_ras_2819t_second_packet_codes(climate::ClimateFanMode fan_mode) { + switch (fan_mode) { + case climate::CLIMATE_FAN_QUIET: + return {RAS_2819T_FAN2_QUIET, RAS_2819T_SUFFIX_QUIET}; + case climate::CLIMATE_FAN_LOW: + return {RAS_2819T_FAN2_LOW, RAS_2819T_SUFFIX_LOW}; + case climate::CLIMATE_FAN_MEDIUM: + return {RAS_2819T_FAN2_MEDIUM, RAS_2819T_SUFFIX_MEDIUM}; + case climate::CLIMATE_FAN_HIGH: + return {RAS_2819T_FAN2_HIGH, RAS_2819T_SUFFIX_HIGH}; + case climate::CLIMATE_FAN_AUTO: + default: + return {RAS_2819T_FAN2_AUTO, RAS_2819T_SUFFIX_AUTO}; + } +} + +/** + * Get temperature code for RAS-2819T protocol + */ +static uint8_t get_ras_2819t_temp_code(float temperature) { + int temp_index = static_cast(temperature) - 18; + if (temp_index < 0 || temp_index >= static_cast(sizeof(RAS_2819T_TEMP_CODES))) { + ESP_LOGW(TAG, "Temperature %.1f°C out of range [18-30°C], defaulting to 24°C", temperature); + return 0x40; // Default to 24°C + } + + return RAS_2819T_TEMP_CODES[temp_index]; +} + +/** + * Decode temperature from RAS-2819T temp code + */ +static float decode_ras_2819t_temperature(uint8_t temp_code) { + uint8_t base_temp_code = temp_code & 0xF0; + + // Find the code in the temperature array + for (size_t temp_index = 0; temp_index < sizeof(RAS_2819T_TEMP_CODES); temp_index++) { + if (RAS_2819T_TEMP_CODES[temp_index] == base_temp_code) { + return static_cast(temp_index + 18); // 18°C is the minimum + } + } + + ESP_LOGW(TAG, "Unknown temp code: 0x%02X, defaulting to 24°C", base_temp_code); + return 24.0f; // Default to 24°C +} + +/** + * Decode fan speed from RAS-2819T IR codes + */ +static climate::ClimateFanMode decode_ras_2819t_fan_mode(uint16_t fan_code) { + switch (fan_code) { + case RAS_2819T_FAN_QUIET: + return climate::CLIMATE_FAN_QUIET; + case RAS_2819T_FAN_LOW: + return climate::CLIMATE_FAN_LOW; + case RAS_2819T_FAN_MEDIUM: + return climate::CLIMATE_FAN_MEDIUM; + case RAS_2819T_FAN_HIGH: + return climate::CLIMATE_FAN_HIGH; + case RAS_2819T_FAN_AUTO: + default: + return climate::CLIMATE_FAN_AUTO; + } +} + +/** + * Validate RAS-2819T IR command structure and content + */ +static bool is_valid_ras_2819t_command(uint64_t rc_code_1, uint64_t rc_code_2 = 0) { + // Check header of first packet + uint16_t header1 = (rc_code_1 >> 32) & 0xFFFF; + if (header1 != RAS_2819T_VALID_HEADER1) { + return false; + } + + // Single packet commands + if (rc_code_2 == 0) { + for (uint64_t valid_cmd : RAS_2819T_VALID_SINGLE_COMMANDS) { + if (rc_code_1 == valid_cmd) { + return true; + } + } + // Additional validation for unknown single packets + return false; + } + + // Two-packet commands - validate second packet header + uint8_t header2 = (rc_code_2 >> 40) & 0xFF; + if (header2 != RAS_2819T_VALID_HEADER2) { + return false; + } + + // Validate temperature complement in first packet (byte 4 should be ~byte 5) + uint8_t temp_byte = (rc_code_1 >> 8) & 0xFF; + uint8_t temp_complement = rc_code_1 & 0xFF; + if (temp_byte != static_cast(~temp_complement)) { + return false; + } + + // Validate fan speed combinations make sense + uint16_t fan_code = (rc_code_1 >> 16) & 0xFFFF; + uint8_t fan2_byte = (rc_code_2 >> 32) & 0xFF; + + // Check if fan codes are from known valid patterns + bool valid_fan_combo = false; + if (fan_code == RAS_2819T_FAN_AUTO && fan2_byte == RAS_2819T_FAN2_AUTO) + valid_fan_combo = true; + if (fan_code == RAS_2819T_FAN_QUIET && fan2_byte == RAS_2819T_FAN2_QUIET) + valid_fan_combo = true; + if (fan_code == RAS_2819T_FAN_LOW && fan2_byte == RAS_2819T_FAN2_LOW) + valid_fan_combo = true; + if (fan_code == RAS_2819T_FAN_MEDIUM && fan2_byte == RAS_2819T_FAN2_MEDIUM) + valid_fan_combo = true; + if (fan_code == RAS_2819T_FAN_HIGH && fan2_byte == RAS_2819T_FAN2_HIGH) + valid_fan_combo = true; + if (fan_code == 0x1FE0 && fan2_byte == RAS_2819T_AUTO_DRY_FAN_BYTE) + valid_fan_combo = true; // AUTO/DRY + + return valid_fan_combo; +} + void ToshibaClimate::setup() { if (this->sensor_) { this->sensor_->add_on_state_callback([this](float state) { @@ -126,16 +403,43 @@ void ToshibaClimate::setup() { this->minimum_temperature_ = this->temperature_min_(); this->maximum_temperature_ = this->temperature_max_(); this->swing_modes_ = this->toshiba_swing_modes_(); + + // Ensure swing mode is always initialized to a valid value + if (this->swing_modes_.empty() || this->swing_modes_.find(this->swing_mode) == this->swing_modes_.end()) { + // No swing support for this model or current swing mode not supported, reset to OFF + this->swing_mode = climate::CLIMATE_SWING_OFF; + } + + // Ensure mode is valid - ESPHome should only use standard climate modes + if (this->mode != climate::CLIMATE_MODE_OFF && this->mode != climate::CLIMATE_MODE_HEAT && + this->mode != climate::CLIMATE_MODE_COOL && this->mode != climate::CLIMATE_MODE_HEAT_COOL && + this->mode != climate::CLIMATE_MODE_DRY && this->mode != climate::CLIMATE_MODE_FAN_ONLY) { + ESP_LOGW(TAG, "Invalid mode detected during setup, resetting to OFF"); + this->mode = climate::CLIMATE_MODE_OFF; + } + + // Ensure fan mode is valid + if (!this->fan_mode.has_value()) { + ESP_LOGW(TAG, "Fan mode not set during setup, defaulting to AUTO"); + this->fan_mode = climate::CLIMATE_FAN_AUTO; + } + // Never send nan to HA if (std::isnan(this->target_temperature)) this->target_temperature = 24; + // Log final state for debugging HA errors + ESP_LOGV(TAG, "Setup complete - Mode: %d, Fan: %s, Swing: %d, Temp: %.1f", static_cast(this->mode), + this->fan_mode.has_value() ? std::to_string(static_cast(this->fan_mode.value())).c_str() : "NONE", + static_cast(this->swing_mode), this->target_temperature); } void ToshibaClimate::transmit_state() { if (this->model_ == MODEL_RAC_PT1411HWRU_C || this->model_ == MODEL_RAC_PT1411HWRU_F) { - transmit_rac_pt1411hwru_(); + this->transmit_rac_pt1411hwru_(); + } else if (this->model_ == MODEL_RAS_2819T) { + this->transmit_ras_2819t_(); } else { - transmit_generic_(); + this->transmit_generic_(); } } @@ -230,7 +534,7 @@ void ToshibaClimate::transmit_generic_() { auto transmit = this->transmitter_->transmit(); auto *data = transmit.get_data(); - encode_(data, message, message_length, 1); + this->encode_(data, message, message_length, 1); transmit.perform(); } @@ -348,15 +652,12 @@ void ToshibaClimate::transmit_rac_pt1411hwru_() { message[11] += message[index]; } } - ESP_LOGV(TAG, "*** Generated codes: 0x%.2X%.2X%.2X%.2X%.2X%.2X 0x%.2X%.2X%.2X%.2X%.2X%.2X", message[0], message[1], - message[2], message[3], message[4], message[5], message[6], message[7], message[8], message[9], message[10], - message[11]); // load first block of IR code and repeat it once - encode_(data, &message[0], RAC_PT1411HWRU_MESSAGE_LENGTH, 1); + this->encode_(data, &message[0], RAC_PT1411HWRU_MESSAGE_LENGTH, 1); // load second block of IR code, if present if (message[6] != 0) { - encode_(data, &message[6], RAC_PT1411HWRU_MESSAGE_LENGTH, 0); + this->encode_(data, &message[6], RAC_PT1411HWRU_MESSAGE_LENGTH, 0); } transmit.perform(); @@ -366,19 +667,19 @@ void ToshibaClimate::transmit_rac_pt1411hwru_() { data->space(TOSHIBA_PACKET_SPACE); switch (this->swing_mode) { case climate::CLIMATE_SWING_VERTICAL: - encode_(data, &RAC_PT1411HWRU_SWING_VERTICAL[0], RAC_PT1411HWRU_MESSAGE_LENGTH, 1); + this->encode_(data, &RAC_PT1411HWRU_SWING_VERTICAL[0], RAC_PT1411HWRU_MESSAGE_LENGTH, 1); break; case climate::CLIMATE_SWING_OFF: default: - encode_(data, &RAC_PT1411HWRU_SWING_OFF[0], RAC_PT1411HWRU_MESSAGE_LENGTH, 1); + this->encode_(data, &RAC_PT1411HWRU_SWING_OFF[0], RAC_PT1411HWRU_MESSAGE_LENGTH, 1); } data->space(TOSHIBA_PACKET_SPACE); transmit.perform(); if (this->sensor_) { - transmit_rac_pt1411hwru_temp_(true, false); + this->transmit_rac_pt1411hwru_temp_(true, false); } } @@ -430,15 +731,217 @@ void ToshibaClimate::transmit_rac_pt1411hwru_temp_(const bool cs_state, const bo // Byte 5: Footer lower/bitwise complement of byte 4 message[5] = ~message[4]; - ESP_LOGV(TAG, "*** Generated code: 0x%.2X%.2X%.2X%.2X%.2X%.2X", message[0], message[1], message[2], message[3], - message[4], message[5]); // load IR code and repeat it once - encode_(data, message, RAC_PT1411HWRU_MESSAGE_LENGTH, 1); + this->encode_(data, message, RAC_PT1411HWRU_MESSAGE_LENGTH, 1); transmit.perform(); } } +void ToshibaClimate::transmit_ras_2819t_() { + // Handle swing mode transmission for RAS-2819T + // Note: RAS-2819T uses a toggle command, so we need to track state changes + + // Check if ONLY swing mode changed (and no other climate parameters) + bool swing_changed = (this->swing_mode != this->last_swing_mode_); + bool mode_changed = (this->mode != this->last_mode_); + bool fan_changed = (this->fan_mode != this->last_fan_mode_); + bool temp_changed = (abs(this->target_temperature - this->last_target_temperature_) > 0.1f); + + bool only_swing_changed = swing_changed && !mode_changed && !fan_changed && !temp_changed; + + if (only_swing_changed) { + // Send ONLY swing toggle command (like the physical remote does) + auto swing_transmit = this->transmitter_->transmit(); + auto *swing_data = swing_transmit.get_data(); + + // Convert toggle command to bytes for transmission + uint8_t swing_message[RAS_2819T_MESSAGE_LENGTH]; + swing_message[0] = (RAS_2819T_SWING_TOGGLE >> 40) & 0xFF; + swing_message[1] = (RAS_2819T_SWING_TOGGLE >> 32) & 0xFF; + swing_message[2] = (RAS_2819T_SWING_TOGGLE >> 24) & 0xFF; + swing_message[3] = (RAS_2819T_SWING_TOGGLE >> 16) & 0xFF; + swing_message[4] = (RAS_2819T_SWING_TOGGLE >> 8) & 0xFF; + swing_message[5] = RAS_2819T_SWING_TOGGLE & 0xFF; + + // Use single packet transmission WITH repeat (like regular commands) + this->encode_(swing_data, swing_message, RAS_2819T_MESSAGE_LENGTH, 1); + swing_transmit.perform(); + + // Update all state tracking + this->last_swing_mode_ = this->swing_mode; + this->last_mode_ = this->mode; + this->last_fan_mode_ = this->fan_mode; + this->last_target_temperature_ = this->target_temperature; + + // Immediately publish the state change to Home Assistant + this->publish_state(); + + return; // Exit early - don't send climate command + } + + // If we get here, send the regular climate command (temperature/mode/fan) + uint8_t message1[RAS_2819T_MESSAGE_LENGTH] = {0}; + uint8_t message2[RAS_2819T_MESSAGE_LENGTH] = {0}; + float temperature = + clamp(this->target_temperature, TOSHIBA_RAS_2819T_TEMP_C_MIN, TOSHIBA_RAS_2819T_TEMP_C_MAX); + + // Build first packet (RAS_2819T_HEADER1 + 4 bytes) + message1[0] = (RAS_2819T_HEADER1 >> 8) & 0xFF; + message1[1] = RAS_2819T_HEADER1 & 0xFF; + + // Handle OFF mode + if (this->mode == climate::CLIMATE_MODE_OFF) { + // Extract bytes from power off command constant + message1[2] = (RAS_2819T_POWER_OFF_COMMAND >> 24) & 0xFF; + message1[3] = (RAS_2819T_POWER_OFF_COMMAND >> 16) & 0xFF; + message1[4] = (RAS_2819T_POWER_OFF_COMMAND >> 8) & 0xFF; + message1[5] = RAS_2819T_POWER_OFF_COMMAND & 0xFF; + // No second packet for OFF + } else { + // Get temperature and fan encoding + uint8_t temp_code = get_ras_2819t_temp_code(temperature); + + // Get fan speed encoding for rc_code_1 + climate::ClimateFanMode effective_fan_mode = this->fan_mode.value(); + + // Dry mode only supports AUTO fan speed + if (this->mode == climate::CLIMATE_MODE_DRY) { + effective_fan_mode = climate::CLIMATE_FAN_AUTO; + if (this->fan_mode.value() != climate::CLIMATE_FAN_AUTO) { + ESP_LOGW(TAG, "Dry mode only supports AUTO fan speed, forcing AUTO"); + } + } + + uint16_t fan_code = get_ras_2819t_fan_code(effective_fan_mode); + + // Mode and temperature encoding + switch (this->mode) { + case climate::CLIMATE_MODE_COOL: + // All cooling temperatures support fan speed control + message1[2] = (fan_code >> 8) & 0xFF; + message1[3] = fan_code & 0xFF; + message1[4] = temp_code; + message1[5] = ~temp_code; + break; + + case climate::CLIMATE_MODE_HEAT: + // Heating supports fan speed control + message1[2] = (fan_code >> 8) & 0xFF; + message1[3] = fan_code & 0xFF; + // Heat mode adds offset to temperature code + message1[4] = temp_code | RAS_2819T_HEAT_TEMP_OFFSET; + message1[5] = ~(temp_code | RAS_2819T_HEAT_TEMP_OFFSET); + break; + + case climate::CLIMATE_MODE_HEAT_COOL: + // Auto mode uses fixed encoding + message1[2] = RAS_2819T_AUTO_BYTE2; + message1[3] = RAS_2819T_AUTO_BYTE3; + message1[4] = temp_code | RAS_2819T_AUTO_TEMP_OFFSET; + message1[5] = ~(temp_code | RAS_2819T_AUTO_TEMP_OFFSET); + break; + + case climate::CLIMATE_MODE_DRY: + // Dry mode uses fixed encoding and forces AUTO fan + message1[2] = RAS_2819T_DRY_BYTE2; + message1[3] = RAS_2819T_DRY_BYTE3; + message1[4] = temp_code | RAS_2819T_DRY_TEMP_OFFSET; + message1[5] = ~message1[4]; + break; + + case climate::CLIMATE_MODE_FAN_ONLY: + // Fan only mode supports fan speed control + message1[2] = (fan_code >> 8) & 0xFF; + message1[3] = fan_code & 0xFF; + message1[4] = RAS_2819T_FAN_ONLY_TEMP; + message1[5] = RAS_2819T_FAN_ONLY_TEMP_INV; + break; + + default: + // Default case supports fan speed control + message1[2] = (fan_code >> 8) & 0xFF; + message1[3] = fan_code & 0xFF; + message1[4] = temp_code; + message1[5] = ~temp_code; + break; + } + + // Build second packet (RAS_2819T_HEADER2 + 4 bytes) + message2[0] = RAS_2819T_HEADER2; + + // Get fan speed encoding for rc_code_2 + Ras2819tSecondPacketCodes second_packet_codes = get_ras_2819t_second_packet_codes(effective_fan_mode); + + // Determine header byte 2 and fan encoding based on mode + switch (this->mode) { + case climate::CLIMATE_MODE_COOL: + message2[1] = second_packet_codes.fan_byte; + message2[2] = 0x00; + message2[3] = second_packet_codes.suffix.byte3; + message2[4] = second_packet_codes.suffix.byte4; + message2[5] = second_packet_codes.suffix.byte5; + break; + + case climate::CLIMATE_MODE_HEAT: + message2[1] = second_packet_codes.fan_byte; + message2[2] = 0x00; + message2[3] = second_packet_codes.suffix.byte3; + message2[4] = 0x00; + message2[5] = RAS_2819T_HEAT_SUFFIX; + break; + + case climate::CLIMATE_MODE_HEAT_COOL: + case climate::CLIMATE_MODE_DRY: + // Auto/Dry modes use fixed values regardless of fan setting + message2[1] = RAS_2819T_AUTO_DRY_FAN_BYTE; + message2[2] = 0x00; + message2[3] = 0x00; + message2[4] = 0x00; + message2[5] = RAS_2819T_AUTO_DRY_SUFFIX; + break; + + case climate::CLIMATE_MODE_FAN_ONLY: + message2[1] = second_packet_codes.fan_byte; + message2[2] = 0x00; + message2[3] = second_packet_codes.suffix.byte3; + message2[4] = 0x00; + message2[5] = RAS_2819T_HEAT_SUFFIX; + break; + + default: + message2[1] = second_packet_codes.fan_byte; + message2[2] = 0x00; + message2[3] = second_packet_codes.suffix.byte3; + message2[4] = second_packet_codes.suffix.byte4; + message2[5] = second_packet_codes.suffix.byte5; + break; + } + } + + // Log final messages being transmitted + + // Transmit using proper Toshiba protocol timing + auto transmit = this->transmitter_->transmit(); + auto *data = transmit.get_data(); + + // Use existing Toshiba encode function for proper timing + this->encode_(data, message1, RAS_2819T_MESSAGE_LENGTH, 1); + + if (this->mode != climate::CLIMATE_MODE_OFF) { + // Send second packet with gap + this->encode_(data, message2, RAS_2819T_MESSAGE_LENGTH, 0); + } + + transmit.perform(); + + // Update all state tracking after successful transmission + this->last_swing_mode_ = this->swing_mode; + this->last_mode_ = this->mode; + this->last_fan_mode_ = this->fan_mode; + this->last_target_temperature_ = this->target_temperature; +} + uint8_t ToshibaClimate::is_valid_rac_pt1411hwru_header_(const uint8_t *message) { const std::vector header{RAC_PT1411HWRU_MESSAGE_HEADER0, RAC_PT1411HWRU_CS_HEADER, RAC_PT1411HWRU_SWING_HEADER}; @@ -464,11 +967,11 @@ bool ToshibaClimate::compare_rac_pt1411hwru_packets_(const uint8_t *message1, co bool ToshibaClimate::is_valid_rac_pt1411hwru_message_(const uint8_t *message) { uint8_t checksum = 0; - switch (is_valid_rac_pt1411hwru_header_(message)) { + switch (this->is_valid_rac_pt1411hwru_header_(message)) { case RAC_PT1411HWRU_MESSAGE_HEADER0: case RAC_PT1411HWRU_CS_HEADER: case RAC_PT1411HWRU_SWING_HEADER: - if (is_valid_rac_pt1411hwru_header_(message) && (message[2] == static_cast(~message[3])) && + if (this->is_valid_rac_pt1411hwru_header_(message) && (message[2] == static_cast(~message[3])) && (message[4] == static_cast(~message[5]))) { return true; } @@ -490,7 +993,103 @@ bool ToshibaClimate::is_valid_rac_pt1411hwru_message_(const uint8_t *message) { return false; } +bool ToshibaClimate::process_ras_2819t_command_(const remote_base::ToshibaAcData &toshiba_data) { + // Check for power-off command (single packet) + if (toshiba_data.rc_code_2 == 0 && toshiba_data.rc_code_1 == RAS_2819T_POWER_OFF_COMMAND) { + this->mode = climate::CLIMATE_MODE_OFF; + ESP_LOGI(TAG, "Mode: OFF"); + this->publish_state(); + return true; + } + + // Check for swing toggle command (single packet) + if (toshiba_data.rc_code_2 == 0 && toshiba_data.rc_code_1 == RAS_2819T_SWING_TOGGLE) { + // Toggle swing mode + if (this->swing_mode == climate::CLIMATE_SWING_VERTICAL) { + this->swing_mode = climate::CLIMATE_SWING_OFF; + ESP_LOGI(TAG, "Swing: OFF"); + } else { + this->swing_mode = climate::CLIMATE_SWING_VERTICAL; + ESP_LOGI(TAG, "Swing: VERTICAL"); + } + this->publish_state(); + return true; + } + + // Handle regular two-packet commands (mode/temperature/fan changes) + if (toshiba_data.rc_code_2 != 0) { + // Convert to byte array for easier processing + uint8_t message1[6], message2[6]; + for (uint8_t i = 0; i < 6; i++) { + message1[i] = (toshiba_data.rc_code_1 >> (40 - i * 8)) & 0xFF; + message2[i] = (toshiba_data.rc_code_2 >> (40 - i * 8)) & 0xFF; + } + + // Decode the protocol using message1 (rc_code_1) + uint8_t temp_code = message1[4]; + + // Decode mode - check bytes 2-3 pattern and temperature code + if ((message1[2] == 0x7B) && (message1[3] == 0x84)) { + // OFF mode has specific pattern + this->mode = climate::CLIMATE_MODE_OFF; + ESP_LOGI(TAG, "Mode: OFF"); + } else if ((message1[2] == 0x1F) && (message1[3] == 0xE0)) { + // 0x1FE0 pattern is used for AUTO, DRY, and low-temp COOL + if ((temp_code & 0x0F) == 0x08) { + this->mode = climate::CLIMATE_MODE_HEAT_COOL; + ESP_LOGI(TAG, "Mode: AUTO"); + } else if ((temp_code & 0x0F) == 0x04) { + this->mode = climate::CLIMATE_MODE_DRY; + ESP_LOGI(TAG, "Mode: DRY"); + } else { + this->mode = climate::CLIMATE_MODE_COOL; + ESP_LOGI(TAG, "Mode: COOL (low temp)"); + } + } else { + // Variable fan speed patterns - decode by temperature code + if ((temp_code & 0x0F) == 0x0C) { + this->mode = climate::CLIMATE_MODE_HEAT; + ESP_LOGI(TAG, "Mode: HEAT"); + } else if (message1[5] == 0x1B) { + this->mode = climate::CLIMATE_MODE_FAN_ONLY; + ESP_LOGI(TAG, "Mode: FAN_ONLY"); + } else { + this->mode = climate::CLIMATE_MODE_COOL; + ESP_LOGI(TAG, "Mode: COOL"); + } + } + + // Decode fan speed from rc_code_1 + uint16_t fan_code = (message1[2] << 8) | message1[3]; + this->fan_mode = decode_ras_2819t_fan_mode(fan_code); + + // Decode temperature + if (this->mode != climate::CLIMATE_MODE_OFF && this->mode != climate::CLIMATE_MODE_FAN_ONLY) { + this->target_temperature = decode_ras_2819t_temperature(temp_code); + } + + this->publish_state(); + return true; + } else { + ESP_LOGD(TAG, "Unknown single-packet RAS-2819T command: 0x%" PRIX64, toshiba_data.rc_code_1); + return false; + } +} + bool ToshibaClimate::on_receive(remote_base::RemoteReceiveData data) { + // Try modern ToshibaAcProtocol decoder first (handles RAS-2819T and potentially others) + remote_base::ToshibaAcProtocol toshiba_protocol; + auto decode_result = toshiba_protocol.decode(data); + + if (decode_result.has_value()) { + auto toshiba_data = decode_result.value(); + // Validate and process RAS-2819T commands + if (is_valid_ras_2819t_command(toshiba_data.rc_code_1, toshiba_data.rc_code_2)) { + return this->process_ras_2819t_command_(toshiba_data); + } + } + + // Fall back to generic processing for older protocols uint8_t message[18] = {0}; uint8_t message_length = TOSHIBA_HEADER_LENGTH, temperature_code = 0; @@ -499,11 +1098,11 @@ bool ToshibaClimate::on_receive(remote_base::RemoteReceiveData data) { return false; } // Read incoming bits into buffer - if (!decode_(&data, message, message_length)) { + if (!this->decode_(&data, message, message_length)) { return false; } // Determine incoming message protocol version and/or length - if (is_valid_rac_pt1411hwru_header_(message)) { + if (this->is_valid_rac_pt1411hwru_header_(message)) { // We already received four bytes message_length = RAC_PT1411HWRU_MESSAGE_LENGTH - 4; } else if ((message[0] ^ message[1] ^ message[2]) != message[3]) { @@ -514,11 +1113,11 @@ bool ToshibaClimate::on_receive(remote_base::RemoteReceiveData data) { message_length = message[2] + 2; } // Decode the remaining bytes - if (!decode_(&data, &message[4], message_length)) { + if (!this->decode_(&data, &message[4], message_length)) { return false; } // If this is a RAC-PT1411HWRU message, we expect the first packet a second time and also possibly a third packet - if (is_valid_rac_pt1411hwru_header_(message)) { + if (this->is_valid_rac_pt1411hwru_header_(message)) { // There is always a space between packets if (!data.expect_item(TOSHIBA_BIT_MARK, TOSHIBA_GAP_SPACE)) { return false; @@ -527,7 +1126,7 @@ bool ToshibaClimate::on_receive(remote_base::RemoteReceiveData data) { if (!data.expect_item(TOSHIBA_HEADER_MARK, TOSHIBA_HEADER_SPACE)) { return false; } - if (!decode_(&data, &message[6], RAC_PT1411HWRU_MESSAGE_LENGTH)) { + if (!this->decode_(&data, &message[6], RAC_PT1411HWRU_MESSAGE_LENGTH)) { return false; } // If this is a RAC-PT1411HWRU message, there may also be a third packet. @@ -535,25 +1134,25 @@ bool ToshibaClimate::on_receive(remote_base::RemoteReceiveData data) { if (data.expect_item(TOSHIBA_BIT_MARK, TOSHIBA_GAP_SPACE)) { // Validate header 3 data.expect_item(TOSHIBA_HEADER_MARK, TOSHIBA_HEADER_SPACE); - if (decode_(&data, &message[12], RAC_PT1411HWRU_MESSAGE_LENGTH)) { - if (!is_valid_rac_pt1411hwru_message_(&message[12])) { + if (this->decode_(&data, &message[12], RAC_PT1411HWRU_MESSAGE_LENGTH)) { + if (!this->is_valid_rac_pt1411hwru_message_(&message[12])) { // If a third packet was received but the checksum is not valid, fail return false; } } } - if (!compare_rac_pt1411hwru_packets_(&message[0], &message[6])) { + if (!this->compare_rac_pt1411hwru_packets_(&message[0], &message[6])) { // If the first two packets don't match each other, fail return false; } - if (!is_valid_rac_pt1411hwru_message_(&message[0])) { + if (!this->is_valid_rac_pt1411hwru_message_(&message[0])) { // If the first packet isn't valid, fail return false; } } // Header has been verified, now determine protocol version and set the climate component properties - switch (is_valid_rac_pt1411hwru_header_(message)) { + switch (this->is_valid_rac_pt1411hwru_header_(message)) { // Power, temperature, mode, fan speed case RAC_PT1411HWRU_MESSAGE_HEADER0: // Get the mode @@ -608,7 +1207,7 @@ bool ToshibaClimate::on_receive(remote_base::RemoteReceiveData data) { break; } // Get the target temperature - if (is_valid_rac_pt1411hwru_message_(&message[12])) { + if (this->is_valid_rac_pt1411hwru_message_(&message[12])) { temperature_code = (message[4] >> 4) | (message[14] & RAC_PT1411HWRU_FLAG_FRAC) | (message[15] & RAC_PT1411HWRU_FLAG_NEG); if (message[15] & RAC_PT1411HWRU_FLAG_FAH) { diff --git a/esphome/components/toshiba/toshiba.h b/esphome/components/toshiba/toshiba.h index 83e85c34db..d76833f406 100644 --- a/esphome/components/toshiba/toshiba.h +++ b/esphome/components/toshiba/toshiba.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/components/climate_ir/climate_ir.h" +#include "esphome/components/remote_base/toshiba_ac_protocol.h" namespace esphome { namespace toshiba { @@ -10,6 +11,7 @@ enum Model { MODEL_GENERIC = 0, // Temperature range is from 17 to 30 MODEL_RAC_PT1411HWRU_C = 1, // Temperature range is from 16 to 30 MODEL_RAC_PT1411HWRU_F = 2, // Temperature range is from 16 to 30 + MODEL_RAS_2819T = 3, // RAS-2819T protocol variant, temperature range 18 to 30 }; // Supported temperature ranges @@ -19,6 +21,8 @@ const float TOSHIBA_RAC_PT1411HWRU_TEMP_C_MIN = 16.0; const float TOSHIBA_RAC_PT1411HWRU_TEMP_C_MAX = 30.0; const float TOSHIBA_RAC_PT1411HWRU_TEMP_F_MIN = 60.0; const float TOSHIBA_RAC_PT1411HWRU_TEMP_F_MAX = 86.0; +const float TOSHIBA_RAS_2819T_TEMP_C_MIN = 18.0; +const float TOSHIBA_RAS_2819T_TEMP_C_MAX = 30.0; class ToshibaClimate : public climate_ir::ClimateIR { public: @@ -35,6 +39,9 @@ class ToshibaClimate : public climate_ir::ClimateIR { void transmit_generic_(); void transmit_rac_pt1411hwru_(); void transmit_rac_pt1411hwru_temp_(bool cs_state = true, bool cs_send_update = true); + void transmit_ras_2819t_(); + // Process RAS-2819T IR command data + bool process_ras_2819t_command_(const remote_base::ToshibaAcData &toshiba_data); // Returns the header if valid, else returns zero uint8_t is_valid_rac_pt1411hwru_header_(const uint8_t *message); // Returns true if message is a valid RAC-PT1411HWRU IR message, regardless if first or second packet @@ -43,11 +50,26 @@ class ToshibaClimate : public climate_ir::ClimateIR { bool compare_rac_pt1411hwru_packets_(const uint8_t *message1, const uint8_t *message2); bool on_receive(remote_base::RemoteReceiveData data) override; + private: + // RAS-2819T state tracking for swing mode optimization + climate::ClimateSwingMode last_swing_mode_{climate::CLIMATE_SWING_OFF}; + climate::ClimateMode last_mode_{climate::CLIMATE_MODE_OFF}; + optional last_fan_mode_{}; + float last_target_temperature_{24.0f}; + float temperature_min_() { - return (this->model_ == MODEL_GENERIC) ? TOSHIBA_GENERIC_TEMP_C_MIN : TOSHIBA_RAC_PT1411HWRU_TEMP_C_MIN; + if (this->model_ == MODEL_RAC_PT1411HWRU_C || this->model_ == MODEL_RAC_PT1411HWRU_F) + return TOSHIBA_RAC_PT1411HWRU_TEMP_C_MIN; + if (this->model_ == MODEL_RAS_2819T) + return TOSHIBA_RAS_2819T_TEMP_C_MIN; + return TOSHIBA_GENERIC_TEMP_C_MIN; // Default to GENERIC for unknown models } float temperature_max_() { - return (this->model_ == MODEL_GENERIC) ? TOSHIBA_GENERIC_TEMP_C_MAX : TOSHIBA_RAC_PT1411HWRU_TEMP_C_MAX; + if (this->model_ == MODEL_RAC_PT1411HWRU_C || this->model_ == MODEL_RAC_PT1411HWRU_F) + return TOSHIBA_RAC_PT1411HWRU_TEMP_C_MAX; + if (this->model_ == MODEL_RAS_2819T) + return TOSHIBA_RAS_2819T_TEMP_C_MAX; + return TOSHIBA_GENERIC_TEMP_C_MAX; // Default to GENERIC for unknown models } std::set toshiba_swing_modes_() { return (this->model_ == MODEL_GENERIC) diff --git a/tests/components/toshiba/common_ras2819t.yaml b/tests/components/toshiba/common_ras2819t.yaml new file mode 100644 index 0000000000..32081fca98 --- /dev/null +++ b/tests/components/toshiba/common_ras2819t.yaml @@ -0,0 +1,13 @@ +remote_transmitter: + pin: ${tx_pin} + carrier_duty_percent: 50% + +remote_receiver: + id: rcvr + pin: ${rx_pin} + +climate: + - platform: toshiba + name: "RAS-2819T Climate" + model: RAS-2819T + receiver_id: rcvr diff --git a/tests/components/toshiba/test_ras2819t.esp32-ard.yaml b/tests/components/toshiba/test_ras2819t.esp32-ard.yaml new file mode 100644 index 0000000000..00805baa01 --- /dev/null +++ b/tests/components/toshiba/test_ras2819t.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO5 + rx_pin: GPIO4 + +<<: !include common_ras2819t.yaml diff --git a/tests/components/toshiba/test_ras2819t.esp32-c3-ard.yaml b/tests/components/toshiba/test_ras2819t.esp32-c3-ard.yaml new file mode 100644 index 0000000000..00805baa01 --- /dev/null +++ b/tests/components/toshiba/test_ras2819t.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO5 + rx_pin: GPIO4 + +<<: !include common_ras2819t.yaml diff --git a/tests/components/toshiba/test_ras2819t.esp32-c3-idf.yaml b/tests/components/toshiba/test_ras2819t.esp32-c3-idf.yaml new file mode 100644 index 0000000000..00805baa01 --- /dev/null +++ b/tests/components/toshiba/test_ras2819t.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO5 + rx_pin: GPIO4 + +<<: !include common_ras2819t.yaml diff --git a/tests/components/toshiba/test_ras2819t.esp32-idf.yaml b/tests/components/toshiba/test_ras2819t.esp32-idf.yaml new file mode 100644 index 0000000000..00805baa01 --- /dev/null +++ b/tests/components/toshiba/test_ras2819t.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO5 + rx_pin: GPIO4 + +<<: !include common_ras2819t.yaml diff --git a/tests/components/toshiba/test_ras2819t.esp8266-ard.yaml b/tests/components/toshiba/test_ras2819t.esp8266-ard.yaml new file mode 100644 index 0000000000..00805baa01 --- /dev/null +++ b/tests/components/toshiba/test_ras2819t.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO5 + rx_pin: GPIO4 + +<<: !include common_ras2819t.yaml From 2b832e9ee8df636076898750162c1259ead4ae5d Mon Sep 17 00:00:00 2001 From: mrtoy-me <118446898+mrtoy-me@users.noreply.github.com> Date: Fri, 17 Oct 2025 22:55:07 +1000 Subject: [PATCH 113/526] [cap1188] remove delays in setup (#11317) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- esphome/components/cap1188/cap1188.cpp | 34 +++++++++++++++++++------- esphome/components/cap1188/cap1188.h | 2 ++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/esphome/components/cap1188/cap1188.cpp b/esphome/components/cap1188/cap1188.cpp index 584ff896c5..683e5cf487 100644 --- a/esphome/components/cap1188/cap1188.cpp +++ b/esphome/components/cap1188/cap1188.cpp @@ -8,17 +8,30 @@ namespace cap1188 { static const char *const TAG = "cap1188"; void CAP1188Component::setup() { - // Reset device using the reset pin - if (this->reset_pin_ != nullptr) { - this->reset_pin_->setup(); - this->reset_pin_->digital_write(false); - delay(100); // NOLINT - this->reset_pin_->digital_write(true); - delay(100); // NOLINT - this->reset_pin_->digital_write(false); - delay(100); // NOLINT + this->disable_loop(); + + // no reset pin + if (this->reset_pin_ == nullptr) { + this->finish_setup_(); + return; } + // reset pin configured so reset before finishing setup + this->reset_pin_->setup(); + this->reset_pin_->digital_write(false); + // delay after reset pin write + this->set_timeout(100, [this]() { + this->reset_pin_->digital_write(true); + // delay after reset pin write + this->set_timeout(100, [this]() { + this->reset_pin_->digital_write(false); + // delay after reset pin write + this->set_timeout(100, [this]() { this->finish_setup_(); }); + }); + }); +} + +void CAP1188Component::finish_setup_() { // Check if CAP1188 is actually connected this->read_byte(CAP1188_PRODUCT_ID, &this->cap1188_product_id_); this->read_byte(CAP1188_MANUFACTURE_ID, &this->cap1188_manufacture_id_); @@ -44,6 +57,9 @@ void CAP1188Component::setup() { // Speed up a bit this->write_byte(CAP1188_STAND_BY_CONFIGURATION, 0x30); + + // Setup successful, so enable loop + this->enable_loop(); } void CAP1188Component::dump_config() { diff --git a/esphome/components/cap1188/cap1188.h b/esphome/components/cap1188/cap1188.h index baefd1c48f..297c601b05 100644 --- a/esphome/components/cap1188/cap1188.h +++ b/esphome/components/cap1188/cap1188.h @@ -49,6 +49,8 @@ class CAP1188Component : public Component, public i2c::I2CDevice { void loop() override; protected: + void finish_setup_(); + std::vector channels_{}; uint8_t touch_threshold_{0x20}; uint8_t allow_multiple_touches_{0x80}; From fe9db75c27a663bbbee5202381386ae81395a36b Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Fri, 17 Oct 2025 15:02:37 +0200 Subject: [PATCH 114/526] [nrf52] add xiao_ble board (#10698) Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/debug/debug_zephyr.cpp | 52 ++++++++++++++++++- esphome/components/nrf52/__init__.py | 13 +++++ esphome/components/nrf52/boards.py | 10 +++- esphome/components/zephyr/__init__.py | 27 ++++++---- .../components/nrf52/test.nrf52-xiao-ble.yaml | 7 +++ .../build_components_base.nrf52-xiao-ble.yaml | 15 ++++++ 6 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 tests/components/nrf52/test.nrf52-xiao-ble.yaml create mode 100644 tests/test_build_components/build_components_base.nrf52-xiao-ble.yaml diff --git a/esphome/components/debug/debug_zephyr.cpp b/esphome/components/debug/debug_zephyr.cpp index 9a361b158f..231b39a711 100644 --- a/esphome/components/debug/debug_zephyr.cpp +++ b/esphome/components/debug/debug_zephyr.cpp @@ -25,10 +25,37 @@ static void show_reset_reason(std::string &reset_reason, bool set, const char *r reset_reason += reason; } -inline uint32_t read_mem_u32(uintptr_t addr) { +static inline uint32_t read_mem_u32(uintptr_t addr) { return *reinterpret_cast(addr); // NOLINT(performance-no-int-to-ptr) } +static inline uint8_t read_mem_u8(uintptr_t addr) { + return *reinterpret_cast(addr); // NOLINT(performance-no-int-to-ptr) +} + +// defines from https://github.com/adafruit/Adafruit_nRF52_Bootloader which prints those information +constexpr uint32_t SD_MAGIC_NUMBER = 0x51B1E5DB; +constexpr uintptr_t MBR_SIZE = 0x1000; +constexpr uintptr_t SOFTDEVICE_INFO_STRUCT_OFFSET = 0x2000; +constexpr uintptr_t SD_ID_OFFSET = SOFTDEVICE_INFO_STRUCT_OFFSET + 0x10; +constexpr uintptr_t SD_VERSION_OFFSET = SOFTDEVICE_INFO_STRUCT_OFFSET + 0x14; + +static inline bool is_sd_present() { + return read_mem_u32(SOFTDEVICE_INFO_STRUCT_OFFSET + MBR_SIZE + 4) == SD_MAGIC_NUMBER; +} +static inline uint32_t sd_id_get() { + if (read_mem_u8(MBR_SIZE + SOFTDEVICE_INFO_STRUCT_OFFSET) > (SD_ID_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) { + return read_mem_u32(MBR_SIZE + SD_ID_OFFSET); + } + return 0; +} +static inline uint32_t sd_version_get() { + if (read_mem_u8(MBR_SIZE + SOFTDEVICE_INFO_STRUCT_OFFSET) > (SD_VERSION_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) { + return read_mem_u32(MBR_SIZE + SD_VERSION_OFFSET); + } + return 0; +} + std::string DebugComponent::get_reset_reason_() { uint32_t cause; auto ret = hwinfo_get_reset_cause(&cause); @@ -271,6 +298,29 @@ void DebugComponent::get_device_info_(std::string &device_info) { NRF_UICR->NRFFW[0]); ESP_LOGD(TAG, "MBR param page addr 0x%08x, UICR param page addr 0x%08x", read_mem_u32(MBR_PARAM_PAGE_ADDR), NRF_UICR->NRFFW[1]); + if (is_sd_present()) { + uint32_t const sd_id = sd_id_get(); + uint32_t const sd_version = sd_version_get(); + + uint32_t ver[3]; + ver[0] = sd_version / 1000000; + ver[1] = (sd_version - ver[0] * 1000000) / 1000; + ver[2] = (sd_version - ver[0] * 1000000 - ver[1] * 1000); + + ESP_LOGD(TAG, "SoftDevice: S%u %u.%u.%u", sd_id, ver[0], ver[1], ver[2]); +#ifdef USE_SOFTDEVICE_ID +#ifdef USE_SOFTDEVICE_VERSION + if (USE_SOFTDEVICE_ID != sd_id || USE_SOFTDEVICE_VERSION != ver[0]) { + ESP_LOGE(TAG, "Built for SoftDevice S%u %u.x.y. It may crash due to mismatch of bootloader version.", + USE_SOFTDEVICE_ID, USE_SOFTDEVICE_VERSION); + } +#else + if (USE_SOFTDEVICE_ID != sd_id) { + ESP_LOGE(TAG, "Built for SoftDevice S%u. It may crash due to mismatch of bootloader version.", USE_SOFTDEVICE_ID); + } +#endif +#endif + } #endif } diff --git a/esphome/components/nrf52/__init__.py b/esphome/components/nrf52/__init__.py index 84e505a90a..727607933d 100644 --- a/esphome/components/nrf52/__init__.py +++ b/esphome/components/nrf52/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging from pathlib import Path from esphome import pins @@ -48,6 +49,7 @@ from .gpio import nrf52_pin_to_code # noqa CODEOWNERS = ["@tomaszduda23"] AUTO_LOAD = ["zephyr"] IS_TARGET_PLATFORM = True +_LOGGER = logging.getLogger(__name__) def set_core_data(config: ConfigType) -> ConfigType: @@ -127,6 +129,10 @@ def _validate_mcumgr(config): def _final_validate(config): if CONF_DFU in config: _validate_mcumgr(config) + if config[KEY_BOOTLOADER] == BOOTLOADER_ADAFRUIT: + _LOGGER.warning( + "Selected generic Adafruit bootloader. The board might crash. Consider settings `bootloader:`" + ) FINAL_VALIDATE_SCHEMA = _final_validate @@ -157,6 +163,13 @@ async def to_code(config: ConfigType) -> None: if config[KEY_BOOTLOADER] == BOOTLOADER_MCUBOOT: cg.add_define("USE_BOOTLOADER_MCUBOOT") else: + if "_sd" in config[KEY_BOOTLOADER]: + bootloader = config[KEY_BOOTLOADER].split("_") + sd_id = bootloader[2][2:] + cg.add_define("USE_SOFTDEVICE_ID", int(sd_id)) + if (len(bootloader)) > 3: + sd_version = bootloader[3][1:] + cg.add_define("USE_SOFTDEVICE_VERSION", int(sd_version)) # make sure that firmware.zip is created # for Adafruit_nRF52_Bootloader cg.add_platformio_option("board_upload.protocol", "nrfutil") diff --git a/esphome/components/nrf52/boards.py b/esphome/components/nrf52/boards.py index 8e5fb2a23d..6064fe844a 100644 --- a/esphome/components/nrf52/boards.py +++ b/esphome/components/nrf52/boards.py @@ -11,10 +11,18 @@ from .const import ( BOARDS_ZEPHYR = { "adafruit_itsybitsy_nrf52840": { KEY_BOOTLOADER: [ + BOOTLOADER_ADAFRUIT_NRF52_SD140_V6, + BOOTLOADER_ADAFRUIT, + BOOTLOADER_ADAFRUIT_NRF52_SD132, + BOOTLOADER_ADAFRUIT_NRF52_SD140_V7, + ] + }, + "xiao_ble": { + KEY_BOOTLOADER: [ + BOOTLOADER_ADAFRUIT_NRF52_SD140_V7, BOOTLOADER_ADAFRUIT, BOOTLOADER_ADAFRUIT_NRF52_SD132, BOOTLOADER_ADAFRUIT_NRF52_SD140_V6, - BOOTLOADER_ADAFRUIT_NRF52_SD140_V7, ] }, } diff --git a/esphome/components/zephyr/__init__.py b/esphome/components/zephyr/__init__.py index ff4644163e..634c99876b 100644 --- a/esphome/components/zephyr/__init__.py +++ b/esphome/components/zephyr/__init__.py @@ -222,18 +222,25 @@ def copy_files(): ] in ["xiao_ble"]: fake_board_manifest = """ { -"frameworks": [ - "zephyr" -], -"name": "esphome nrf52", -"upload": { - "maximum_ram_size": 248832, - "maximum_size": 815104 -}, -"url": "https://esphome.io/", -"vendor": "esphome" + "frameworks": [ + "zephyr" + ], + "name": "esphome nrf52", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200 + }, + "url": "https://esphome.io/", + "vendor": "esphome", + "build": { + "softdevice": { + "sd_fwid": "0x00B6" + } + } } """ + write_file_if_changed( CORE.relative_build_path(f"boards/{zephyr_data()[KEY_BOARD]}.json"), fake_board_manifest, diff --git a/tests/components/nrf52/test.nrf52-xiao-ble.yaml b/tests/components/nrf52/test.nrf52-xiao-ble.yaml new file mode 100644 index 0000000000..3fe80209b6 --- /dev/null +++ b/tests/components/nrf52/test.nrf52-xiao-ble.yaml @@ -0,0 +1,7 @@ +nrf52: + dfu: + reset_pin: + number: 14 + inverted: true + mode: + output: true diff --git a/tests/test_build_components/build_components_base.nrf52-xiao-ble.yaml b/tests/test_build_components/build_components_base.nrf52-xiao-ble.yaml new file mode 100644 index 0000000000..2f3f91d957 --- /dev/null +++ b/tests/test_build_components/build_components_base.nrf52-xiao-ble.yaml @@ -0,0 +1,15 @@ +esphome: + name: componenttestnrf52 + friendly_name: $component_name + +nrf52: + board: xiao_ble + +logger: + level: VERY_VERBOSE + +packages: + component_under_test: !include + file: $component_test_file + vars: + component_test_file: $component_test_file From 6d09e68b2e9d7c13940962427f69128d62b92471 Mon Sep 17 00:00:00 2001 From: B48D81EFCC <111175947+B48D81EFCC@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:11:51 +0200 Subject: [PATCH 115/526] [bh1900nux] Add bh1900nux temperature Sensor (#8631) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Andreas Riehl Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/bh1900nux/__init__.py | 0 esphome/components/bh1900nux/bh1900nux.cpp | 54 +++++++++++++++++++ esphome/components/bh1900nux/bh1900nux.h | 18 +++++++ esphome/components/bh1900nux/sensor.py | 34 ++++++++++++ tests/components/bh1900nux/common.yaml | 6 +++ .../bh1900nux/test.esp32-c3-idf.yaml | 4 ++ .../components/bh1900nux/test.esp32-idf.yaml | 4 ++ .../bh1900nux/test.esp8266-ard.yaml | 4 ++ .../components/bh1900nux/test.rp2040-ard.yaml | 4 ++ 10 files changed, 129 insertions(+) create mode 100644 esphome/components/bh1900nux/__init__.py create mode 100644 esphome/components/bh1900nux/bh1900nux.cpp create mode 100644 esphome/components/bh1900nux/bh1900nux.h create mode 100644 esphome/components/bh1900nux/sensor.py create mode 100644 tests/components/bh1900nux/common.yaml create mode 100644 tests/components/bh1900nux/test.esp32-c3-idf.yaml create mode 100644 tests/components/bh1900nux/test.esp32-idf.yaml create mode 100644 tests/components/bh1900nux/test.esp8266-ard.yaml create mode 100644 tests/components/bh1900nux/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 03ea5d0e47..b5cefa1e0c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -62,6 +62,7 @@ esphome/components/bedjet/fan/* @jhansche esphome/components/bedjet/sensor/* @javawizard @jhansche esphome/components/beken_spi_led_strip/* @Mat931 esphome/components/bh1750/* @OttoWinter +esphome/components/bh1900nux/* @B48D81EFCC esphome/components/binary_sensor/* @esphome/core esphome/components/bk72xx/* @kuba2k2 esphome/components/bl0906/* @athom-tech @jesserockz @tarontop diff --git a/esphome/components/bh1900nux/__init__.py b/esphome/components/bh1900nux/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/bh1900nux/bh1900nux.cpp b/esphome/components/bh1900nux/bh1900nux.cpp new file mode 100644 index 0000000000..96a06adaa0 --- /dev/null +++ b/esphome/components/bh1900nux/bh1900nux.cpp @@ -0,0 +1,54 @@ +#include "esphome/core/log.h" +#include "bh1900nux.h" + +namespace esphome { +namespace bh1900nux { + +static const char *const TAG = "bh1900nux.sensor"; + +// I2C Registers +static const uint8_t TEMPERATURE_REG = 0x00; +static const uint8_t CONFIG_REG = 0x01; // Not used and supported yet +static const uint8_t TEMPERATURE_LOW_REG = 0x02; // Not used and supported yet +static const uint8_t TEMPERATURE_HIGH_REG = 0x03; // Not used and supported yet +static const uint8_t SOFT_RESET_REG = 0x04; + +// I2C Command payloads +static const uint8_t SOFT_RESET_PAYLOAD = 0x01; // Soft Reset value + +static const float SENSOR_RESOLUTION = 0.0625f; // Sensor resolution per bit in degrees celsius + +void BH1900NUXSensor::setup() { + // Initialize I2C device + i2c::ErrorCode result_code = + this->write_register(SOFT_RESET_REG, &SOFT_RESET_PAYLOAD, 1); // Software Reset to check communication + if (result_code != i2c::ERROR_OK) { + this->mark_failed(ESP_LOG_MSG_COMM_FAIL); + return; + } +} + +void BH1900NUXSensor::update() { + uint8_t temperature_raw[2]; + if (this->read_register(TEMPERATURE_REG, temperature_raw, 2) != i2c::ERROR_OK) { + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); + return; + } + + // Combined raw value, unsigned and unaligned 16 bit + // Temperature is represented in just 12 bits, shift needed + int16_t raw_temperature_register_value = encode_uint16(temperature_raw[0], temperature_raw[1]); + raw_temperature_register_value >>= 4; + float temperature_value = raw_temperature_register_value * SENSOR_RESOLUTION; // Apply sensor resolution + + this->publish_state(temperature_value); +} + +void BH1900NUXSensor::dump_config() { + LOG_SENSOR("", "BH1900NUX", this); + LOG_I2C_DEVICE(this); + LOG_UPDATE_INTERVAL(this); +} + +} // namespace bh1900nux +} // namespace esphome diff --git a/esphome/components/bh1900nux/bh1900nux.h b/esphome/components/bh1900nux/bh1900nux.h new file mode 100644 index 0000000000..fd7f8848d6 --- /dev/null +++ b/esphome/components/bh1900nux/bh1900nux.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace bh1900nux { + +class BH1900NUXSensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void update() override; + void dump_config() override; +}; + +} // namespace bh1900nux +} // namespace esphome diff --git a/esphome/components/bh1900nux/sensor.py b/esphome/components/bh1900nux/sensor.py new file mode 100644 index 0000000000..5e1c0395af --- /dev/null +++ b/esphome/components/bh1900nux/sensor.py @@ -0,0 +1,34 @@ +import esphome.codegen as cg +from esphome.components import i2c, sensor +import esphome.config_validation as cv +from esphome.const import ( + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, +) + +DEPENDENCIES = ["i2c"] +CODEOWNERS = ["@B48D81EFCC"] + +sensor_ns = cg.esphome_ns.namespace("bh1900nux") +BH1900NUXSensor = sensor_ns.class_( + "BH1900NUXSensor", cg.PollingComponent, i2c.I2CDevice +) + +CONFIG_SCHEMA = ( + sensor.sensor_schema( + BH1900NUXSensor, + accuracy_decimals=1, + unit_of_measurement=UNIT_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x48)) +) + + +async def to_code(config): + var = await sensor.new_sensor(config) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) diff --git a/tests/components/bh1900nux/common.yaml b/tests/components/bh1900nux/common.yaml new file mode 100644 index 0000000000..3438418702 --- /dev/null +++ b/tests/components/bh1900nux/common.yaml @@ -0,0 +1,6 @@ +sensor: + - platform: bh1900nux + i2c_id: i2c_bus + name: Temperature Living Room + address: 0x48 + update_interval: 30s diff --git a/tests/components/bh1900nux/test.esp32-c3-idf.yaml b/tests/components/bh1900nux/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..9990d96d29 --- /dev/null +++ b/tests/components/bh1900nux/test.esp32-c3-idf.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/bh1900nux/test.esp32-idf.yaml b/tests/components/bh1900nux/test.esp32-idf.yaml new file mode 100644 index 0000000000..b47e39c389 --- /dev/null +++ b/tests/components/bh1900nux/test.esp32-idf.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/bh1900nux/test.esp8266-ard.yaml b/tests/components/bh1900nux/test.esp8266-ard.yaml new file mode 100644 index 0000000000..4a98b9388a --- /dev/null +++ b/tests/components/bh1900nux/test.esp8266-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + +<<: !include common.yaml diff --git a/tests/components/bh1900nux/test.rp2040-ard.yaml b/tests/components/bh1900nux/test.rp2040-ard.yaml new file mode 100644 index 0000000000..319a7c71a6 --- /dev/null +++ b/tests/components/bh1900nux/test.rp2040-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + +<<: !include common.yaml From f5774cc1384fd3ec79b750b49e9a0e9b34a36cac Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 17 Oct 2025 08:20:31 -1000 Subject: [PATCH 116/526] [debug] Replace std::map with struct array for ESP32 chip features (#11307) --- esphome/components/debug/debug_esp32.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index b1dfe1bc9a..1c3dc3699b 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -11,8 +11,6 @@ #include #include -#include - #ifdef USE_ARDUINO #include #endif @@ -125,7 +123,12 @@ void DebugComponent::log_partition_info_() { uint32_t DebugComponent::get_free_heap_() { return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); } -static const std::map CHIP_FEATURES = { +struct ChipFeature { + int bit; + const char *name; +}; + +static constexpr ChipFeature CHIP_FEATURES[] = { {CHIP_FEATURE_BLE, "BLE"}, {CHIP_FEATURE_BT, "BT"}, {CHIP_FEATURE_EMB_FLASH, "EMB Flash"}, @@ -170,11 +173,13 @@ void DebugComponent::get_device_info_(std::string &device_info) { esp_chip_info(&info); const char *model = ESPHOME_VARIANT; std::string features; - for (auto feature : CHIP_FEATURES) { - if (info.features & feature.first) { - features += feature.second; + + // Check each known feature bit + for (const auto &feature : CHIP_FEATURES) { + if (info.features & feature.bit) { + features += feature.name; features += ", "; - info.features &= ~feature.first; + info.features &= ~feature.bit; } } if (info.features != 0) From ba6c8c87c236d77c5d216a9919d6c1bf7d9725fd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 17 Oct 2025 08:20:55 -1000 Subject: [PATCH 117/526] [dashboard] Fix binary download with packages using secrets after Path migration (#11313) --- esphome/dashboard/settings.py | 11 ++++- tests/dashboard/test_settings.py | 62 ++++++++++++++++++++++++++ tests/dashboard/test_web_server.py | 71 ++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 1 deletion(-) diff --git a/esphome/dashboard/settings.py b/esphome/dashboard/settings.py index 35b67c0d23..6035b4a1d6 100644 --- a/esphome/dashboard/settings.py +++ b/esphome/dashboard/settings.py @@ -10,6 +10,10 @@ from esphome.helpers import get_bool_env from .util.password import password_hash +# Sentinel file name used for CORE.config_path when dashboard initializes. +# This ensures .parent returns the config directory instead of root. +_DASHBOARD_SENTINEL_FILE = "___DASHBOARD_SENTINEL___.yaml" + class DashboardSettings: """Settings for the dashboard.""" @@ -48,7 +52,12 @@ class DashboardSettings: self.config_dir = Path(args.configuration) self.absolute_config_dir = self.config_dir.resolve() self.verbose = args.verbose - CORE.config_path = self.config_dir / "." + # Set to a sentinel file so .parent gives us the config directory. + # Previously this was `os.path.join(self.config_dir, ".")` which worked because + # os.path.dirname("/config/.") returns "/config", but Path("/config/.").parent + # normalizes to Path("/config") first, then .parent returns Path("/"), breaking + # secret resolution. Using a sentinel file ensures .parent gives the correct directory. + CORE.config_path = self.config_dir / _DASHBOARD_SENTINEL_FILE @property def relative_url(self) -> str: diff --git a/tests/dashboard/test_settings.py b/tests/dashboard/test_settings.py index c9097fe5e2..91a8ec70c3 100644 --- a/tests/dashboard/test_settings.py +++ b/tests/dashboard/test_settings.py @@ -2,11 +2,13 @@ from __future__ import annotations +from argparse import Namespace from pathlib import Path import tempfile import pytest +from esphome.core import CORE from esphome.dashboard.settings import DashboardSettings @@ -159,3 +161,63 @@ def test_rel_path_with_numeric_args(dashboard_settings: DashboardSettings) -> No result = dashboard_settings.rel_path("123", "456.789") expected = dashboard_settings.config_dir / "123" / "456.789" assert result == expected + + +def test_config_path_parent_resolves_to_config_dir(tmp_path: Path) -> None: + """Test that CORE.config_path.parent resolves to config_dir after parse_args. + + This is a regression test for issue #11280 where binary download failed + when using packages with secrets after the Path migration in 2025.10.0. + + The issue was that after switching from os.path to Path: + - Before: os.path.dirname("/config/.") → "/config" + - After: Path("/config/.").parent → Path("/") (normalized first!) + + The fix uses a sentinel file so .parent returns the correct directory: + - Fixed: Path("/config/___DASHBOARD_SENTINEL___.yaml").parent → Path("/config") + """ + # Create test directory structure with secrets and packages + config_dir = tmp_path / "config" + config_dir.mkdir() + + # Create secrets.yaml with obviously fake test values + secrets_file = config_dir / "secrets.yaml" + secrets_file.write_text( + "wifi_ssid: TEST-DUMMY-SSID\n" + "wifi_password: not-a-real-password-just-for-testing\n" + ) + + # Create package file that uses secrets + package_file = config_dir / "common.yaml" + package_file.write_text( + "wifi:\n ssid: !secret wifi_ssid\n password: !secret wifi_password\n" + ) + + # Create main device config that includes the package + device_config = config_dir / "test-device.yaml" + device_config.write_text( + "esphome:\n name: test-device\n\npackages:\n common: !include common.yaml\n" + ) + + # Set up dashboard settings with our test config directory + settings = DashboardSettings() + args = Namespace( + configuration=str(config_dir), + password=None, + username=None, + ha_addon=False, + verbose=False, + ) + settings.parse_args(args) + + # Verify that CORE.config_path.parent correctly points to the config directory + # This is critical for secret resolution in yaml_util.py which does: + # main_config_dir = CORE.config_path.parent + # main_secret_yml = main_config_dir / "secrets.yaml" + assert CORE.config_path.parent == config_dir.resolve() + assert (CORE.config_path.parent / "secrets.yaml").exists() + assert (CORE.config_path.parent / "common.yaml").exists() + + # Verify that CORE.config_path itself uses the sentinel file + assert CORE.config_path.name == "___DASHBOARD_SENTINEL___.yaml" + assert not CORE.config_path.exists() # Sentinel file doesn't actually exist diff --git a/tests/dashboard/test_web_server.py b/tests/dashboard/test_web_server.py index 5bbe7e78fc..6c424e56d4 100644 --- a/tests/dashboard/test_web_server.py +++ b/tests/dashboard/test_web_server.py @@ -1,5 +1,6 @@ from __future__ import annotations +from argparse import Namespace import asyncio from collections.abc import Generator from contextlib import asynccontextmanager @@ -17,6 +18,8 @@ from tornado.ioloop import IOLoop from tornado.testing import bind_unused_port from tornado.websocket import WebSocketClientConnection, websocket_connect +from esphome import yaml_util +from esphome.core import CORE from esphome.dashboard import web_server from esphome.dashboard.const import DashboardEvent from esphome.dashboard.core import DASHBOARD @@ -1302,3 +1305,71 @@ async def test_dashboard_subscriber_refresh_event( # Give it a moment to clean up await asyncio.sleep(0.01) + + +@pytest.mark.asyncio +async def test_dashboard_yaml_loading_with_packages_and_secrets( + tmp_path: Path, +) -> None: + """Test dashboard YAML loading with packages referencing secrets. + + This is a regression test for issue #11280 where binary download failed + when using packages with secrets after the Path migration in 2025.10.0. + + This test verifies that CORE.config_path initialization in the dashboard + allows yaml_util.load_yaml() to correctly resolve secrets from packages. + """ + # Create test directory structure with secrets and packages + config_dir = tmp_path / "config" + config_dir.mkdir() + + # Create secrets.yaml with obviously fake test values + secrets_file = config_dir / "secrets.yaml" + secrets_file.write_text( + "wifi_ssid: TEST-DUMMY-SSID\n" + "wifi_password: not-a-real-password-just-for-testing\n" + ) + + # Create package file that uses secrets + package_file = config_dir / "common.yaml" + package_file.write_text( + "wifi:\n ssid: !secret wifi_ssid\n password: !secret wifi_password\n" + ) + + # Create main device config that includes the package + device_config = config_dir / "test-download-secrets.yaml" + device_config.write_text( + "esphome:\n name: test-download-secrets\n platform: ESP32\n board: esp32dev\n\n" + "packages:\n common: !include common.yaml\n" + ) + + # Initialize DASHBOARD settings with our test config directory + # This is what sets CORE.config_path - the critical code path for the bug + args = Namespace( + configuration=str(config_dir), + password=None, + username=None, + ha_addon=False, + verbose=False, + ) + DASHBOARD.settings.parse_args(args) + + # With the fix: CORE.config_path should be config_dir / "___DASHBOARD_SENTINEL___.yaml" + # so CORE.config_path.parent would be config_dir + # Without the fix: CORE.config_path is config_dir / "." which normalizes to config_dir + # so CORE.config_path.parent would be tmp_path (the parent of config_dir) + + # The fix ensures CORE.config_path.parent points to config_dir + assert CORE.config_path.parent == config_dir.resolve(), ( + f"CORE.config_path.parent should point to config_dir. " + f"Got {CORE.config_path.parent}, expected {config_dir.resolve()}. " + f"CORE.config_path is {CORE.config_path}" + ) + + # Now load the YAML with packages that reference secrets + # This is where the bug would manifest - yaml_util.load_yaml would fail + # to find secrets.yaml because CORE.config_path.parent pointed to the wrong place + config = yaml_util.load_yaml(device_config) + # If we get here, secret resolution worked! + assert "esphome" in config + assert config["esphome"]["name"] == "test-download-secrets" From 24243fb22ccb5c02ab826a5c68162fc132cc04d5 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Fri, 17 Oct 2025 14:23:49 -0400 Subject: [PATCH 118/526] [tests] Add i2c_id to mcp47a1 & mcp4725 and remove from isolation (#11324) --- script/analyze_component_buses.py | 2 -- tests/components/mcp4725/common.yaml | 1 + tests/components/mcp47a1/common.yaml | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/analyze_component_buses.py b/script/analyze_component_buses.py index fc7d021cbe..98edfef145 100755 --- a/script/analyze_component_buses.py +++ b/script/analyze_component_buses.py @@ -83,8 +83,6 @@ ISOLATED_COMPONENTS = { "openthread": "Conflicts with wifi: used by most components", "openthread_info": "Conflicts with wifi: used by most components", "matrix_keypad": "Needs isolation due to keypad", - "mcp4725": "no YAML config to specify i2c bus id", - "mcp47a1": "no YAML config to specify i2c bus id", "modbus_controller": "Defines multiple modbus buses for testing client/server functionality - conflicts with package modbus bus", "neopixelbus": "RMT type conflict with ESP32 Arduino/ESP-IDF headers (enum vs struct rmt_channel_t)", "packages": "cannot merge packages", diff --git a/tests/components/mcp4725/common.yaml b/tests/components/mcp4725/common.yaml index 871c2805a3..9352ebfd19 100644 --- a/tests/components/mcp4725/common.yaml +++ b/tests/components/mcp4725/common.yaml @@ -1,3 +1,4 @@ output: - platform: mcp4725 id: mcp4725_dac_output + i2c_id: i2c_bus diff --git a/tests/components/mcp47a1/common.yaml b/tests/components/mcp47a1/common.yaml index 3448fcfb31..5a5786ff0f 100644 --- a/tests/components/mcp47a1/common.yaml +++ b/tests/components/mcp47a1/common.yaml @@ -1,3 +1,4 @@ output: - platform: mcp47a1 id: output_mcp47a1 + i2c_id: i2c_bus From b61cec8e77edda72d1a3f2807b622293f3bab1fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 09:14:45 -1000 Subject: [PATCH 119/526] Bump github/codeql-action from 4.30.8 to 4.30.9 (#11326) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9aa7856116..c24900d378 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 + uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -86,6 +86,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 + uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 with: category: "/language:${{matrix.language}}" From b3b65316f004b9c5ee783b0a8b140f3f955e54c3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 17 Oct 2025 13:05:44 -1000 Subject: [PATCH 120/526] [ci] Fix test_build_components missing test files with hyphen naming pattern (#11329) --- script/test_build_components.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/test_build_components.py b/script/test_build_components.py index c98d425447..df092c091d 100755 --- a/script/test_build_components.py +++ b/script/test_build_components.py @@ -99,7 +99,8 @@ def find_component_tests( if not comp_dir.is_dir(): continue - for test_file in comp_dir.glob("test.*.yaml"): + # Find test files matching test.*.yaml or test-*.yaml patterns + for test_file in comp_dir.glob("test[.-]*.yaml"): component_tests[comp_dir.name].append(test_file) return dict(component_tests) From b134d42e3b91512cdcdfe6b8f13f0e62ba0b2c9b Mon Sep 17 00:00:00 2001 From: Niall Douglas Date: Sat, 18 Oct 2025 02:47:18 +0200 Subject: [PATCH 121/526] [xgzp68xx] Add oversampling config and tidy up implementation. (#10306) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/xgzp68xx/sensor.py | 19 ++++++ esphome/components/xgzp68xx/xgzp68xx.cpp | 80 ++++++++++++++++-------- esphome/components/xgzp68xx/xgzp68xx.h | 20 ++++++ tests/components/xgzp68xx/common.yaml | 1 + 4 files changed, 94 insertions(+), 26 deletions(-) diff --git a/esphome/components/xgzp68xx/sensor.py b/esphome/components/xgzp68xx/sensor.py index 74cef3bf7b..2b38392a02 100644 --- a/esphome/components/xgzp68xx/sensor.py +++ b/esphome/components/xgzp68xx/sensor.py @@ -3,6 +3,7 @@ from esphome.components import i2c, sensor import esphome.config_validation as cv from esphome.const import ( CONF_ID, + CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, DEVICE_CLASS_PRESSURE, @@ -18,6 +19,17 @@ CODEOWNERS = ["@gcormier"] CONF_K_VALUE = "k_value" xgzp68xx_ns = cg.esphome_ns.namespace("xgzp68xx") +XGZP68XXOversampling = xgzp68xx_ns.enum("XGZP68XXOversampling") +OVERSAMPLING_OPTIONS = { + "256X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_256X, + "512X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_512X, + "1024X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_1024X, + "2048X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_2048X, + "4096X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_4096X, + "8192X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_8192X, + "16384X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_16384X, + "32768X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_32768X, +} XGZP68XXComponent = xgzp68xx_ns.class_( "XGZP68XXComponent", cg.PollingComponent, i2c.I2CDevice ) @@ -31,6 +43,12 @@ CONFIG_SCHEMA = ( accuracy_decimals=1, device_class=DEVICE_CLASS_PRESSURE, state_class=STATE_CLASS_MEASUREMENT, + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="4096X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } ), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, @@ -58,5 +76,6 @@ async def to_code(config): if pressure_config := config.get(CONF_PRESSURE): sens = await sensor.new_sensor(pressure_config) cg.add(var.set_pressure_sensor(sens)) + cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING])) cg.add(var.set_k_value(config[CONF_K_VALUE])) diff --git a/esphome/components/xgzp68xx/xgzp68xx.cpp b/esphome/components/xgzp68xx/xgzp68xx.cpp index 20a97cd04b..2b0824de0a 100644 --- a/esphome/components/xgzp68xx/xgzp68xx.cpp +++ b/esphome/components/xgzp68xx/xgzp68xx.cpp @@ -16,16 +16,49 @@ static const uint8_t SYSCONFIG_ADDRESS = 0xA5; static const uint8_t PCONFIG_ADDRESS = 0xA6; static const uint8_t READ_COMMAND = 0x0A; +[[maybe_unused]] static const char *oversampling_to_str(XGZP68XXOversampling oversampling) { + switch (oversampling) { + case XGZP68XX_OVERSAMPLING_256X: + return "256x"; + case XGZP68XX_OVERSAMPLING_512X: + return "512x"; + case XGZP68XX_OVERSAMPLING_1024X: + return "1024x"; + case XGZP68XX_OVERSAMPLING_2048X: + return "2048x"; + case XGZP68XX_OVERSAMPLING_4096X: + return "4096x"; + case XGZP68XX_OVERSAMPLING_8192X: + return "8192x"; + case XGZP68XX_OVERSAMPLING_16384X: + return "16384x"; + case XGZP68XX_OVERSAMPLING_32768X: + return "32768x"; + default: + return "UNKNOWN"; + } +} + void XGZP68XXComponent::update() { + // Do we need to change oversampling? + if (this->last_pressure_oversampling_ != this->pressure_oversampling_) { + uint8_t oldconfig = 0; + this->read_register(PCONFIG_ADDRESS, &oldconfig, 1); + uint8_t newconfig = (oldconfig & 0xf8) | (this->pressure_oversampling_ & 0x7); + this->write_register(PCONFIG_ADDRESS, &newconfig, 1); + ESP_LOGD(TAG, "oversampling to %s: oldconfig = 0x%x newconfig = 0x%x", + oversampling_to_str(this->pressure_oversampling_), oldconfig, newconfig); + this->last_pressure_oversampling_ = this->pressure_oversampling_; + } + // Request temp + pressure acquisition this->write_register(0x30, &READ_COMMAND, 1); // Wait 20mS per datasheet this->set_timeout("measurement", 20, [this]() { - uint8_t data[5]; - uint32_t pressure_raw; - uint16_t temperature_raw; - float pressure_in_pa, temperature; + uint8_t data[5] = {}; + uint32_t pressure_raw = 0; + uint16_t temperature_raw = 0; int success; // Read the sensor data @@ -42,23 +75,11 @@ void XGZP68XXComponent::update() { ESP_LOGV(TAG, "Got raw pressure=%" PRIu32 ", raw temperature=%u", pressure_raw, temperature_raw); ESP_LOGV(TAG, "K value is %u", this->k_value_); - // The most significant bit of both pressure and temperature will be 1 to indicate a negative value. - // This is directly from the datasheet, and the calculations below will handle this. - if (pressure_raw > pow(2, 23)) { - // Negative pressure - pressure_in_pa = (pressure_raw - pow(2, 24)) / (float) (this->k_value_); - } else { - // Positive pressure - pressure_in_pa = pressure_raw / (float) (this->k_value_); - } + // Sign extend the pressure + float pressure_in_pa = (float) (((int32_t) pressure_raw << 8) >> 8); + pressure_in_pa /= (float) (this->k_value_); - if (temperature_raw > pow(2, 15)) { - // Negative temperature - temperature = (float) (temperature_raw - pow(2, 16)) / 256.0f; - } else { - // Positive temperature - temperature = (float) temperature_raw / 256.0f; - } + float temperature = ((float) (int16_t) temperature_raw) / 256.0f; if (this->pressure_sensor_ != nullptr) this->pressure_sensor_->publish_state(pressure_in_pa); @@ -69,20 +90,27 @@ void XGZP68XXComponent::update() { } void XGZP68XXComponent::setup() { - uint8_t config; + uint8_t config1 = 0, config2 = 0; // Display some sample bits to confirm we are talking to the sensor - this->read_register(SYSCONFIG_ADDRESS, &config, 1); - ESP_LOGCONFIG(TAG, - "Gain value is %d\n" - "XGZP68xx started!", - (config >> 3) & 0b111); + if (i2c::ErrorCode::ERROR_OK != this->read_register(SYSCONFIG_ADDRESS, &config1, 1)) { + this->mark_failed(); + return; + } + if (i2c::ErrorCode::ERROR_OK != this->read_register(PCONFIG_ADDRESS, &config2, 1)) { + this->mark_failed(); + return; + } + ESP_LOGD(TAG, "sys_config 0x%x, p_config 0x%x", config1, config2); } void XGZP68XXComponent::dump_config() { ESP_LOGCONFIG(TAG, "XGZP68xx:"); LOG_SENSOR(" ", "Temperature: ", this->temperature_sensor_); LOG_SENSOR(" ", "Pressure: ", this->pressure_sensor_); + if (this->pressure_sensor_ != nullptr) { + ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->pressure_oversampling_)); + } LOG_I2C_DEVICE(this); if (this->is_failed()) { ESP_LOGE(TAG, " Connection failed"); diff --git a/esphome/components/xgzp68xx/xgzp68xx.h b/esphome/components/xgzp68xx/xgzp68xx.h index 1bb7304b15..ce9cfd6b78 100644 --- a/esphome/components/xgzp68xx/xgzp68xx.h +++ b/esphome/components/xgzp68xx/xgzp68xx.h @@ -7,11 +7,29 @@ namespace esphome { namespace xgzp68xx { +/// Enum listing all oversampling options for the XGZP68XX. +enum XGZP68XXOversampling : uint8_t { + XGZP68XX_OVERSAMPLING_256X = 0b100, + XGZP68XX_OVERSAMPLING_512X = 0b101, + XGZP68XX_OVERSAMPLING_1024X = 0b000, + XGZP68XX_OVERSAMPLING_2048X = 0b001, + XGZP68XX_OVERSAMPLING_4096X = 0b010, + XGZP68XX_OVERSAMPLING_8192X = 0b011, + XGZP68XX_OVERSAMPLING_16384X = 0b110, + XGZP68XX_OVERSAMPLING_32768X = 0b111, + + XGZP68XX_OVERSAMPLING_UNKNOWN = (uint8_t) -1, +}; + class XGZP68XXComponent : public PollingComponent, public sensor::Sensor, public i2c::I2CDevice { public: SUB_SENSOR(temperature) SUB_SENSOR(pressure) void set_k_value(uint16_t k_value) { this->k_value_ = k_value; } + /// Set the pressure oversampling value. Defaults to 4096X. + void set_pressure_oversampling(XGZP68XXOversampling pressure_oversampling) { + this->pressure_oversampling_ = pressure_oversampling; + } void update() override; void setup() override; @@ -21,6 +39,8 @@ class XGZP68XXComponent : public PollingComponent, public sensor::Sensor, public /// Internal method to read the pressure from the component after it has been scheduled. void read_pressure_(); uint16_t k_value_; + XGZP68XXOversampling pressure_oversampling_{XGZP68XX_OVERSAMPLING_4096X}; + XGZP68XXOversampling last_pressure_oversampling_{XGZP68XX_OVERSAMPLING_UNKNOWN}; }; } // namespace xgzp68xx diff --git a/tests/components/xgzp68xx/common.yaml b/tests/components/xgzp68xx/common.yaml index f76b1de508..00e51e764c 100644 --- a/tests/components/xgzp68xx/common.yaml +++ b/tests/components/xgzp68xx/common.yaml @@ -6,3 +6,4 @@ sensor: name: Pressure Temperature pressure: name: Differential pressure + oversampling: 1024x From bfeade1e2bcb1372fed563f813a24032cbb7d04b Mon Sep 17 00:00:00 2001 From: Leonardo Rivera Date: Fri, 17 Oct 2025 22:13:33 -0300 Subject: [PATCH 122/526] [remote_base] Add Symphony IR protocol (encode/decode) with command_repeats support (#10777) --- esphome/components/remote_base/__init__.py | 46 +++++++ .../remote_base/symphony_protocol.cpp | 120 ++++++++++++++++++ .../remote_base/symphony_protocol.h | 44 +++++++ .../remote_receiver/common-actions.yaml | 5 + .../remote_transmitter/common-buttons.yaml | 6 + 5 files changed, 221 insertions(+) create mode 100644 esphome/components/remote_base/symphony_protocol.cpp create mode 100644 esphome/components/remote_base/symphony_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 42ebae77f7..ccf16a8beb 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1056,6 +1056,52 @@ async def sony_action(var, config, args): cg.add(var.set_nbits(template_)) +# Symphony +SymphonyData, SymphonyBinarySensor, SymphonyTrigger, SymphonyAction, SymphonyDumper = ( + declare_protocol("Symphony") +) +SYMPHONY_SCHEMA = cv.Schema( + { + cv.Required(CONF_DATA): cv.hex_uint32_t, + cv.Required(CONF_NBITS): cv.int_range(min=1, max=32), + cv.Optional(CONF_COMMAND_REPEATS, default=2): cv.uint8_t, + } +) + + +@register_binary_sensor("symphony", SymphonyBinarySensor, SYMPHONY_SCHEMA) +def symphony_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + SymphonyData, + ("data", config[CONF_DATA]), + ("nbits", config[CONF_NBITS]), + ) + ) + ) + + +@register_trigger("symphony", SymphonyTrigger, SymphonyData) +def symphony_trigger(var, config): + pass + + +@register_dumper("symphony", SymphonyDumper) +def symphony_dumper(var, config): + pass + + +@register_action("symphony", SymphonyAction, SYMPHONY_SCHEMA) +async def symphony_action(var, config, args): + template_ = await cg.templatable(config[CONF_DATA], args, cg.uint32) + cg.add(var.set_data(template_)) + template_ = await cg.templatable(config[CONF_NBITS], args, cg.uint32) + cg.add(var.set_nbits(template_)) + template_ = await cg.templatable(config[CONF_COMMAND_REPEATS], args, cg.uint8) + cg.add(var.set_repeats(template_)) + + # Raw def validate_raw_alternating(value): assert isinstance(value, list) diff --git a/esphome/components/remote_base/symphony_protocol.cpp b/esphome/components/remote_base/symphony_protocol.cpp new file mode 100644 index 0000000000..34b5dba07f --- /dev/null +++ b/esphome/components/remote_base/symphony_protocol.cpp @@ -0,0 +1,120 @@ +#include "symphony_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.symphony"; + +// Reference implementation and timing details: +// IRremoteESP8266 ir_Symphony.cpp +// https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp +// The implementation below mirrors the constant bit-time mapping and +// footer-gap handling used there. + +// Symphony protocol timing specifications (tuned to handset captures) +static const uint32_t BIT_ZERO_HIGH_US = 460; // short +static const uint32_t BIT_ZERO_LOW_US = 1260; // long +static const uint32_t BIT_ONE_HIGH_US = 1260; // long +static const uint32_t BIT_ONE_LOW_US = 460; // short +static const uint32_t CARRIER_FREQUENCY = 38000; + +// IRremoteESP8266 reference: kSymphonyFooterGap = 4 * (mark + space) +static const uint32_t FOOTER_GAP_US = 4 * (BIT_ZERO_HIGH_US + BIT_ZERO_LOW_US); +// Typical inter-frame gap (~34.8 ms observed) +static const uint32_t INTER_FRAME_GAP_US = 34760; + +void SymphonyProtocol::encode(RemoteTransmitData *dst, const SymphonyData &data) { + dst->set_carrier_frequency(CARRIER_FREQUENCY); + ESP_LOGD(TAG, "Sending Symphony: data=0x%0*X nbits=%u repeats=%u", (data.nbits + 3) / 4, (uint32_t) data.data, + data.nbits, data.repeats); + // Each bit produces a mark+space (2 entries). We fold the inter-frame/footer gap + // into the last bit's space of each frame to avoid over-length gaps. + dst->reserve(data.nbits * 2u * data.repeats); + + for (uint8_t repeats = 0; repeats < data.repeats; repeats++) { + // Data bits (MSB first) + for (uint32_t mask = 1UL << (data.nbits - 1); mask != 0; mask >>= 1) { + const bool is_last_bit = (mask == 1); + const bool is_last_frame = (repeats == (data.repeats - 1)); + if (is_last_bit) { + // Emit last bit's mark; replace its space with the proper gap + if (data.data & mask) { + dst->mark(BIT_ONE_HIGH_US); + } else { + dst->mark(BIT_ZERO_HIGH_US); + } + dst->space(is_last_frame ? FOOTER_GAP_US : INTER_FRAME_GAP_US); + } else { + if (data.data & mask) { + dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US); + } + } + } + } +} + +optional SymphonyProtocol::decode(RemoteReceiveData src) { + auto is_valid_len = [](uint8_t nbits) -> bool { return nbits == 8 || nbits == 12 || nbits == 16; }; + + RemoteReceiveData s = src; // copy + SymphonyData out{0, 0, 1}; + + for (; out.nbits < 32; out.nbits++) { + if (s.expect_mark(BIT_ONE_HIGH_US)) { + if (!s.expect_space(BIT_ONE_LOW_US)) { + // Allow footer gap immediately after the last mark + if (s.peek_space_at_least(FOOTER_GAP_US)) { + uint8_t bits_with_this = out.nbits + 1; + if (is_valid_len(bits_with_this)) { + out.data = (out.data << 1UL) | 1UL; + out.nbits = bits_with_this; + return out; + } + } + return {}; + } + // Successfully consumed a '1' bit (mark + space) + out.data = (out.data << 1UL) | 1UL; + continue; + } else if (s.expect_mark(BIT_ZERO_HIGH_US)) { + if (!s.expect_space(BIT_ZERO_LOW_US)) { + // Allow footer gap immediately after the last mark + if (s.peek_space_at_least(FOOTER_GAP_US)) { + uint8_t bits_with_this = out.nbits + 1; + if (is_valid_len(bits_with_this)) { + out.data = (out.data << 1UL) | 0UL; + out.nbits = bits_with_this; + return out; + } + } + return {}; + } + // Successfully consumed a '0' bit (mark + space) + out.data = (out.data << 1UL) | 0UL; + continue; + } else { + // Completed a valid-length frame followed by a footer gap + if (is_valid_len(out.nbits) && s.peek_space_at_least(FOOTER_GAP_US)) { + return out; + } + return {}; + } + } + + if (is_valid_len(out.nbits) && s.peek_space_at_least(FOOTER_GAP_US)) { + return out; + } + + return {}; +} + +void SymphonyProtocol::dump(const SymphonyData &data) { + const int32_t hex_width = (data.nbits + 3) / 4; // pad to nibble width + ESP_LOGI(TAG, "Received Symphony: data=0x%0*X, nbits=%d", hex_width, (uint32_t) data.data, data.nbits); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/symphony_protocol.h b/esphome/components/remote_base/symphony_protocol.h new file mode 100644 index 0000000000..7e77a268ba --- /dev/null +++ b/esphome/components/remote_base/symphony_protocol.h @@ -0,0 +1,44 @@ +#pragma once + +#include "esphome/core/component.h" +#include "remote_base.h" + +#include + +namespace esphome { +namespace remote_base { + +struct SymphonyData { + uint32_t data; + uint8_t nbits; + uint8_t repeats{1}; + + bool operator==(const SymphonyData &rhs) const { return data == rhs.data && nbits == rhs.nbits; } +}; + +class SymphonyProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const SymphonyData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const SymphonyData &data) override; +}; + +DECLARE_REMOTE_PROTOCOL(Symphony) + +template class SymphonyAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint32_t, data) + TEMPLATABLE_VALUE(uint8_t, nbits) + TEMPLATABLE_VALUE(uint8_t, repeats) + + void encode(RemoteTransmitData *dst, Ts... x) override { + SymphonyData data{}; + data.data = this->data_.value(x...); + data.nbits = this->nbits_.value(x...); + data.repeats = this->repeats_.value(x...); + SymphonyProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome diff --git a/tests/components/remote_receiver/common-actions.yaml b/tests/components/remote_receiver/common-actions.yaml index ca7713f58a..c2dc2f0c29 100644 --- a/tests/components/remote_receiver/common-actions.yaml +++ b/tests/components/remote_receiver/common-actions.yaml @@ -143,6 +143,11 @@ on_sony: - logger.log: format: "on_sony: %lu %u" args: ["long(x.data)", "x.nbits"] +on_symphony: + then: + - logger.log: + format: "on_symphony: 0x%lX %u" + args: ["long(x.data)", "x.nbits"] on_toshiba_ac: then: - logger.log: diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml index 3be4bf3cca..58127d1ab4 100644 --- a/tests/components/remote_transmitter/common-buttons.yaml +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -53,6 +53,12 @@ button: remote_transmitter.transmit_sony: data: 0xABCDEF nbits: 12 + - platform: template + name: Symphony + on_press: + remote_transmitter.transmit_symphony: + data: 0xE88 + nbits: 12 - platform: template name: Panasonic on_press: From 85f1019d90a7e5d6eeafda94e6aec0b519fe4b21 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 17 Oct 2025 17:21:38 -1000 Subject: [PATCH 123/526] [tests] Migrate remote_transmitter/receiver to common bus definitions (#11325) --- script/analyze_component_buses.py | 11 ++++++++++- tests/components/ballu/common.yaml | 5 +---- tests/components/ballu/test.esp8266-ard.yaml | 4 ++-- tests/components/climate_ir_lg/common.yaml | 5 +---- .../climate_ir_lg/test.esp32-c3-idf.yaml | 4 ++-- .../components/climate_ir_lg/test.esp32-idf.yaml | 4 ++-- .../climate_ir_lg/test.esp8266-ard.yaml | 4 ++-- tests/components/coolix/common.yaml | 5 +---- tests/components/coolix/test.esp32-c3-idf.yaml | 4 ++-- tests/components/coolix/test.esp32-idf.yaml | 4 ++-- tests/components/coolix/test.esp8266-ard.yaml | 4 ++-- tests/components/daikin/common.yaml | 5 +---- tests/components/daikin/test.esp8266-ard.yaml | 4 ++-- tests/components/daikin_arc/common.yaml | 15 --------------- .../components/daikin_arc/test.esp8266-ard.yaml | 6 +++--- tests/components/daikin_brc/common.yaml | 5 +---- .../components/daikin_brc/test.esp32-c3-idf.yaml | 4 ++-- tests/components/daikin_brc/test.esp32-idf.yaml | 4 ++-- .../components/daikin_brc/test.esp8266-ard.yaml | 4 ++-- tests/components/delonghi/common.yaml | 5 +---- tests/components/delonghi/test.esp32-c3-idf.yaml | 4 ++-- tests/components/delonghi/test.esp32-idf.yaml | 4 ++-- tests/components/delonghi/test.esp8266-ard.yaml | 4 ++-- tests/components/emmeti/common.yaml | 11 +---------- tests/components/emmeti/test.esp32-idf.yaml | 6 +++--- tests/components/emmeti/test.esp8266-ard.yaml | 6 +++--- tests/components/fujitsu_general/common.yaml | 5 +---- .../fujitsu_general/test.esp32-c3-idf.yaml | 4 ++-- .../fujitsu_general/test.esp32-idf.yaml | 4 ++-- .../fujitsu_general/test.esp8266-ard.yaml | 4 ++-- tests/components/gree/common.yaml | 5 +---- tests/components/gree/test.esp32-c3-idf.yaml | 4 ++-- tests/components/gree/test.esp32-idf.yaml | 4 ++-- tests/components/gree/test.esp8266-ard.yaml | 4 ++-- tests/components/heatpumpir/common.yaml | 7 +++---- tests/components/heatpumpir/test.bk72xx-ard.yaml | 4 ++-- tests/components/heatpumpir/test.esp32-ard.yaml | 4 ++-- .../components/heatpumpir/test.esp8266-ard.yaml | 4 ++-- tests/components/hitachi_ac344/common.yaml | 5 +---- .../hitachi_ac344/test.bk72xx-ard.yaml | 4 ++-- .../hitachi_ac344/test.esp32-c3-idf.yaml | 4 ++-- .../components/hitachi_ac344/test.esp32-idf.yaml | 4 ++-- .../hitachi_ac344/test.esp8266-ard.yaml | 4 ++-- tests/components/hitachi_ac424/common.yaml | 5 +---- .../hitachi_ac424/test.bk72xx-ard.yaml | 4 ++-- .../hitachi_ac424/test.esp32-c3-idf.yaml | 4 ++-- .../components/hitachi_ac424/test.esp32-idf.yaml | 4 ++-- .../hitachi_ac424/test.esp8266-ard.yaml | 4 ++-- tests/components/midea/common.yaml | 6 +----- tests/components/midea/test.esp32-ard.yaml | 4 +--- tests/components/midea/test.esp8266-ard.yaml | 4 +--- tests/components/midea_ir/common.yaml | 5 +---- tests/components/midea_ir/test.esp32-c3-idf.yaml | 3 +++ tests/components/midea_ir/test.esp32-idf.yaml | 3 +++ tests/components/midea_ir/test.esp8266-ard.yaml | 3 +++ tests/components/mitsubishi/common.yaml | 5 +---- .../components/mitsubishi/test.esp32-c3-idf.yaml | 3 +++ tests/components/mitsubishi/test.esp32-idf.yaml | 3 +++ .../components/mitsubishi/test.esp8266-ard.yaml | 3 +++ tests/components/noblex/common.yaml | 9 --------- tests/components/noblex/test.esp32-c3-idf.yaml | 4 ++++ tests/components/noblex/test.esp32-idf.yaml | 4 ++++ tests/components/noblex/test.esp8266-ard.yaml | 4 ++++ tests/components/prometheus/common.yaml | 5 +---- .../components/prometheus/test.esp32-c3-idf.yaml | 4 +++- tests/components/prometheus/test.esp32-idf.yaml | 2 +- .../components/prometheus/test.esp8266-ard.yaml | 4 +++- tests/components/tcl112/common.yaml | 5 +---- tests/components/tcl112/test.esp32-c3-idf.yaml | 4 ++-- tests/components/tcl112/test.esp32-idf.yaml | 4 ++-- tests/components/tcl112/test.esp8266-ard.yaml | 4 ++-- tests/components/toshiba/common.yaml | 5 +---- tests/components/toshiba/test.esp32-c3-idf.yaml | 4 ++-- tests/components/toshiba/test.esp32-idf.yaml | 4 ++-- tests/components/toshiba/test.esp8266-ard.yaml | 4 ++-- tests/components/whirlpool/common.yaml | 5 +---- .../components/whirlpool/test.esp32-c3-idf.yaml | 4 ++-- tests/components/whirlpool/test.esp32-idf.yaml | 4 ++-- tests/components/whirlpool/test.esp8266-ard.yaml | 4 ++-- tests/components/whynter/common.yaml | 5 +---- tests/components/whynter/test.esp32-c3-idf.yaml | 4 ++-- tests/components/whynter/test.esp32-idf.yaml | 4 ++-- tests/components/whynter/test.esp8266-ard.yaml | 4 ++-- tests/components/yashima/common.yaml | 5 +---- tests/components/yashima/test.esp32-c3-idf.yaml | 4 ++-- tests/components/yashima/test.esp32-idf.yaml | 4 ++-- tests/components/yashima/test.esp8266-ard.yaml | 4 ++-- tests/components/zhlt01/common.yaml | 5 +---- tests/components/zhlt01/test.esp32-c3-idf.yaml | 4 ++-- tests/components/zhlt01/test.esp32-idf.yaml | 4 ++-- tests/components/zhlt01/test.esp8266-ard.yaml | 4 ++-- .../common/remote_receiver/esp32-c3-idf.yaml | 16 ++++++++++++++++ .../common/remote_receiver/esp32-idf.yaml | 16 ++++++++++++++++ .../common/remote_receiver/esp8266-ard.yaml | 12 ++++++++++++ .../common/remote_transmitter/bk72xx-ard.yaml | 11 +++++++++++ .../common/remote_transmitter/esp32-ard.yaml | 11 +++++++++++ .../common/remote_transmitter/esp32-c3-idf.yaml | 13 +++++++++++++ .../common/remote_transmitter/esp32-idf.yaml | 13 +++++++++++++ .../common/remote_transmitter/esp8266-ard.yaml | 11 +++++++++++ 99 files changed, 283 insertions(+), 236 deletions(-) create mode 100644 tests/test_build_components/common/remote_receiver/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/remote_receiver/esp32-idf.yaml create mode 100644 tests/test_build_components/common/remote_receiver/esp8266-ard.yaml create mode 100644 tests/test_build_components/common/remote_transmitter/bk72xx-ard.yaml create mode 100644 tests/test_build_components/common/remote_transmitter/esp32-ard.yaml create mode 100644 tests/test_build_components/common/remote_transmitter/esp32-c3-idf.yaml create mode 100644 tests/test_build_components/common/remote_transmitter/esp32-idf.yaml create mode 100644 tests/test_build_components/common/remote_transmitter/esp8266-ard.yaml diff --git a/script/analyze_component_buses.py b/script/analyze_component_buses.py index 98edfef145..d0882e22e9 100755 --- a/script/analyze_component_buses.py +++ b/script/analyze_component_buses.py @@ -50,7 +50,14 @@ PACKAGE_DEPENDENCIES = { # Bus types that can be defined directly in config files # Components defining these directly cannot be grouped (they create unique bus IDs) -DIRECT_BUS_TYPES = ("i2c", "spi", "uart", "modbus") +DIRECT_BUS_TYPES = ( + "i2c", + "spi", + "uart", + "modbus", + "remote_transmitter", + "remote_receiver", +) # Signature for components with no bus requirements # These components can be merged with any other group @@ -68,6 +75,8 @@ BASE_BUS_COMPONENTS = { "uart", "modbus", "canbus", + "remote_transmitter", + "remote_receiver", } # Components that must be tested in isolation (not grouped or batched with others) diff --git a/tests/components/ballu/common.yaml b/tests/components/ballu/common.yaml index 52f86aa26a..178c39b8ce 100644 --- a/tests/components/ballu/common.yaml +++ b/tests/components/ballu/common.yaml @@ -1,7 +1,3 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: heatpumpir protocol: ballu @@ -10,3 +6,4 @@ climate: name: HeatpumpIR Climate min_temperature: 18 max_temperature: 30 + transmitter_id: xmitr diff --git a/tests/components/ballu/test.esp8266-ard.yaml b/tests/components/ballu/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/ballu/test.esp8266-ard.yaml +++ b/tests/components/ballu/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/climate_ir_lg/common.yaml b/tests/components/climate_ir_lg/common.yaml index c8f84411c0..da0d656b21 100644 --- a/tests/components/climate_ir_lg/common.yaml +++ b/tests/components/climate_ir_lg/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: climate_ir_lg name: LG Climate + transmitter_id: xmitr diff --git a/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml b/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml +++ b/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/climate_ir_lg/test.esp32-idf.yaml b/tests/components/climate_ir_lg/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/climate_ir_lg/test.esp32-idf.yaml +++ b/tests/components/climate_ir_lg/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/climate_ir_lg/test.esp8266-ard.yaml b/tests/components/climate_ir_lg/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/climate_ir_lg/test.esp8266-ard.yaml +++ b/tests/components/climate_ir_lg/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/coolix/common.yaml b/tests/components/coolix/common.yaml index abe609c3ea..a1f68b8be0 100644 --- a/tests/components/coolix/common.yaml +++ b/tests/components/coolix/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: coolix name: Coolix Climate + transmitter_id: xmitr diff --git a/tests/components/coolix/test.esp32-c3-idf.yaml b/tests/components/coolix/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/coolix/test.esp32-c3-idf.yaml +++ b/tests/components/coolix/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/coolix/test.esp32-idf.yaml b/tests/components/coolix/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/coolix/test.esp32-idf.yaml +++ b/tests/components/coolix/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/coolix/test.esp8266-ard.yaml b/tests/components/coolix/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/coolix/test.esp8266-ard.yaml +++ b/tests/components/coolix/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/daikin/common.yaml b/tests/components/daikin/common.yaml index 27f381b422..fd73841686 100644 --- a/tests/components/daikin/common.yaml +++ b/tests/components/daikin/common.yaml @@ -1,7 +1,3 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: heatpumpir protocol: daikin @@ -10,3 +6,4 @@ climate: name: HeatpumpIR Climate min_temperature: 18 max_temperature: 30 + transmitter_id: xmitr diff --git a/tests/components/daikin/test.esp8266-ard.yaml b/tests/components/daikin/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/daikin/test.esp8266-ard.yaml +++ b/tests/components/daikin/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/daikin_arc/common.yaml b/tests/components/daikin_arc/common.yaml index 5c0510f6df..53df3cf911 100644 --- a/tests/components/daikin_arc/common.yaml +++ b/tests/components/daikin_arc/common.yaml @@ -1,18 +1,3 @@ -remote_transmitter: - pin: ${tx_pin} - carrier_duty_percent: 50% - id: tsvr - -remote_receiver: - id: rcvr - pin: - number: ${rx_pin} - inverted: true - mode: - input: true - pullup: true - tolerance: 40% - climate: - platform: daikin_arc name: Daikin AC diff --git a/tests/components/daikin_arc/test.esp8266-ard.yaml b/tests/components/daikin_arc/test.esp8266-ard.yaml index 5698a7ef5f..aa8651e556 100644 --- a/tests/components/daikin_arc/test.esp8266-ard.yaml +++ b/tests/components/daikin_arc/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ -substitutions: - tx_pin: GPIO0 - rx_pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml + remote_receiver: !include ../../test_build_components/common/remote_receiver/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/daikin_brc/common.yaml b/tests/components/daikin_brc/common.yaml index c9d7baa989..89786954ba 100644 --- a/tests/components/daikin_brc/common.yaml +++ b/tests/components/daikin_brc/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: daikin_brc name: Daikin_brc Climate + transmitter_id: xmitr diff --git a/tests/components/daikin_brc/test.esp32-c3-idf.yaml b/tests/components/daikin_brc/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/daikin_brc/test.esp32-c3-idf.yaml +++ b/tests/components/daikin_brc/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/daikin_brc/test.esp32-idf.yaml b/tests/components/daikin_brc/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/daikin_brc/test.esp32-idf.yaml +++ b/tests/components/daikin_brc/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/daikin_brc/test.esp8266-ard.yaml b/tests/components/daikin_brc/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/daikin_brc/test.esp8266-ard.yaml +++ b/tests/components/daikin_brc/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/delonghi/common.yaml b/tests/components/delonghi/common.yaml index 8e9a1293d7..c3935adcbd 100644 --- a/tests/components/delonghi/common.yaml +++ b/tests/components/delonghi/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: delonghi name: Delonghi Climate + transmitter_id: xmitr diff --git a/tests/components/delonghi/test.esp32-c3-idf.yaml b/tests/components/delonghi/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/delonghi/test.esp32-c3-idf.yaml +++ b/tests/components/delonghi/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/delonghi/test.esp32-idf.yaml b/tests/components/delonghi/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/delonghi/test.esp32-idf.yaml +++ b/tests/components/delonghi/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/delonghi/test.esp8266-ard.yaml b/tests/components/delonghi/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/delonghi/test.esp8266-ard.yaml +++ b/tests/components/delonghi/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/emmeti/common.yaml b/tests/components/emmeti/common.yaml index ac4201e19b..77f381ecf9 100644 --- a/tests/components/emmeti/common.yaml +++ b/tests/components/emmeti/common.yaml @@ -1,14 +1,5 @@ -remote_transmitter: - id: tx - pin: ${remote_transmitter_pin} - carrier_duty_percent: 100% - -remote_receiver: - id: rcvr - pin: ${remote_receiver_pin} - climate: - platform: emmeti name: Emmeti receiver_id: rcvr - transmitter_id: tx + transmitter_id: xmitr diff --git a/tests/components/emmeti/test.esp32-idf.yaml b/tests/components/emmeti/test.esp32-idf.yaml index 2689ff279e..b241dbd159 100644 --- a/tests/components/emmeti/test.esp32-idf.yaml +++ b/tests/components/emmeti/test.esp32-idf.yaml @@ -1,5 +1,5 @@ -substitutions: - remote_transmitter_pin: GPIO33 - remote_receiver_pin: GPIO32 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml + remote_receiver: !include ../../test_build_components/common/remote_receiver/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/emmeti/test.esp8266-ard.yaml b/tests/components/emmeti/test.esp8266-ard.yaml index 1c9baa4ea3..aa8651e556 100644 --- a/tests/components/emmeti/test.esp8266-ard.yaml +++ b/tests/components/emmeti/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ -substitutions: - remote_transmitter_pin: GPIO0 - remote_receiver_pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml + remote_receiver: !include ../../test_build_components/common/remote_receiver/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/fujitsu_general/common.yaml b/tests/components/fujitsu_general/common.yaml index 3359b89f2a..51bd1441c0 100644 --- a/tests/components/fujitsu_general/common.yaml +++ b/tests/components/fujitsu_general/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: fujitsu_general name: Fujitsu General Climate + transmitter_id: xmitr diff --git a/tests/components/fujitsu_general/test.esp32-c3-idf.yaml b/tests/components/fujitsu_general/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/fujitsu_general/test.esp32-c3-idf.yaml +++ b/tests/components/fujitsu_general/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/fujitsu_general/test.esp32-idf.yaml b/tests/components/fujitsu_general/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/fujitsu_general/test.esp32-idf.yaml +++ b/tests/components/fujitsu_general/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/fujitsu_general/test.esp8266-ard.yaml b/tests/components/fujitsu_general/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/fujitsu_general/test.esp8266-ard.yaml +++ b/tests/components/fujitsu_general/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/gree/common.yaml b/tests/components/gree/common.yaml index c221184bbf..e706076034 100644 --- a/tests/components/gree/common.yaml +++ b/tests/components/gree/common.yaml @@ -1,8 +1,5 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: gree name: GREE model: generic + transmitter_id: xmitr diff --git a/tests/components/gree/test.esp32-c3-idf.yaml b/tests/components/gree/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/gree/test.esp32-c3-idf.yaml +++ b/tests/components/gree/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/gree/test.esp32-idf.yaml b/tests/components/gree/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/gree/test.esp32-idf.yaml +++ b/tests/components/gree/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/gree/test.esp8266-ard.yaml b/tests/components/gree/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/gree/test.esp8266-ard.yaml +++ b/tests/components/gree/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/heatpumpir/common.yaml b/tests/components/heatpumpir/common.yaml index d740f31518..a2779f9803 100644 --- a/tests/components/heatpumpir/common.yaml +++ b/tests/components/heatpumpir/common.yaml @@ -1,7 +1,3 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: heatpumpir protocol: mitsubishi_heavy_zm @@ -10,6 +6,7 @@ climate: name: HeatpumpIR Climate Mitsubishi min_temperature: 18 max_temperature: 30 + transmitter_id: xmitr - platform: heatpumpir protocol: daikin horizontal_default: mleft @@ -17,6 +14,7 @@ climate: name: HeatpumpIR Climate Daikin min_temperature: 18 max_temperature: 30 + transmitter_id: xmitr - platform: heatpumpir protocol: panasonic_altdke horizontal_default: mright @@ -24,3 +22,4 @@ climate: name: HeatpumpIR Climate Panasonic min_temperature: 18 max_temperature: 30 + transmitter_id: xmitr diff --git a/tests/components/heatpumpir/test.bk72xx-ard.yaml b/tests/components/heatpumpir/test.bk72xx-ard.yaml index 06e1aea364..6cce191825 100644 --- a/tests/components/heatpumpir/test.bk72xx-ard.yaml +++ b/tests/components/heatpumpir/test.bk72xx-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO6 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/bk72xx-ard.yaml <<: !include common.yaml diff --git a/tests/components/heatpumpir/test.esp32-ard.yaml b/tests/components/heatpumpir/test.esp32-ard.yaml index 7b012aa64c..01009de071 100644 --- a/tests/components/heatpumpir/test.esp32-ard.yaml +++ b/tests/components/heatpumpir/test.esp32-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-ard.yaml <<: !include common.yaml diff --git a/tests/components/heatpumpir/test.esp8266-ard.yaml b/tests/components/heatpumpir/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/heatpumpir/test.esp8266-ard.yaml +++ b/tests/components/heatpumpir/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/hitachi_ac344/common.yaml b/tests/components/hitachi_ac344/common.yaml index 960f032035..797e7875e5 100644 --- a/tests/components/hitachi_ac344/common.yaml +++ b/tests/components/hitachi_ac344/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: hitachi_ac344 name: Hitachi Climate + transmitter_id: xmitr diff --git a/tests/components/hitachi_ac344/test.bk72xx-ard.yaml b/tests/components/hitachi_ac344/test.bk72xx-ard.yaml index 06e1aea364..6cce191825 100644 --- a/tests/components/hitachi_ac344/test.bk72xx-ard.yaml +++ b/tests/components/hitachi_ac344/test.bk72xx-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO6 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/bk72xx-ard.yaml <<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml b/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml +++ b/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp32-idf.yaml b/tests/components/hitachi_ac344/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/hitachi_ac344/test.esp32-idf.yaml +++ b/tests/components/hitachi_ac344/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp8266-ard.yaml b/tests/components/hitachi_ac344/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/hitachi_ac344/test.esp8266-ard.yaml +++ b/tests/components/hitachi_ac344/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/hitachi_ac424/common.yaml b/tests/components/hitachi_ac424/common.yaml index ad904c73a3..615bda4544 100644 --- a/tests/components/hitachi_ac424/common.yaml +++ b/tests/components/hitachi_ac424/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: hitachi_ac424 name: Hitachi Climate + transmitter_id: xmitr diff --git a/tests/components/hitachi_ac424/test.bk72xx-ard.yaml b/tests/components/hitachi_ac424/test.bk72xx-ard.yaml index 06e1aea364..6cce191825 100644 --- a/tests/components/hitachi_ac424/test.bk72xx-ard.yaml +++ b/tests/components/hitachi_ac424/test.bk72xx-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO6 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/bk72xx-ard.yaml <<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml b/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml +++ b/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp32-idf.yaml b/tests/components/hitachi_ac424/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/hitachi_ac424/test.esp32-idf.yaml +++ b/tests/components/hitachi_ac424/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp8266-ard.yaml b/tests/components/hitachi_ac424/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/hitachi_ac424/test.esp8266-ard.yaml +++ b/tests/components/hitachi_ac424/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/midea/common.yaml b/tests/components/midea/common.yaml index a0909401ff..fec85aee96 100644 --- a/tests/components/midea/common.yaml +++ b/tests/components/midea/common.yaml @@ -2,10 +2,6 @@ wifi: ssid: MySSID password: password1 -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: midea id: midea_unit @@ -16,7 +12,7 @@ climate: x.set_mode(CLIMATE_MODE_FAN_ONLY); on_state: - logger.log: State changed! - transmitter_id: + transmitter_id: xmitr period: 1s num_attempts: 5 timeout: 2s diff --git a/tests/components/midea/test.esp32-ard.yaml b/tests/components/midea/test.esp32-ard.yaml index b78163199a..1e3fe0ff51 100644 --- a/tests/components/midea/test.esp32-ard.yaml +++ b/tests/components/midea/test.esp32-ard.yaml @@ -1,7 +1,5 @@ -substitutions: - pin: GPIO2 - packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-ard.yaml uart: !include ../../test_build_components/common/uart/esp32-ard.yaml <<: !include common.yaml diff --git a/tests/components/midea/test.esp8266-ard.yaml b/tests/components/midea/test.esp8266-ard.yaml index dc276e274c..9825ff85a1 100644 --- a/tests/components/midea/test.esp8266-ard.yaml +++ b/tests/components/midea/test.esp8266-ard.yaml @@ -1,7 +1,5 @@ -substitutions: - pin: GPIO15 - packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/midea_ir/common.yaml b/tests/components/midea_ir/common.yaml index e8d89cecc2..e4cc4bb19c 100644 --- a/tests/components/midea_ir/common.yaml +++ b/tests/components/midea_ir/common.yaml @@ -1,8 +1,5 @@ -remote_transmitter: - pin: 4 - carrier_duty_percent: 50% - climate: - platform: midea_ir name: Midea IR use_fahrenheit: true + transmitter_id: xmitr diff --git a/tests/components/midea_ir/test.esp32-c3-idf.yaml b/tests/components/midea_ir/test.esp32-c3-idf.yaml index dade44d145..43d5343715 100644 --- a/tests/components/midea_ir/test.esp32-c3-idf.yaml +++ b/tests/components/midea_ir/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/midea_ir/test.esp32-idf.yaml b/tests/components/midea_ir/test.esp32-idf.yaml index dade44d145..e891f9dc85 100644 --- a/tests/components/midea_ir/test.esp32-idf.yaml +++ b/tests/components/midea_ir/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/midea_ir/test.esp8266-ard.yaml b/tests/components/midea_ir/test.esp8266-ard.yaml index dade44d145..4bed2f03e5 100644 --- a/tests/components/midea_ir/test.esp8266-ard.yaml +++ b/tests/components/midea_ir/test.esp8266-ard.yaml @@ -1 +1,4 @@ +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/mitsubishi/common.yaml b/tests/components/mitsubishi/common.yaml index c0fc959c5b..4a2deda163 100644 --- a/tests/components/mitsubishi/common.yaml +++ b/tests/components/mitsubishi/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 4 - carrier_duty_percent: 50% - climate: - platform: mitsubishi name: Mitsubishi + transmitter_id: xmitr diff --git a/tests/components/mitsubishi/test.esp32-c3-idf.yaml b/tests/components/mitsubishi/test.esp32-c3-idf.yaml index dade44d145..43d5343715 100644 --- a/tests/components/mitsubishi/test.esp32-c3-idf.yaml +++ b/tests/components/mitsubishi/test.esp32-c3-idf.yaml @@ -1 +1,4 @@ +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mitsubishi/test.esp32-idf.yaml b/tests/components/mitsubishi/test.esp32-idf.yaml index dade44d145..e891f9dc85 100644 --- a/tests/components/mitsubishi/test.esp32-idf.yaml +++ b/tests/components/mitsubishi/test.esp32-idf.yaml @@ -1 +1,4 @@ +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/mitsubishi/test.esp8266-ard.yaml b/tests/components/mitsubishi/test.esp8266-ard.yaml index dade44d145..4bed2f03e5 100644 --- a/tests/components/mitsubishi/test.esp8266-ard.yaml +++ b/tests/components/mitsubishi/test.esp8266-ard.yaml @@ -1 +1,4 @@ +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/noblex/common.yaml b/tests/components/noblex/common.yaml index f5e471a9a7..8053d84d4b 100644 --- a/tests/components/noblex/common.yaml +++ b/tests/components/noblex/common.yaml @@ -1,12 +1,3 @@ -remote_receiver: - id: rcvr - pin: 4 - dump: all - -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% - sensor: - platform: template id: noblex_ac_sensor diff --git a/tests/components/noblex/test.esp32-c3-idf.yaml b/tests/components/noblex/test.esp32-c3-idf.yaml index dade44d145..fe77c44eed 100644 --- a/tests/components/noblex/test.esp32-c3-idf.yaml +++ b/tests/components/noblex/test.esp32-c3-idf.yaml @@ -1 +1,5 @@ +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml + remote_receiver: !include ../../test_build_components/common/remote_receiver/esp32-c3-idf.yaml + <<: !include common.yaml diff --git a/tests/components/noblex/test.esp32-idf.yaml b/tests/components/noblex/test.esp32-idf.yaml index dade44d145..b241dbd159 100644 --- a/tests/components/noblex/test.esp32-idf.yaml +++ b/tests/components/noblex/test.esp32-idf.yaml @@ -1 +1,5 @@ +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml + remote_receiver: !include ../../test_build_components/common/remote_receiver/esp32-idf.yaml + <<: !include common.yaml diff --git a/tests/components/noblex/test.esp8266-ard.yaml b/tests/components/noblex/test.esp8266-ard.yaml index dade44d145..aa8651e556 100644 --- a/tests/components/noblex/test.esp8266-ard.yaml +++ b/tests/components/noblex/test.esp8266-ard.yaml @@ -1 +1,5 @@ +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml + remote_receiver: !include ../../test_build_components/common/remote_receiver/esp8266-ard.yaml + <<: !include common.yaml diff --git a/tests/components/prometheus/common.yaml b/tests/components/prometheus/common.yaml index 9a16088ba0..a9354ebe3c 100644 --- a/tests/components/prometheus/common.yaml +++ b/tests/components/prometheus/common.yaml @@ -128,13 +128,10 @@ valve: optimistic: true has_position: true -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: climate_ir_lg name: LG Climate + transmitter_id: xmitr prometheus: include_internal: true diff --git a/tests/components/prometheus/test.esp32-c3-idf.yaml b/tests/components/prometheus/test.esp32-c3-idf.yaml index f00bca5947..fedeaf822a 100644 --- a/tests/components/prometheus/test.esp32-c3-idf.yaml +++ b/tests/components/prometheus/test.esp32-c3-idf.yaml @@ -1,5 +1,7 @@ substitutions: verify_ssl: "false" - pin: GPIO2 + +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/prometheus/test.esp32-idf.yaml b/tests/components/prometheus/test.esp32-idf.yaml index d60caadb05..e590417623 100644 --- a/tests/components/prometheus/test.esp32-idf.yaml +++ b/tests/components/prometheus/test.esp32-idf.yaml @@ -1,8 +1,8 @@ substitutions: verify_ssl: "false" - pin: GPIO2 packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml spi: !include ../../test_build_components/common/spi/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/prometheus/test.esp8266-ard.yaml b/tests/components/prometheus/test.esp8266-ard.yaml index 6ee1831769..bae76751e8 100644 --- a/tests/components/prometheus/test.esp8266-ard.yaml +++ b/tests/components/prometheus/test.esp8266-ard.yaml @@ -1,5 +1,7 @@ substitutions: verify_ssl: "false" - pin: GPIO5 + +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/tcl112/common.yaml b/tests/components/tcl112/common.yaml index 0e43de4a4a..1074712f94 100644 --- a/tests/components/tcl112/common.yaml +++ b/tests/components/tcl112/common.yaml @@ -1,7 +1,3 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - sensor: - platform: template id: tcl112_sensor @@ -13,3 +9,4 @@ climate: supports_heat: true supports_cool: true sensor: tcl112_sensor + transmitter_id: xmitr diff --git a/tests/components/tcl112/test.esp32-c3-idf.yaml b/tests/components/tcl112/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/tcl112/test.esp32-c3-idf.yaml +++ b/tests/components/tcl112/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/tcl112/test.esp32-idf.yaml b/tests/components/tcl112/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/tcl112/test.esp32-idf.yaml +++ b/tests/components/tcl112/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/tcl112/test.esp8266-ard.yaml b/tests/components/tcl112/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/tcl112/test.esp8266-ard.yaml +++ b/tests/components/tcl112/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/toshiba/common.yaml b/tests/components/toshiba/common.yaml index 79a833980e..ba96c0628d 100644 --- a/tests/components/toshiba/common.yaml +++ b/tests/components/toshiba/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: toshiba name: Toshiba Climate + transmitter_id: xmitr diff --git a/tests/components/toshiba/test.esp32-c3-idf.yaml b/tests/components/toshiba/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/toshiba/test.esp32-c3-idf.yaml +++ b/tests/components/toshiba/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/toshiba/test.esp32-idf.yaml b/tests/components/toshiba/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/toshiba/test.esp32-idf.yaml +++ b/tests/components/toshiba/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/toshiba/test.esp8266-ard.yaml b/tests/components/toshiba/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/toshiba/test.esp8266-ard.yaml +++ b/tests/components/toshiba/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/whirlpool/common.yaml b/tests/components/whirlpool/common.yaml index 804c1aac26..6d55db1f08 100644 --- a/tests/components/whirlpool/common.yaml +++ b/tests/components/whirlpool/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: whirlpool name: Whirlpool Climate + transmitter_id: xmitr diff --git a/tests/components/whirlpool/test.esp32-c3-idf.yaml b/tests/components/whirlpool/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/whirlpool/test.esp32-c3-idf.yaml +++ b/tests/components/whirlpool/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/whirlpool/test.esp32-idf.yaml b/tests/components/whirlpool/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/whirlpool/test.esp32-idf.yaml +++ b/tests/components/whirlpool/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/whirlpool/test.esp8266-ard.yaml b/tests/components/whirlpool/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/whirlpool/test.esp8266-ard.yaml +++ b/tests/components/whirlpool/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/whynter/common.yaml b/tests/components/whynter/common.yaml index 04ad6bed54..63df11dd91 100644 --- a/tests/components/whynter/common.yaml +++ b/tests/components/whynter/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: whynter name: Whynter Climate + transmitter_id: xmitr diff --git a/tests/components/whynter/test.esp32-c3-idf.yaml b/tests/components/whynter/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/whynter/test.esp32-c3-idf.yaml +++ b/tests/components/whynter/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/whynter/test.esp32-idf.yaml b/tests/components/whynter/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/whynter/test.esp32-idf.yaml +++ b/tests/components/whynter/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/whynter/test.esp8266-ard.yaml b/tests/components/whynter/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/whynter/test.esp8266-ard.yaml +++ b/tests/components/whynter/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/yashima/common.yaml b/tests/components/yashima/common.yaml index bfe181f1a6..431c27ebb3 100644 --- a/tests/components/yashima/common.yaml +++ b/tests/components/yashima/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: yashima name: Yashima Climate + transmitter_id: xmitr diff --git a/tests/components/yashima/test.esp32-c3-idf.yaml b/tests/components/yashima/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/yashima/test.esp32-c3-idf.yaml +++ b/tests/components/yashima/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/yashima/test.esp32-idf.yaml b/tests/components/yashima/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/yashima/test.esp32-idf.yaml +++ b/tests/components/yashima/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/yashima/test.esp8266-ard.yaml b/tests/components/yashima/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/yashima/test.esp8266-ard.yaml +++ b/tests/components/yashima/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/components/zhlt01/common.yaml b/tests/components/zhlt01/common.yaml index 0adbe77325..d0fd531c87 100644 --- a/tests/components/zhlt01/common.yaml +++ b/tests/components/zhlt01/common.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: ${pin} - carrier_duty_percent: 50% - climate: - platform: zhlt01 name: ZH/LT-01 Climate + transmitter_id: xmitr diff --git a/tests/components/zhlt01/test.esp32-c3-idf.yaml b/tests/components/zhlt01/test.esp32-c3-idf.yaml index 7b012aa64c..43d5343715 100644 --- a/tests/components/zhlt01/test.esp32-c3-idf.yaml +++ b/tests/components/zhlt01/test.esp32-c3-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml <<: !include common.yaml diff --git a/tests/components/zhlt01/test.esp32-idf.yaml b/tests/components/zhlt01/test.esp32-idf.yaml index 7b012aa64c..e891f9dc85 100644 --- a/tests/components/zhlt01/test.esp32-idf.yaml +++ b/tests/components/zhlt01/test.esp32-idf.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO2 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml <<: !include common.yaml diff --git a/tests/components/zhlt01/test.esp8266-ard.yaml b/tests/components/zhlt01/test.esp8266-ard.yaml index f5097fcf5f..4bed2f03e5 100644 --- a/tests/components/zhlt01/test.esp8266-ard.yaml +++ b/tests/components/zhlt01/test.esp8266-ard.yaml @@ -1,4 +1,4 @@ -substitutions: - pin: GPIO5 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml <<: !include common.yaml diff --git a/tests/test_build_components/common/remote_receiver/esp32-c3-idf.yaml b/tests/test_build_components/common/remote_receiver/esp32-c3-idf.yaml new file mode 100644 index 0000000000..ad5acedf55 --- /dev/null +++ b/tests/test_build_components/common/remote_receiver/esp32-c3-idf.yaml @@ -0,0 +1,16 @@ +# Common remote_receiver configuration for ESP32-C3 IDF tests +# Provides a shared remote receiver that all components can use +# Components will auto-use this receiver if they don't specify receiver_id + +substitutions: + remote_receiver_pin: GPIO5 + +remote_receiver: + - id: rcvr + pin: ${remote_receiver_pin} + dump: all + tolerance: 25% + clock_resolution: 2000000 + filter_symbols: 2 + receive_symbols: 4 + rmt_symbols: 64 diff --git a/tests/test_build_components/common/remote_receiver/esp32-idf.yaml b/tests/test_build_components/common/remote_receiver/esp32-idf.yaml new file mode 100644 index 0000000000..2905e22233 --- /dev/null +++ b/tests/test_build_components/common/remote_receiver/esp32-idf.yaml @@ -0,0 +1,16 @@ +# Common remote_receiver configuration for ESP32 IDF tests +# Provides a shared remote receiver that all components can use +# Components will auto-use this receiver if they don't specify receiver_id + +substitutions: + remote_receiver_pin: GPIO32 + +remote_receiver: + - id: rcvr + pin: ${remote_receiver_pin} + dump: all + tolerance: 25% + clock_resolution: 2000000 + filter_symbols: 2 + receive_symbols: 4 + rmt_symbols: 64 diff --git a/tests/test_build_components/common/remote_receiver/esp8266-ard.yaml b/tests/test_build_components/common/remote_receiver/esp8266-ard.yaml new file mode 100644 index 0000000000..e2472d00c5 --- /dev/null +++ b/tests/test_build_components/common/remote_receiver/esp8266-ard.yaml @@ -0,0 +1,12 @@ +# Common remote_receiver configuration for ESP8266 Arduino tests +# Provides a shared remote receiver that all components can use +# Components will auto-use this receiver if they don't specify receiver_id + +substitutions: + remote_receiver_pin: GPIO5 + +remote_receiver: + id: rcvr + pin: ${remote_receiver_pin} + dump: all + tolerance: 25% diff --git a/tests/test_build_components/common/remote_transmitter/bk72xx-ard.yaml b/tests/test_build_components/common/remote_transmitter/bk72xx-ard.yaml new file mode 100644 index 0000000000..b951b8713f --- /dev/null +++ b/tests/test_build_components/common/remote_transmitter/bk72xx-ard.yaml @@ -0,0 +1,11 @@ +# Common remote_transmitter configuration for BK72XX Arduino tests +# Provides a shared remote transmitter that all components can use +# Components will auto-use this transmitter if they don't specify transmitter_id + +substitutions: + remote_transmitter_pin: GPIO6 + +remote_transmitter: + id: xmitr + pin: ${remote_transmitter_pin} + carrier_duty_percent: 50% diff --git a/tests/test_build_components/common/remote_transmitter/esp32-ard.yaml b/tests/test_build_components/common/remote_transmitter/esp32-ard.yaml new file mode 100644 index 0000000000..4378e328af --- /dev/null +++ b/tests/test_build_components/common/remote_transmitter/esp32-ard.yaml @@ -0,0 +1,11 @@ +# Common remote_transmitter configuration for ESP32 Arduino tests +# Provides a shared remote transmitter that all components can use +# Components will auto-use this transmitter if they don't specify transmitter_id + +substitutions: + remote_transmitter_pin: GPIO2 + +remote_transmitter: + id: xmitr + pin: ${remote_transmitter_pin} + carrier_duty_percent: 50% diff --git a/tests/test_build_components/common/remote_transmitter/esp32-c3-idf.yaml b/tests/test_build_components/common/remote_transmitter/esp32-c3-idf.yaml new file mode 100644 index 0000000000..b6b9a87fe1 --- /dev/null +++ b/tests/test_build_components/common/remote_transmitter/esp32-c3-idf.yaml @@ -0,0 +1,13 @@ +# Common remote_transmitter configuration for ESP32-C3 IDF tests +# Provides a shared remote transmitter that all components can use +# Components will auto-use this transmitter if they don't specify transmitter_id + +substitutions: + remote_transmitter_pin: GPIO2 + +remote_transmitter: + - id: xmitr + pin: ${remote_transmitter_pin} + carrier_duty_percent: 50% + clock_resolution: 2000000 + rmt_symbols: 64 diff --git a/tests/test_build_components/common/remote_transmitter/esp32-idf.yaml b/tests/test_build_components/common/remote_transmitter/esp32-idf.yaml new file mode 100644 index 0000000000..1d771b3edd --- /dev/null +++ b/tests/test_build_components/common/remote_transmitter/esp32-idf.yaml @@ -0,0 +1,13 @@ +# Common remote_transmitter configuration for ESP32 IDF tests +# Provides a shared remote transmitter that all components can use +# Components will auto-use this transmitter if they don't specify transmitter_id + +substitutions: + remote_transmitter_pin: GPIO2 + +remote_transmitter: + - id: xmitr + pin: ${remote_transmitter_pin} + carrier_duty_percent: 50% + clock_resolution: 2000000 + rmt_symbols: 64 diff --git a/tests/test_build_components/common/remote_transmitter/esp8266-ard.yaml b/tests/test_build_components/common/remote_transmitter/esp8266-ard.yaml new file mode 100644 index 0000000000..3be59c7997 --- /dev/null +++ b/tests/test_build_components/common/remote_transmitter/esp8266-ard.yaml @@ -0,0 +1,11 @@ +# Common remote_transmitter configuration for ESP8266 Arduino tests +# Provides a shared remote transmitter that all components can use +# Components will auto-use this transmitter if they don't specify transmitter_id + +substitutions: + remote_transmitter_pin: GPIO2 + +remote_transmitter: + id: xmitr + pin: ${remote_transmitter_pin} + carrier_duty_percent: 50% From d5c36eaf2a3f4cf020a77ce6cd8213b014038e7c Mon Sep 17 00:00:00 2001 From: Juan Antonio Aldea Date: Sat, 18 Oct 2025 09:40:54 +0200 Subject: [PATCH 124/526] [tests] Remove superfluous else-blocks from lambdas (#11322) Co-authored-by: J. Nick Koston --- .../components/absolute_humidity/common.yaml | 6 ++---- tests/components/analog_threshold/common.yaml | 3 +-- tests/components/binary_sensor/common.yaml | 3 +-- .../components/binary_sensor_map/common.yaml | 9 +++------ tests/components/combination/common.yaml | 6 ++---- tests/components/duty_time/common.yaml | 3 +-- tests/components/endstop/common.yaml | 3 +-- tests/components/lock/common.yaml | 3 +-- tests/components/mqtt/common.yaml | 20 +++++++------------ tests/components/pid/common.yaml | 3 +-- tests/components/prometheus/common.yaml | 18 ++++++----------- tests/components/template/common-base.yaml | 18 ++++++----------- .../batch_delay_zero_rapid_transitions.yaml | 11 +++++----- 13 files changed, 37 insertions(+), 69 deletions(-) diff --git a/tests/components/absolute_humidity/common.yaml b/tests/components/absolute_humidity/common.yaml index 87a99f5206..026f88654f 100644 --- a/tests/components/absolute_humidity/common.yaml +++ b/tests/components/absolute_humidity/common.yaml @@ -8,14 +8,12 @@ sensor: lambda: |- if (millis() > 10000) { return 0.6; - } else { - return 0.0; } + return 0.0; - platform: template id: template_temperature lambda: |- if (millis() > 10000) { return 42.0; - } else { - return 0.0; } + return 0.0; diff --git a/tests/components/analog_threshold/common.yaml b/tests/components/analog_threshold/common.yaml index 44d79756b5..26c401b92a 100644 --- a/tests/components/analog_threshold/common.yaml +++ b/tests/components/analog_threshold/common.yaml @@ -5,9 +5,8 @@ sensor: lambda: |- if (millis() > 10000) { return 42.0; - } else { - return 0.0; } + return 0.0; update_interval: 15s binary_sensor: diff --git a/tests/components/binary_sensor/common.yaml b/tests/components/binary_sensor/common.yaml index 2b4a006352..ed6322768f 100644 --- a/tests/components/binary_sensor/common.yaml +++ b/tests/components/binary_sensor/common.yaml @@ -23,9 +23,8 @@ binary_sensor: - lambda: |- if (id(some_binary_sensor).state) { return x; - } else { - return {}; } + return {}; - settle: 100ms - timeout: 10s diff --git a/tests/components/binary_sensor_map/common.yaml b/tests/components/binary_sensor_map/common.yaml index 2fed5ae515..c054022583 100644 --- a/tests/components/binary_sensor_map/common.yaml +++ b/tests/components/binary_sensor_map/common.yaml @@ -4,25 +4,22 @@ binary_sensor: lambda: |- if (millis() > 10000) { return true; - } else { - return false; } + return false; - platform: template id: bin2 lambda: |- if (millis() > 20000) { return true; - } else { - return false; } + return false; - platform: template id: bin3 lambda: |- if (millis() > 30000) { return true; - } else { - return false; } + return false; sensor: - platform: binary_sensor_map diff --git a/tests/components/combination/common.yaml b/tests/components/combination/common.yaml index 62246190af..0e5d512d08 100644 --- a/tests/components/combination/common.yaml +++ b/tests/components/combination/common.yaml @@ -4,17 +4,15 @@ sensor: lambda: |- if (millis() > 10000) { return 0.6; - } else { - return 0.0; } + return 0.0; - platform: template id: template_temperature2 lambda: |- if (millis() > 20000) { return 0.8; - } else { - return 0.0; } + return 0.0; - platform: combination type: kalman name: Kalman-filtered temperature diff --git a/tests/components/duty_time/common.yaml b/tests/components/duty_time/common.yaml index 28fa4afd1c..761d10f16a 100644 --- a/tests/components/duty_time/common.yaml +++ b/tests/components/duty_time/common.yaml @@ -4,9 +4,8 @@ binary_sensor: lambda: |- if (millis() > 10000) { return true; - } else { - return false; } + return false; sensor: - platform: duty_time diff --git a/tests/components/endstop/common.yaml b/tests/components/endstop/common.yaml index 341fbf7260..b92b1e13b9 100644 --- a/tests/components/endstop/common.yaml +++ b/tests/components/endstop/common.yaml @@ -4,9 +4,8 @@ binary_sensor: lambda: |- if (millis() > 10000) { return true; - } else { - return false; } + return false; switch: - platform: template diff --git a/tests/components/lock/common.yaml b/tests/components/lock/common.yaml index 1ee88a239a..9ba7f34857 100644 --- a/tests/components/lock/common.yaml +++ b/tests/components/lock/common.yaml @@ -17,9 +17,8 @@ lock: lambda: |- if (millis() > 10000) { return LOCK_STATE_LOCKED; - } else { - return LOCK_STATE_UNLOCKED; } + return LOCK_STATE_UNLOCKED; optimistic: true assumed_state: false on_unlock: diff --git a/tests/components/mqtt/common.yaml b/tests/components/mqtt/common.yaml index 1ab8872fdb..3f1b83bb01 100644 --- a/tests/components/mqtt/common.yaml +++ b/tests/components/mqtt/common.yaml @@ -72,10 +72,9 @@ binary_sensor: if (id(template_sens).state > 30) { // Garage Door is open. return true; - } else { - // Garage Door is closed. - return false; } + // Garage Door is closed. + return false; on_state: - mqtt.publish: topic: some/topic/binary_sensor @@ -217,9 +216,8 @@ cover: lambda: |- if (id(some_binary_sensor).state) { return COVER_OPEN; - } else { - return COVER_CLOSED; } + return COVER_CLOSED; open_action: - logger.log: open_action close_action: @@ -321,9 +319,8 @@ lock: lambda: |- if (id(some_binary_sensor).state) { return LOCK_STATE_LOCKED; - } else { - return LOCK_STATE_UNLOCKED; } + return LOCK_STATE_UNLOCKED; lock_action: - logger.log: lock_action unlock_action: @@ -360,9 +357,8 @@ sensor: lambda: |- if (id(some_binary_sensor).state) { return 42.0; - } else { - return 0.0; } + return 0.0; update_interval: 60s on_value: - mqtt.publish: @@ -390,9 +386,8 @@ switch: lambda: |- if (id(some_binary_sensor).state) { return true; - } else { - return false; } + return false; turn_on_action: - logger.log: turn_on_action turn_off_action: @@ -436,9 +431,8 @@ valve: lambda: |- if (id(some_binary_sensor).state) { return VALVE_OPEN; - } else { - return VALVE_CLOSED; } + return VALVE_CLOSED; alarm_control_panel: - platform: template diff --git a/tests/components/pid/common.yaml b/tests/components/pid/common.yaml index 5f7762872f..262e75591e 100644 --- a/tests/components/pid/common.yaml +++ b/tests/components/pid/common.yaml @@ -27,9 +27,8 @@ sensor: lambda: |- if (millis() > 10000) { return 42.0; - } else { - return 0.0; } + return 0.0; update_interval: 60s climate: diff --git a/tests/components/prometheus/common.yaml b/tests/components/prometheus/common.yaml index a9354ebe3c..cf46e882a7 100644 --- a/tests/components/prometheus/common.yaml +++ b/tests/components/prometheus/common.yaml @@ -35,9 +35,8 @@ sensor: lambda: |- if (millis() > 10000) { return 42.0; - } else { - return 0.0; } + return 0.0; update_interval: 60s text_sensor: @@ -49,9 +48,8 @@ text_sensor: lambda: |- if (millis() > 10000) { return {"Hello World"}; - } else { - return {"Goodbye (cruel) World"}; } + return {"Goodbye (cruel) World"}; update_interval: 60s binary_sensor: @@ -60,9 +58,8 @@ binary_sensor: lambda: |- if (millis() > 10000) { return true; - } else { - return false; } + return false; switch: - platform: template @@ -70,9 +67,8 @@ switch: lambda: |- if (millis() > 10000) { return true; - } else { - return false; } + return false; optimistic: true fan: @@ -85,9 +81,8 @@ cover: lambda: |- if (millis() > 10000) { return COVER_OPEN; - } else { - return COVER_CLOSED; } + return COVER_CLOSED; lock: - platform: template @@ -95,9 +90,8 @@ lock: lambda: |- if (millis() > 10000) { return LOCK_STATE_LOCKED; - } else { - return LOCK_STATE_UNLOCKED; } + return LOCK_STATE_UNLOCKED; optimistic: true select: diff --git a/tests/components/template/common-base.yaml b/tests/components/template/common-base.yaml index 48537d21bc..ea812532d4 100644 --- a/tests/components/template/common-base.yaml +++ b/tests/components/template/common-base.yaml @@ -59,9 +59,8 @@ binary_sensor: - lambda: |- if (id(other_binary_sensor).state) { return x; - } else { - return {}; } + return {}; - settle: 500ms - timeout: 5s @@ -72,9 +71,8 @@ sensor: lambda: |- if (id(some_binary_sensor).state) { return 42.0; - } else { - return 0.0; } + return 0.0; update_interval: 60s filters: - calibrate_linear: @@ -183,9 +181,8 @@ switch: lambda: |- if (id(some_binary_sensor).state) { return true; - } else { - return false; } + return false; turn_on_action: - logger.log: "turn_on_action" turn_off_action: @@ -203,9 +200,8 @@ cover: lambda: |- if (id(some_binary_sensor).state) { return COVER_OPEN; - } else { - return COVER_CLOSED; } + return COVER_CLOSED; open_action: - logger.log: open_action close_action: @@ -238,9 +234,8 @@ lock: lambda: |- if (id(some_binary_sensor).state) { return LOCK_STATE_LOCKED; - } else { - return LOCK_STATE_UNLOCKED; } + return LOCK_STATE_UNLOCKED; lock_action: - logger.log: lock_action unlock_action: @@ -255,9 +250,8 @@ valve: lambda: |- if (id(some_binary_sensor).state) { return VALVE_OPEN; - } else { - return VALVE_CLOSED; } + return VALVE_CLOSED; open_action: - logger.log: open_action close_action: diff --git a/tests/integration/fixtures/batch_delay_zero_rapid_transitions.yaml b/tests/integration/fixtures/batch_delay_zero_rapid_transitions.yaml index 32cacfaa79..f7b0fdcb63 100644 --- a/tests/integration/fixtures/batch_delay_zero_rapid_transitions.yaml +++ b/tests/integration/fixtures/batch_delay_zero_rapid_transitions.yaml @@ -34,10 +34,9 @@ binary_sensor: ESP_LOGD("test", "Button ON at %u", now); } return true; - } else { - // Only log state change - if (id(ir_remote_button).state) { - ESP_LOGD("test", "Button OFF at %u", now); - } - return false; } + // Only log state change + if (id(ir_remote_button).state) { + ESP_LOGD("test", "Button OFF at %u", now); + } + return false; From 91a10d0e3672f148aa1fcf88346285534ef09071 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 18 Oct 2025 02:36:30 -1000 Subject: [PATCH 125/526] [total_daily_energy] Fix ID conflicts in component test configuration (#11337) --- tests/components/total_daily_energy/common.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/components/total_daily_energy/common.yaml b/tests/components/total_daily_energy/common.yaml index ae4d30408b..dd7e648da6 100644 --- a/tests/components/total_daily_energy/common.yaml +++ b/tests/components/total_daily_energy/common.yaml @@ -17,10 +17,10 @@ sensor: name: HLW8012 Voltage power: name: HLW8012 Power - id: hlw8012_power + id: total_daily_energy_hlw8012_power energy: name: HLW8012 Energy - id: hlw8012_energy + id: total_daily_energy_hlw8012_energy update_interval: 15s current_resistor: 0.001 ohm voltage_divider: 2351 @@ -29,4 +29,4 @@ sensor: model: hlw8012 - platform: total_daily_energy name: HLW8012 Total Daily Energy - power_id: hlw8012_power + power_id: total_daily_energy_hlw8012_power From ae010fd6f18515dda58d39902ab49771ccb0a782 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sat, 18 Oct 2025 19:32:12 +0200 Subject: [PATCH 126/526] [dashboard] fix migration to Path (#11342) Co-authored-by: J. Nick Koston --- esphome/dashboard/web_server.py | 3 +- tests/dashboard/test_web_server.py | 194 +++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index a79c67c3d2..804a2b99af 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -1058,7 +1058,8 @@ class DownloadBinaryRequestHandler(BaseHandler): "download", f"{storage_json.name}-{file_name}", ) - path = storage_json.firmware_bin_path.with_name(file_name) + + path = storage_json.firmware_bin_path.parent.joinpath(file_name) if not path.is_file(): args = ["esphome", "idedata", settings.rel_path(configuration)] diff --git a/tests/dashboard/test_web_server.py b/tests/dashboard/test_web_server.py index 6c424e56d4..385841b1c8 100644 --- a/tests/dashboard/test_web_server.py +++ b/tests/dashboard/test_web_server.py @@ -35,6 +35,26 @@ from esphome.zeroconf import DiscoveredImport from .common import get_fixture_path +def get_build_path(base_path: Path, device_name: str) -> Path: + """Get the build directory path for a device. + + This is a test helper that constructs the standard ESPHome build directory + structure. Note: This helper does NOT perform path traversal sanitization + because it's only used in tests where we control the inputs. The actual + web_server.py code handles sanitization in DownloadBinaryRequestHandler.get() + via file_name.replace("..", "").lstrip("/"). + + Args: + base_path: The base temporary path (typically tmp_path from pytest) + device_name: The name of the device (should not contain path separators + in production use, but tests may use it for specific scenarios) + + Returns: + Path to the build directory (.esphome/build/device_name) + """ + return base_path / ".esphome" / "build" / device_name + + class DashboardTestHelper: def __init__(self, io_loop: IOLoop, client: AsyncHTTPClient, port: int) -> None: self.io_loop = io_loop @@ -417,6 +437,180 @@ async def test_download_binary_handler_idedata_fallback( assert response.body == b"bootloader content" +@pytest.mark.asyncio +@pytest.mark.usefixtures("mock_ext_storage_path") +async def test_download_binary_handler_subdirectory_file( + dashboard: DashboardTestHelper, + tmp_path: Path, + mock_storage_json: MagicMock, +) -> None: + """Test the DownloadBinaryRequestHandler.get with file in subdirectory (nRF52 case). + + This is a regression test for issue #11343 where the Path migration broke + downloads for nRF52 firmware files in subdirectories like 'zephyr/zephyr.uf2'. + + The issue was that with_name() doesn't accept path separators: + - Before: path = storage_json.firmware_bin_path.with_name(file_name) + ValueError: Invalid name 'zephyr/zephyr.uf2' + - After: path = storage_json.firmware_bin_path.parent.joinpath(file_name) + Works correctly with subdirectory paths + """ + # Create a fake nRF52 build structure with firmware in subdirectory + build_dir = get_build_path(tmp_path, "nrf52-device") + zephyr_dir = build_dir / "zephyr" + zephyr_dir.mkdir(parents=True) + + # Create the main firmware binary (would be in build root) + firmware_file = build_dir / "firmware.bin" + firmware_file.write_bytes(b"main firmware") + + # Create the UF2 file in zephyr subdirectory (nRF52 specific) + uf2_file = zephyr_dir / "zephyr.uf2" + uf2_file.write_bytes(b"nRF52 UF2 firmware content") + + # Mock storage JSON + mock_storage = Mock() + mock_storage.name = "nrf52-device" + mock_storage.firmware_bin_path = firmware_file + mock_storage_json.load.return_value = mock_storage + + # Request the UF2 file with subdirectory path + response = await dashboard.fetch( + "/download.bin?configuration=nrf52-device.yaml&file=zephyr/zephyr.uf2", + method="GET", + ) + assert response.code == 200 + assert response.body == b"nRF52 UF2 firmware content" + assert response.headers["Content-Type"] == "application/octet-stream" + assert "attachment" in response.headers["Content-Disposition"] + # Download name should be device-name + full file path + assert "nrf52-device-zephyr/zephyr.uf2" in response.headers["Content-Disposition"] + + +@pytest.mark.asyncio +@pytest.mark.usefixtures("mock_ext_storage_path") +async def test_download_binary_handler_subdirectory_file_url_encoded( + dashboard: DashboardTestHelper, + tmp_path: Path, + mock_storage_json: MagicMock, +) -> None: + """Test the DownloadBinaryRequestHandler.get with URL-encoded subdirectory path. + + Verifies that URL-encoded paths (e.g., zephyr%2Fzephyr.uf2) are correctly + decoded and handled, and that custom download names work with subdirectories. + """ + # Create a fake build structure with firmware in subdirectory + build_dir = get_build_path(tmp_path, "test") + zephyr_dir = build_dir / "zephyr" + zephyr_dir.mkdir(parents=True) + + firmware_file = build_dir / "firmware.bin" + firmware_file.write_bytes(b"content") + + uf2_file = zephyr_dir / "zephyr.uf2" + uf2_file.write_bytes(b"content") + + # Mock storage JSON + mock_storage = Mock() + mock_storage.name = "test_device" + mock_storage.firmware_bin_path = firmware_file + mock_storage_json.load.return_value = mock_storage + + # Request with URL-encoded path and custom download name + response = await dashboard.fetch( + "/download.bin?configuration=test.yaml&file=zephyr%2Fzephyr.uf2&download=custom_name.bin", + method="GET", + ) + assert response.code == 200 + assert "custom_name.bin" in response.headers["Content-Disposition"] + + +@pytest.mark.asyncio +@pytest.mark.usefixtures("mock_ext_storage_path") +@pytest.mark.parametrize( + "attack_path", + [ + pytest.param("../../../secrets.yaml", id="basic_traversal"), + pytest.param("..%2F..%2F..%2Fsecrets.yaml", id="url_encoded"), + pytest.param("zephyr/../../../secrets.yaml", id="traversal_with_prefix"), + pytest.param("/etc/passwd", id="absolute_path"), + pytest.param("//etc/passwd", id="double_slash_absolute"), + pytest.param("....//secrets.yaml", id="multiple_dots"), + ], +) +async def test_download_binary_handler_path_traversal_protection( + dashboard: DashboardTestHelper, + tmp_path: Path, + mock_storage_json: MagicMock, + attack_path: str, +) -> None: + """Test that DownloadBinaryRequestHandler prevents path traversal attacks. + + Verifies that attempts to use '..' in file paths are sanitized to prevent + accessing files outside the build directory. Tests multiple attack vectors. + """ + # Create build structure + build_dir = get_build_path(tmp_path, "test") + build_dir.mkdir(parents=True) + firmware_file = build_dir / "firmware.bin" + firmware_file.write_bytes(b"firmware content") + + # Create a sensitive file outside the build directory that should NOT be accessible + sensitive_file = tmp_path / "secrets.yaml" + sensitive_file.write_bytes(b"secret: my_secret_password") + + # Mock storage JSON + mock_storage = Mock() + mock_storage.name = "test_device" + mock_storage.firmware_bin_path = firmware_file + mock_storage_json.load.return_value = mock_storage + + # Attempt path traversal attack - should be blocked + with pytest.raises(HTTPClientError) as exc_info: + await dashboard.fetch( + f"/download.bin?configuration=test.yaml&file={attack_path}", + method="GET", + ) + # Should get 404 (file not found after sanitization) or 500 (idedata fails) + assert exc_info.value.code in (404, 500) + + +@pytest.mark.asyncio +@pytest.mark.usefixtures("mock_ext_storage_path") +async def test_download_binary_handler_multiple_subdirectory_levels( + dashboard: DashboardTestHelper, + tmp_path: Path, + mock_storage_json: MagicMock, +) -> None: + """Test downloading files from multiple subdirectory levels. + + Verifies that joinpath correctly handles multi-level paths like 'build/output/firmware.bin'. + """ + # Create nested directory structure + build_dir = get_build_path(tmp_path, "test") + nested_dir = build_dir / "build" / "output" + nested_dir.mkdir(parents=True) + + firmware_file = build_dir / "firmware.bin" + firmware_file.write_bytes(b"main") + + nested_file = nested_dir / "firmware.bin" + nested_file.write_bytes(b"nested firmware content") + + # Mock storage JSON + mock_storage = Mock() + mock_storage.name = "test_device" + mock_storage.firmware_bin_path = firmware_file + mock_storage_json.load.return_value = mock_storage + + response = await dashboard.fetch( + "/download.bin?configuration=test.yaml&file=build/output/firmware.bin", + method="GET", + ) + assert response.code == 200 + assert response.body == b"nested firmware content" + + @pytest.mark.asyncio async def test_edit_request_handler_post_invalid_file( dashboard: DashboardTestHelper, From 865663ce5f4f59301efccf4477f3efe76aec3d1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Oct 2025 11:48:25 -1000 Subject: [PATCH 127/526] Bump aioesphomeapi from 42.0.0 to 42.1.0 (#11350) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 92ab24e754..2f25905d94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==42.0.0 +aioesphomeapi==42.1.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import From acef2085d9fb1224df316271727ceaa1414bf332 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Oct 2025 23:11:36 +0000 Subject: [PATCH 128/526] Bump aioesphomeapi from 42.1.0 to 42.2.0 (#11352) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2f25905d94..ec7794c75a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==42.1.0 +aioesphomeapi==42.2.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import From c11a9bb97f6f14a4fc8b7b307110e38cff2fbda0 Mon Sep 17 00:00:00 2001 From: Spectre5 <31610422+Spectre5@users.noreply.github.com> Date: Sat, 18 Oct 2025 18:13:57 -0700 Subject: [PATCH 129/526] Change all temperature offsets to temperature_delta (#11347) --- esphome/components/bme680_bsec/__init__.py | 2 +- esphome/components/bme68x_bsec2/__init__.py | 2 +- esphome/components/scd4x/sensor.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/bme680_bsec/__init__.py b/esphome/components/bme680_bsec/__init__.py index 330dc4dd9c..8a8d74b5f3 100644 --- a/esphome/components/bme680_bsec/__init__.py +++ b/esphome/components/bme680_bsec/__init__.py @@ -41,7 +41,7 @@ CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(BME680BSECComponent), - cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature, + cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature_delta, cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum( IAQ_MODE_OPTIONS, upper=True ), diff --git a/esphome/components/bme68x_bsec2/__init__.py b/esphome/components/bme68x_bsec2/__init__.py index f4235b31b4..e421efb2d6 100644 --- a/esphome/components/bme68x_bsec2/__init__.py +++ b/esphome/components/bme68x_bsec2/__init__.py @@ -139,7 +139,7 @@ CONFIG_SCHEMA_BASE = ( cv.Optional(CONF_SUPPLY_VOLTAGE, default="3.3V"): cv.enum( VOLTAGE_OPTIONS, upper=True ), - cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature, + cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature_delta, cv.Optional( CONF_STATE_SAVE_INTERVAL, default="6hours" ): cv.positive_time_period_minutes, diff --git a/esphome/components/scd4x/sensor.py b/esphome/components/scd4x/sensor.py index 6b2188cd5a..ec90234ac3 100644 --- a/esphome/components/scd4x/sensor.py +++ b/esphome/components/scd4x/sensor.py @@ -81,7 +81,7 @@ CONFIG_SCHEMA = ( cv.int_range(min=0, max=0xFFFF, max_included=False), ), cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION): cv.pressure, - cv.Optional(CONF_TEMPERATURE_OFFSET, default="4°C"): cv.temperature, + cv.Optional(CONF_TEMPERATURE_OFFSET, default="4°C"): cv.temperature_delta, cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE): cv.use_id( sensor.Sensor ), From a33ed5e47b6ab889028650141e06053624b56e1b Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sat, 18 Oct 2025 22:25:53 -0500 Subject: [PATCH 130/526] [thermostat] Add humidity support (#11286) --- esphome/components/thermostat/climate.py | 59 +++++- .../thermostat/thermostat_climate.cpp | 168 +++++++++++++++++- .../thermostat/thermostat_climate.h | 53 +++++- tests/components/thermostat/common.yaml | 5 + 4 files changed, 278 insertions(+), 7 deletions(-) diff --git a/esphome/components/thermostat/climate.py b/esphome/components/thermostat/climate.py index 57abbc67b9..a928d208f3 100644 --- a/esphome/components/thermostat/climate.py +++ b/esphome/components/thermostat/climate.py @@ -71,9 +71,14 @@ from esphome.const import ( CONF_VISUAL, ) -CONF_PRESET_CHANGE = "preset_change" CONF_DEFAULT_PRESET = "default_preset" +CONF_HUMIDITY_CONTROL_DEHUMIDIFY_ACTION = "humidity_control_dehumidify_action" +CONF_HUMIDITY_CONTROL_HUMIDIFY_ACTION = "humidity_control_humidify_action" +CONF_HUMIDITY_CONTROL_OFF_ACTION = "humidity_control_off_action" +CONF_HUMIDITY_HYSTERESIS = "humidity_hysteresis" CONF_ON_BOOT_RESTORE_FROM = "on_boot_restore_from" +CONF_PRESET_CHANGE = "preset_change" +CONF_TARGET_HUMIDITY_CHANGE_ACTION = "target_humidity_change_action" CODEOWNERS = ["@kbx81"] @@ -241,6 +246,14 @@ def validate_thermostat(config): CONF_MAX_HEATING_RUN_TIME, CONF_SUPPLEMENTAL_HEATING_ACTION, ], + CONF_HUMIDITY_CONTROL_DEHUMIDIFY_ACTION: [ + CONF_HUMIDITY_CONTROL_OFF_ACTION, + CONF_HUMIDITY_SENSOR, + ], + CONF_HUMIDITY_CONTROL_HUMIDIFY_ACTION: [ + CONF_HUMIDITY_CONTROL_OFF_ACTION, + CONF_HUMIDITY_SENSOR, + ], } for config_trigger, req_triggers in requirements.items(): for req_trigger in req_triggers: @@ -338,7 +351,7 @@ def validate_thermostat(config): # Warn about using the removed CONF_DEFAULT_MODE and advise users if CONF_DEFAULT_MODE in config and config[CONF_DEFAULT_MODE] is not None: raise cv.Invalid( - f"{CONF_DEFAULT_MODE} is no longer valid. Please switch to using presets and specify a {CONF_DEFAULT_PRESET}." + f"{CONF_DEFAULT_MODE} is no longer valid. Please switch to using presets and specify a {CONF_DEFAULT_PRESET}" ) default_mode = config[CONF_DEFAULT_MODE] @@ -588,9 +601,24 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_SWING_VERTICAL_ACTION): automation.validate_automation( single=True ), + cv.Optional( + CONF_TARGET_HUMIDITY_CHANGE_ACTION + ): automation.validate_automation(single=True), cv.Optional( CONF_TARGET_TEMPERATURE_CHANGE_ACTION ): automation.validate_automation(single=True), + cv.Exclusive( + CONF_HUMIDITY_CONTROL_DEHUMIDIFY_ACTION, + group_of_exclusion="humidity_control", + ): automation.validate_automation(single=True), + cv.Exclusive( + CONF_HUMIDITY_CONTROL_HUMIDIFY_ACTION, + group_of_exclusion="humidity_control", + ): automation.validate_automation(single=True), + cv.Optional( + CONF_HUMIDITY_CONTROL_OFF_ACTION + ): automation.validate_automation(single=True), + cv.Optional(CONF_HUMIDITY_HYSTERESIS, default=1.0): cv.percentage, cv.Optional(CONF_DEFAULT_MODE, default=None): cv.valid, cv.Optional(CONF_DEFAULT_PRESET): cv.templatable(cv.string), cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, @@ -882,12 +910,39 @@ async def to_code(config): config[CONF_SWING_VERTICAL_ACTION], ) cg.add(var.set_supports_swing_mode_vertical(True)) + if CONF_TARGET_HUMIDITY_CHANGE_ACTION in config: + await automation.build_automation( + var.get_humidity_change_trigger(), + [], + config[CONF_TARGET_HUMIDITY_CHANGE_ACTION], + ) if CONF_TARGET_TEMPERATURE_CHANGE_ACTION in config: await automation.build_automation( var.get_temperature_change_trigger(), [], config[CONF_TARGET_TEMPERATURE_CHANGE_ACTION], ) + if CONF_HUMIDITY_CONTROL_DEHUMIDIFY_ACTION in config: + cg.add(var.set_supports_dehumidification(True)) + await automation.build_automation( + var.get_humidity_control_dehumidify_action_trigger(), + [], + config[CONF_HUMIDITY_CONTROL_DEHUMIDIFY_ACTION], + ) + if CONF_HUMIDITY_CONTROL_HUMIDIFY_ACTION in config: + cg.add(var.set_supports_humidification(True)) + await automation.build_automation( + var.get_humidity_control_humidify_action_trigger(), + [], + config[CONF_HUMIDITY_CONTROL_HUMIDIFY_ACTION], + ) + if CONF_HUMIDITY_CONTROL_OFF_ACTION in config: + await automation.build_automation( + var.get_humidity_control_off_action_trigger(), + [], + config[CONF_HUMIDITY_CONTROL_OFF_ACTION], + ) + cg.add(var.set_humidity_hysteresis(config[CONF_HUMIDITY_HYSTERESIS])) if CONF_PRESET in config: for preset_config in config[CONF_PRESET]: diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index 784b3cfb50..18efe3984e 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -32,6 +32,7 @@ void ThermostatClimate::setup() { if (this->humidity_sensor_ != nullptr) { this->humidity_sensor_->add_on_state_callback([this](float state) { this->current_humidity = state; + this->switch_to_humidity_control_action_(this->compute_humidity_control_action_()); this->publish_state(); }); this->current_humidity = this->humidity_sensor_->state; @@ -84,6 +85,8 @@ void ThermostatClimate::refresh() { this->switch_to_supplemental_action_(this->compute_supplemental_action_()); this->switch_to_fan_mode_(this->fan_mode.value(), false); this->switch_to_swing_mode_(this->swing_mode, false); + this->switch_to_humidity_control_action_(this->compute_humidity_control_action_()); + this->check_humidity_change_trigger_(); this->check_temperature_change_trigger_(); this->publish_state(); } @@ -129,6 +132,11 @@ bool ThermostatClimate::hysteresis_valid() { return true; } +bool ThermostatClimate::humidity_hysteresis_valid() { + return !std::isnan(this->humidity_hysteresis_) && this->humidity_hysteresis_ >= 0.0f && + this->humidity_hysteresis_ < 100.0f; +} + bool ThermostatClimate::limit_setpoints_for_heat_cool() { return this->mode == climate::CLIMATE_MODE_HEAT_COOL || (this->mode == climate::CLIMATE_MODE_AUTO && this->supports_heat_cool_); @@ -189,6 +197,16 @@ void ThermostatClimate::validate_target_temperature_high() { } } +void ThermostatClimate::validate_target_humidity() { + if (std::isnan(this->target_humidity)) { + this->target_humidity = + (this->get_traits().get_visual_max_humidity() - this->get_traits().get_visual_min_humidity()) / 2.0f; + } else { + this->target_humidity = clamp(this->target_humidity, this->get_traits().get_visual_min_humidity(), + this->get_traits().get_visual_max_humidity()); + } +} + void ThermostatClimate::control(const climate::ClimateCall &call) { bool target_temperature_high_changed = false; @@ -235,6 +253,10 @@ void ThermostatClimate::control(const climate::ClimateCall &call) { this->validate_target_temperature(); } } + if (call.get_target_humidity().has_value()) { + this->target_humidity = call.get_target_humidity().value(); + this->validate_target_humidity(); + } // make any changes happen this->refresh(); } @@ -250,6 +272,9 @@ climate::ClimateTraits ThermostatClimate::traits() { if (this->humidity_sensor_ != nullptr) traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY); + if (this->supports_humidification_ || this->supports_dehumidification_) + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY); + if (this->supports_auto_) traits.add_supported_mode(climate::CLIMATE_MODE_AUTO); if (this->supports_heat_cool_) @@ -423,6 +448,28 @@ climate::ClimateAction ThermostatClimate::compute_supplemental_action_() { return target_action; } +HumidificationAction ThermostatClimate::compute_humidity_control_action_() { + auto target_action = THERMOSTAT_HUMIDITY_CONTROL_ACTION_OFF; + // if hysteresis value or current_humidity is not valid, we go to OFF + if (std::isnan(this->current_humidity) || !this->humidity_hysteresis_valid()) { + return THERMOSTAT_HUMIDITY_CONTROL_ACTION_OFF; + } + + // ensure set point is valid before computing the action + this->validate_target_humidity(); + // everything has been validated so we can now safely compute the action + if (this->dehumidification_required_() && this->humidification_required_()) { + // this is bad and should never happen, so just stop. + // target_action = THERMOSTAT_HUMIDITY_CONTROL_ACTION_OFF; + } else if (this->supports_dehumidification_ && this->dehumidification_required_()) { + target_action = THERMOSTAT_HUMIDITY_CONTROL_ACTION_DEHUMIDIFY; + } else if (this->supports_humidification_ && this->humidification_required_()) { + target_action = THERMOSTAT_HUMIDITY_CONTROL_ACTION_HUMIDIFY; + } + + return target_action; +} + void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool publish_state) { // setup_complete_ helps us ensure an action is called immediately after boot if ((action == this->action) && this->setup_complete_) { @@ -596,6 +643,44 @@ void ThermostatClimate::trigger_supplemental_action_() { } } +void ThermostatClimate::switch_to_humidity_control_action_(HumidificationAction action) { + // setup_complete_ helps us ensure an action is called immediately after boot + if ((action == this->humidification_action_) && this->setup_complete_) { + // already in target mode + return; + } + + Trigger<> *trig = this->humidity_control_off_action_trigger_; + switch (action) { + case THERMOSTAT_HUMIDITY_CONTROL_ACTION_OFF: + // trig = this->humidity_control_off_action_trigger_; + ESP_LOGVV(TAG, "Switching to HUMIDIFICATION_OFF action"); + break; + case THERMOSTAT_HUMIDITY_CONTROL_ACTION_DEHUMIDIFY: + trig = this->humidity_control_dehumidify_action_trigger_; + ESP_LOGVV(TAG, "Switching to DEHUMIDIFY action"); + break; + case THERMOSTAT_HUMIDITY_CONTROL_ACTION_HUMIDIFY: + trig = this->humidity_control_humidify_action_trigger_; + ESP_LOGVV(TAG, "Switching to HUMIDIFY action"); + break; + case THERMOSTAT_HUMIDITY_CONTROL_ACTION_NONE: + default: + action = THERMOSTAT_HUMIDITY_CONTROL_ACTION_OFF; + // trig = this->humidity_control_off_action_trigger_; + } + + if (this->prev_humidity_control_trigger_ != nullptr) { + this->prev_humidity_control_trigger_->stop_action(); + this->prev_humidity_control_trigger_ = nullptr; + } + this->humidification_action_ = action; + this->prev_humidity_control_trigger_ = trig; + if (trig != nullptr) { + trig->trigger(); + } +} + void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode, bool publish_state) { // setup_complete_ helps us ensure an action is called immediately after boot if ((fan_mode == this->prev_fan_mode_) && this->setup_complete_) { @@ -887,6 +972,20 @@ void ThermostatClimate::idle_on_timer_callback_() { this->switch_to_supplemental_action_(this->compute_supplemental_action_()); } +void ThermostatClimate::check_humidity_change_trigger_() { + if ((this->prev_target_humidity_ == this->target_humidity) && this->setup_complete_) { + return; // nothing changed, no reason to trigger + } else { + // save the new temperature so we can check it again later; the trigger will fire below + this->prev_target_humidity_ = this->target_humidity; + } + // trigger the action + Trigger<> *trig = this->humidity_change_trigger_; + if (trig != nullptr) { + trig->trigger(); + } +} + void ThermostatClimate::check_temperature_change_trigger_() { if (this->supports_two_points_) { // setup_complete_ helps us ensure an action is called immediately after boot @@ -996,6 +1095,32 @@ bool ThermostatClimate::supplemental_heating_required_() { (this->supplemental_action_ == climate::CLIMATE_ACTION_HEATING)); } +bool ThermostatClimate::dehumidification_required_() { + if (this->current_humidity > this->target_humidity + this->humidity_hysteresis_) { + // if the current humidity exceeds the target + hysteresis, dehumidification is required + return true; + } else if (this->current_humidity < this->target_humidity - this->humidity_hysteresis_) { + // if the current humidity is less than the target - hysteresis, dehumidification should stop + return false; + } + // if we get here, the current humidity is between target + hysteresis and target - hysteresis, + // so the action should not change + return this->humidification_action_ == THERMOSTAT_HUMIDITY_CONTROL_ACTION_DEHUMIDIFY; +} + +bool ThermostatClimate::humidification_required_() { + if (this->current_humidity < this->target_humidity - this->humidity_hysteresis_) { + // if the current humidity is below the target - hysteresis, humidification is required + return true; + } else if (this->current_humidity > this->target_humidity + this->humidity_hysteresis_) { + // if the current humidity is above the target + hysteresis, humidification should stop + return false; + } + // if we get here, the current humidity is between target - hysteresis and target + hysteresis, + // so the action should not change + return this->humidification_action_ == THERMOSTAT_HUMIDITY_CONTROL_ACTION_HUMIDIFY; +} + void ThermostatClimate::dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config) { if (this->supports_heat_) { ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.1f°C", @@ -1152,8 +1277,12 @@ ThermostatClimate::ThermostatClimate() swing_mode_off_trigger_(new Trigger<>()), swing_mode_horizontal_trigger_(new Trigger<>()), swing_mode_vertical_trigger_(new Trigger<>()), + humidity_change_trigger_(new Trigger<>()), temperature_change_trigger_(new Trigger<>()), - preset_change_trigger_(new Trigger<>()) {} + preset_change_trigger_(new Trigger<>()), + humidity_control_dehumidify_action_trigger_(new Trigger<>()), + humidity_control_humidify_action_trigger_(new Trigger<>()), + humidity_control_off_action_trigger_(new Trigger<>()) {} void ThermostatClimate::set_default_preset(const std::string &custom_preset) { this->default_custom_preset_ = custom_preset; @@ -1217,6 +1346,9 @@ void ThermostatClimate::set_sensor(sensor::Sensor *sensor) { this->sensor_ = sen void ThermostatClimate::set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; } +void ThermostatClimate::set_humidity_hysteresis(float humidity_hysteresis) { + this->humidity_hysteresis_ = std::clamp(humidity_hysteresis, 0.0f, 100.0f); +} void ThermostatClimate::set_use_startup_delay(bool use_startup_delay) { this->use_startup_delay_ = use_startup_delay; } void ThermostatClimate::set_supports_heat_cool(bool supports_heat_cool) { this->supports_heat_cool_ = supports_heat_cool; @@ -1284,6 +1416,18 @@ void ThermostatClimate::set_supports_swing_mode_vertical(bool supports_swing_mod void ThermostatClimate::set_supports_two_points(bool supports_two_points) { this->supports_two_points_ = supports_two_points; } +void ThermostatClimate::set_supports_dehumidification(bool supports_dehumidification) { + this->supports_dehumidification_ = supports_dehumidification; + if (supports_dehumidification) { + this->supports_humidification_ = false; + } +} +void ThermostatClimate::set_supports_humidification(bool supports_humidification) { + this->supports_humidification_ = supports_humidification; + if (supports_humidification) { + this->supports_dehumidification_ = false; + } +} Trigger<> *ThermostatClimate::get_cool_action_trigger() const { return this->cool_action_trigger_; } Trigger<> *ThermostatClimate::get_supplemental_cool_action_trigger() const { @@ -1317,8 +1461,18 @@ Trigger<> *ThermostatClimate::get_swing_mode_both_trigger() const { return this- Trigger<> *ThermostatClimate::get_swing_mode_off_trigger() const { return this->swing_mode_off_trigger_; } Trigger<> *ThermostatClimate::get_swing_mode_horizontal_trigger() const { return this->swing_mode_horizontal_trigger_; } Trigger<> *ThermostatClimate::get_swing_mode_vertical_trigger() const { return this->swing_mode_vertical_trigger_; } +Trigger<> *ThermostatClimate::get_humidity_change_trigger() const { return this->humidity_change_trigger_; } Trigger<> *ThermostatClimate::get_temperature_change_trigger() const { return this->temperature_change_trigger_; } Trigger<> *ThermostatClimate::get_preset_change_trigger() const { return this->preset_change_trigger_; } +Trigger<> *ThermostatClimate::get_humidity_control_dehumidify_action_trigger() const { + return this->humidity_control_dehumidify_action_trigger_; +} +Trigger<> *ThermostatClimate::get_humidity_control_humidify_action_trigger() const { + return this->humidity_control_humidify_action_trigger_; +} +Trigger<> *ThermostatClimate::get_humidity_control_off_action_trigger() const { + return this->humidity_control_off_action_trigger_; +} void ThermostatClimate::dump_config() { LOG_CLIMATE("", "Thermostat", this); @@ -1422,7 +1576,12 @@ void ThermostatClimate::dump_config() { " OFF: %s\n" " HORIZONTAL: %s\n" " VERTICAL: %s\n" - " Supports TWO SET POINTS: %s", + " Supports TWO SET POINTS: %s\n" + " Supported Humidity Parameters:\n" + " CURRENT: %s\n" + " TARGET: %s\n" + " DEHUMIDIFICATION: %s\n" + " HUMIDIFICATION: %s", YESNO(this->supports_fan_mode_on_), YESNO(this->supports_fan_mode_off_), YESNO(this->supports_fan_mode_auto_), YESNO(this->supports_fan_mode_low_), YESNO(this->supports_fan_mode_medium_), YESNO(this->supports_fan_mode_high_), @@ -1430,7 +1589,10 @@ void ThermostatClimate::dump_config() { YESNO(this->supports_fan_mode_diffuse_), YESNO(this->supports_fan_mode_quiet_), YESNO(this->supports_swing_mode_both_), YESNO(this->supports_swing_mode_off_), YESNO(this->supports_swing_mode_horizontal_), YESNO(this->supports_swing_mode_vertical_), - YESNO(this->supports_two_points_)); + YESNO(this->supports_two_points_), + YESNO(this->get_traits().has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)), + YESNO(this->supports_dehumidification_ || this->supports_humidification_), + YESNO(this->supports_dehumidification_), YESNO(this->supports_humidification_)); if (!this->preset_config_.empty()) { ESP_LOGCONFIG(TAG, " Supported PRESETS:"); diff --git a/esphome/components/thermostat/thermostat_climate.h b/esphome/components/thermostat/thermostat_climate.h index 526f07116e..363d2b09fc 100644 --- a/esphome/components/thermostat/thermostat_climate.h +++ b/esphome/components/thermostat/thermostat_climate.h @@ -13,6 +13,13 @@ namespace esphome { namespace thermostat { +enum HumidificationAction : uint8_t { + THERMOSTAT_HUMIDITY_CONTROL_ACTION_OFF = 0, + THERMOSTAT_HUMIDITY_CONTROL_ACTION_DEHUMIDIFY = 1, + THERMOSTAT_HUMIDITY_CONTROL_ACTION_HUMIDIFY = 2, + THERMOSTAT_HUMIDITY_CONTROL_ACTION_NONE, +}; + enum ThermostatClimateTimerIndex : uint8_t { THERMOSTAT_TIMER_COOLING_MAX_RUN_TIME = 0, THERMOSTAT_TIMER_COOLING_OFF = 1, @@ -90,6 +97,7 @@ class ThermostatClimate : public climate::Climate, public Component { void set_idle_minimum_time_in_sec(uint32_t time); void set_sensor(sensor::Sensor *sensor); void set_humidity_sensor(sensor::Sensor *humidity_sensor); + void set_humidity_hysteresis(float humidity_hysteresis); void set_use_startup_delay(bool use_startup_delay); void set_supports_auto(bool supports_auto); void set_supports_heat_cool(bool supports_heat_cool); @@ -115,6 +123,8 @@ class ThermostatClimate : public climate::Climate, public Component { void set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal); void set_supports_swing_mode_off(bool supports_swing_mode_off); void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical); + void set_supports_dehumidification(bool supports_dehumidification); + void set_supports_humidification(bool supports_humidification); void set_supports_two_points(bool supports_two_points); void set_preset_config(climate::ClimatePreset preset, const ThermostatClimateTargetTempConfig &config); @@ -148,8 +158,12 @@ class ThermostatClimate : public climate::Climate, public Component { Trigger<> *get_swing_mode_horizontal_trigger() const; Trigger<> *get_swing_mode_off_trigger() const; Trigger<> *get_swing_mode_vertical_trigger() const; + Trigger<> *get_humidity_change_trigger() const; Trigger<> *get_temperature_change_trigger() const; Trigger<> *get_preset_change_trigger() const; + Trigger<> *get_humidity_control_dehumidify_action_trigger() const; + Trigger<> *get_humidity_control_humidify_action_trigger() const; + Trigger<> *get_humidity_control_off_action_trigger() const; /// Get current hysteresis values float cool_deadband(); float cool_overrun(); @@ -166,11 +180,13 @@ class ThermostatClimate : public climate::Climate, public Component { climate::ClimateFanMode locked_fan_mode(); /// Set point and hysteresis validation bool hysteresis_valid(); // returns true if valid + bool humidity_hysteresis_valid(); // returns true if valid bool limit_setpoints_for_heat_cool(); // returns true if set points should be further limited within visual range void validate_target_temperature(); void validate_target_temperatures(bool pin_target_temperature_high); void validate_target_temperature_low(); void validate_target_temperature_high(); + void validate_target_humidity(); protected: /// Override control to change settings of the climate device. @@ -192,11 +208,13 @@ class ThermostatClimate : public climate::Climate, public Component { /// Re-compute the required action of this climate controller. climate::ClimateAction compute_action_(bool ignore_timers = false); climate::ClimateAction compute_supplemental_action_(); + HumidificationAction compute_humidity_control_action_(); /// Switch the climate device to the given climate action. void switch_to_action_(climate::ClimateAction action, bool publish_state = true); void switch_to_supplemental_action_(climate::ClimateAction action); void trigger_supplemental_action_(); + void switch_to_humidity_control_action_(HumidificationAction action); /// Switch the climate device to the given climate fan mode. void switch_to_fan_mode_(climate::ClimateFanMode fan_mode, bool publish_state = true); @@ -207,6 +225,9 @@ class ThermostatClimate : public climate::Climate, public Component { /// Switch the climate device to the given climate swing mode. void switch_to_swing_mode_(climate::ClimateSwingMode swing_mode, bool publish_state = true); + /// Check if the humidity change trigger should be called. + void check_humidity_change_trigger_(); + /// Check if the temperature change trigger should be called. void check_temperature_change_trigger_(); @@ -243,6 +264,8 @@ class ThermostatClimate : public climate::Climate, public Component { bool heating_required_(); bool supplemental_cooling_required_(); bool supplemental_heating_required_(); + bool dehumidification_required_(); + bool humidification_required_(); void dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config); @@ -259,6 +282,9 @@ class ThermostatClimate : public climate::Climate, public Component { /// The current supplemental action climate::ClimateAction supplemental_action_{climate::CLIMATE_ACTION_OFF}; + /// The current humidification action + HumidificationAction humidification_action_{THERMOSTAT_HUMIDITY_CONTROL_ACTION_NONE}; + /// Default standard preset to use on start up climate::ClimatePreset default_preset_{}; @@ -321,6 +347,12 @@ class ThermostatClimate : public climate::Climate, public Component { /// A false value means that the controller has no such support. bool supports_two_points_{false}; + /// Whether the controller supports dehumidification and/or humidification + /// + /// A false value means that the controller has no such support. + bool supports_dehumidification_{false}; + bool supports_humidification_{false}; + /// Flags indicating if maximum allowable run time was exceeded bool cooling_max_runtime_exceeded_{false}; bool heating_max_runtime_exceeded_{false}; @@ -331,9 +363,10 @@ class ThermostatClimate : public climate::Climate, public Component { /// setup_complete_ blocks modifying/resetting the temps immediately after boot bool setup_complete_{false}; - /// Store previously-known temperatures + /// Store previously-known humidity and temperatures /// - /// These are used to determine when the temperature change trigger/action needs to be called + /// These are used to determine when a temperature/humidity has changed + float prev_target_humidity_{NAN}; float prev_target_temperature_{NAN}; float prev_target_temperature_low_{NAN}; float prev_target_temperature_high_{NAN}; @@ -347,6 +380,9 @@ class ThermostatClimate : public climate::Climate, public Component { float heating_deadband_{0}; float heating_overrun_{0}; + /// Hysteresis values used for computing humidification action + float humidity_hysteresis_{0}; + /// Maximum allowable temperature deltas before engaging supplemental cooling/heating actions float supplemental_cool_delta_{0}; float supplemental_heat_delta_{0}; @@ -448,12 +484,24 @@ class ThermostatClimate : public climate::Climate, public Component { /// The trigger to call when the controller should switch the swing mode to "vertical". Trigger<> *swing_mode_vertical_trigger_{nullptr}; + /// The trigger to call when the target humidity changes. + Trigger<> *humidity_change_trigger_{nullptr}; + /// The trigger to call when the target temperature(s) change(es). Trigger<> *temperature_change_trigger_{nullptr}; /// The trigger to call when the preset mode changes Trigger<> *preset_change_trigger_{nullptr}; + /// The trigger to call when dehumidification is required + Trigger<> *humidity_control_dehumidify_action_trigger_{nullptr}; + + /// The trigger to call when humidification is required + Trigger<> *humidity_control_humidify_action_trigger_{nullptr}; + + /// The trigger to call when (de)humidification should stop + Trigger<> *humidity_control_off_action_trigger_{nullptr}; + /// A reference to the trigger that was previously active. /// /// This is so that the previous trigger can be stopped before enabling a new one @@ -462,6 +510,7 @@ class ThermostatClimate : public climate::Climate, public Component { Trigger<> *prev_fan_mode_trigger_{nullptr}; Trigger<> *prev_mode_trigger_{nullptr}; Trigger<> *prev_swing_mode_trigger_{nullptr}; + Trigger<> *prev_humidity_control_trigger_{nullptr}; /// Default custom preset to use on start up std::string default_custom_preset_{}; diff --git a/tests/components/thermostat/common.yaml b/tests/components/thermostat/common.yaml index d630a93efc..4aa87c0ac3 100644 --- a/tests/components/thermostat/common.yaml +++ b/tests/components/thermostat/common.yaml @@ -69,6 +69,11 @@ climate: - logger.log: swing_vertical_action swing_both_action: - logger.log: swing_both_action + humidity_control_humidify_action: + - logger.log: humidity_control_humidify_action + humidity_control_off_action: + - logger.log: humidity_control_off_action + humidity_hysteresis: 1.0 startup_delay: true supplemental_cooling_delta: 2.0 cool_deadband: 0.5 From 590f6ff70b06de89dcd49c796283178504574a95 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 19 Oct 2025 01:20:11 -0500 Subject: [PATCH 131/526] [api] Update to use new climate API (#11357) --- esphome/components/api/api_connection.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 1f3456a205..ea1e130092 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -661,11 +661,12 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection ListEntitiesClimateResponse msg; auto traits = climate->get_traits(); // Flags set for backward compatibility, deprecated in 2025.11.0 - msg.supports_current_temperature = traits.get_supports_current_temperature(); - msg.supports_current_humidity = traits.get_supports_current_humidity(); - msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature(); - msg.supports_target_humidity = traits.get_supports_target_humidity(); - msg.supports_action = traits.get_supports_action(); + msg.supports_current_temperature = traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + msg.supports_current_humidity = traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY); + msg.supports_two_point_target_temperature = traits.has_feature_flags( + climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE); + msg.supports_target_humidity = traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY); + msg.supports_action = traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION); // Current feature flags and other supported parameters msg.feature_flags = traits.get_feature_flags(); msg.supported_modes = &traits.get_supported_modes_for_api_(); From 25f03074ab271f0f861548df4e2e634c67247410 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 19 Oct 2025 03:10:07 -0500 Subject: [PATCH 132/526] [web_server] Update to use new climate API (#11363) --- esphome/components/web_server/web_server.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index f18f21b16b..1d08ef5a35 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1325,7 +1325,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy); root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy); root["step"] = traits.get_visual_target_temperature_step(); - if (traits.get_supports_action()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action)); root["state"] = root["action"]; has_state = true; @@ -1345,14 +1345,15 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf if (traits.get_supports_swing_modes()) { root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode)); } - if (traits.get_supports_current_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { if (!std::isnan(obj->current_temperature)) { root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, current_accuracy); } else { root["current_temperature"] = "NA"; } } - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, target_accuracy); root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, target_accuracy); if (!has_state) { From b4d9fddd074481909824d3fe21b62323d912f5ca Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 19 Oct 2025 03:11:10 -0500 Subject: [PATCH 133/526] [mqtt] Update to use new climate API (#11360) --- esphome/components/mqtt/mqtt_climate.cpp | 30 ++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index e16f097812..a6f4e0a201 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -17,11 +17,11 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson auto traits = this->device_->get_traits(); // current_temperature_topic - if (traits.get_supports_current_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { root[MQTT_CURRENT_TEMPERATURE_TOPIC] = this->get_current_temperature_state_topic(); } // current_humidity_topic - if (traits.get_supports_current_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) { root[MQTT_CURRENT_HUMIDITY_TOPIC] = this->get_current_humidity_state_topic(); } // mode_command_topic @@ -45,7 +45,8 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo if (traits.supports_mode(CLIMATE_MODE_HEAT_COOL)) modes.add("heat_cool"); - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { // temperature_low_command_topic root[MQTT_TEMPERATURE_LOW_COMMAND_TOPIC] = this->get_target_temperature_low_command_topic(); // temperature_low_state_topic @@ -61,7 +62,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo root[MQTT_TEMPERATURE_STATE_TOPIC] = this->get_target_temperature_state_topic(); } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { // target_humidity_command_topic root[MQTT_TARGET_HUMIDITY_COMMAND_TOPIC] = this->get_target_humidity_command_topic(); // target_humidity_state_topic @@ -109,7 +110,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo presets.add(preset); } - if (traits.get_supports_action()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { // action_topic root[MQTT_ACTION_TOPIC] = this->get_action_state_topic(); } @@ -174,7 +175,8 @@ void MQTTClimateComponent::setup() { call.perform(); }); - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { this->subscribe(this->get_target_temperature_low_command_topic(), [this](const std::string &topic, const std::string &payload) { auto val = parse_number(payload); @@ -211,7 +213,7 @@ void MQTTClimateComponent::setup() { }); } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { this->subscribe(this->get_target_humidity_command_topic(), [this](const std::string &topic, const std::string &payload) { auto val = parse_number(payload); @@ -290,12 +292,14 @@ bool MQTTClimateComponent::publish_state_() { success = false; int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals(); int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals(); - if (traits.get_supports_current_temperature() && !std::isnan(this->device_->current_temperature)) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE) && + !std::isnan(this->device_->current_temperature)) { std::string payload = value_accuracy_to_string(this->device_->current_temperature, current_accuracy); if (!this->publish(this->get_current_temperature_state_topic(), payload)) success = false; } - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { std::string payload = value_accuracy_to_string(this->device_->target_temperature_low, target_accuracy); if (!this->publish(this->get_target_temperature_low_state_topic(), payload)) success = false; @@ -308,12 +312,14 @@ bool MQTTClimateComponent::publish_state_() { success = false; } - if (traits.get_supports_current_humidity() && !std::isnan(this->device_->current_humidity)) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY) && + !std::isnan(this->device_->current_humidity)) { std::string payload = value_accuracy_to_string(this->device_->current_humidity, 0); if (!this->publish(this->get_current_humidity_state_topic(), payload)) success = false; } - if (traits.get_supports_target_humidity() && !std::isnan(this->device_->target_humidity)) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY) && + !std::isnan(this->device_->target_humidity)) { std::string payload = value_accuracy_to_string(this->device_->target_humidity, 0); if (!this->publish(this->get_target_humidity_state_topic(), payload)) success = false; @@ -357,7 +363,7 @@ bool MQTTClimateComponent::publish_state_() { success = false; } - if (traits.get_supports_action()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { const char *payload; switch (this->device_->action) { case CLIMATE_ACTION_OFF: From ddf1b67e49bc8859d3fa472582582960a1fbd06c Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 19 Oct 2025 03:11:44 -0500 Subject: [PATCH 134/526] [prometheus] Update to use new climate API (#11361) --- esphome/components/prometheus/prometheus_handler.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 68ef18e5ce..6e7ed6f79f 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -916,7 +916,7 @@ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Clima auto min_temp_value = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy); climate_value_row_(stream, obj, area, node, friendly_name, min_temp, min_temp_value); // now check optional traits - if (traits.get_supports_current_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { std::string current_temp = "current_temperature"; if (std::isnan(obj->current_temperature)) { climate_failed_row_(stream, obj, area, node, friendly_name, current_temp, true); @@ -927,7 +927,7 @@ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Clima climate_failed_row_(stream, obj, area, node, friendly_name, current_temp, false); } } - if (traits.get_supports_current_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) { std::string current_humidity = "current_humidity"; if (std::isnan(obj->current_humidity)) { climate_failed_row_(stream, obj, area, node, friendly_name, current_humidity, true); @@ -938,7 +938,7 @@ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Clima climate_failed_row_(stream, obj, area, node, friendly_name, current_humidity, false); } } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { std::string target_humidity = "target_humidity"; if (std::isnan(obj->target_humidity)) { climate_failed_row_(stream, obj, area, node, friendly_name, target_humidity, true); @@ -949,7 +949,8 @@ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Clima climate_failed_row_(stream, obj, area, node, friendly_name, target_humidity, false); } } - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { std::string target_temp_low = "target_temperature_low"; auto target_temp_low_value = value_accuracy_to_string(obj->target_temperature_low, target_accuracy); climate_value_row_(stream, obj, area, node, friendly_name, target_temp_low, target_temp_low_value); @@ -961,7 +962,7 @@ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Clima auto target_temp_value = value_accuracy_to_string(obj->target_temperature, target_accuracy); climate_value_row_(stream, obj, area, node, friendly_name, target_temp, target_temp_value); } - if (traits.get_supports_action()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { std::string climate_trait_category = "action"; const auto *climate_trait_value = climate::climate_action_to_string(obj->action); climate_setting_row_(stream, obj, area, node, friendly_name, climate_trait_category, climate_trait_value); From f3cdbd0a05aa3e2969c596b92522c1d1095f4e76 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sun, 19 Oct 2025 19:39:48 +0200 Subject: [PATCH 135/526] [nrf52] fix task names in logs (#11367) --- esphome/components/logger/logger.h | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index 2099520049..dc8e06e0c9 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -68,6 +68,9 @@ static constexpr char LOG_LEVEL_LETTER_CHARS[] = { // Maximum header size: 35 bytes fixed + 32 bytes tag + 16 bytes thread name = 83 bytes (45 byte safety margin) static constexpr uint16_t MAX_HEADER_SIZE = 128; +// "0x" + 2 hex digits per byte + '\0' +static constexpr size_t MAX_POINTER_REPRESENTATION = 2 + sizeof(void *) * 2 + 1; + #if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) /** Enum for logging UART selection * @@ -177,8 +180,11 @@ class Logger : public Component { inline void HOT format_log_to_buffer_with_terminator_(uint8_t level, const char *tag, int line, const char *format, va_list args, char *buffer, uint16_t *buffer_at, uint16_t buffer_size) { -#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) +#if defined(USE_ESP32) || defined(USE_LIBRETINY) this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size); +#elif defined(USE_ZEPHYR) + char buff[MAX_POINTER_REPRESENTATION]; + this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(buff), buffer, buffer_at, buffer_size); #else this->write_header_to_buffer_(level, tag, line, nullptr, buffer, buffer_at, buffer_size); #endif @@ -277,7 +283,11 @@ class Logger : public Component { #endif #if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) - const char *HOT get_thread_name_() { + const char *HOT get_thread_name_( +#ifdef USE_ZEPHYR + char *buff +#endif + ) { #ifdef USE_ZEPHYR k_tid_t current_task = k_current_get(); #else @@ -291,7 +301,13 @@ class Logger : public Component { #elif defined(USE_LIBRETINY) return pcTaskGetTaskName(current_task); #elif defined(USE_ZEPHYR) - return k_thread_name_get(current_task); + const char *name = k_thread_name_get(current_task); + if (name) { + // zephyr print task names only if debug component is present + return name; + } + std::snprintf(buff, MAX_POINTER_REPRESENTATION, "%p", current_task); + return buff; #endif } } From 5e1019a6fa139a6839181153cdb9c3e293f94c56 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sun, 19 Oct 2025 19:41:19 +0200 Subject: [PATCH 136/526] [nrf52, ble_nus] add logging over BLE (#9846) --- CODEOWNERS | 1 + esphome/components/ble_nus/__init__.py | 29 ++++ esphome/components/ble_nus/ble_nus.cpp | 157 ++++++++++++++++++ esphome/components/ble_nus/ble_nus.h | 37 +++++ .../components/zephyr_ble_server/__init__.py | 34 ++++ .../zephyr_ble_server/ble_server.cpp | 100 +++++++++++ .../components/zephyr_ble_server/ble_server.h | 19 +++ script/helpers_zephyr.py | 1 + .../ble_nus/test.nrf52-adafruit.yaml | 2 + .../components/ble_nus/test.nrf52-mcumgr.yaml | 2 + 10 files changed, 382 insertions(+) create mode 100644 esphome/components/ble_nus/__init__.py create mode 100644 esphome/components/ble_nus/ble_nus.cpp create mode 100644 esphome/components/ble_nus/ble_nus.h create mode 100644 esphome/components/zephyr_ble_server/__init__.py create mode 100644 esphome/components/zephyr_ble_server/ble_server.cpp create mode 100644 esphome/components/zephyr_ble_server/ble_server.h create mode 100644 tests/components/ble_nus/test.nrf52-adafruit.yaml create mode 100644 tests/components/ble_nus/test.nrf52-mcumgr.yaml diff --git a/CODEOWNERS b/CODEOWNERS index b5cefa1e0c..09bd15137a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -70,6 +70,7 @@ esphome/components/bl0939/* @ziceva esphome/components/bl0940/* @dan-s-github @tobias- esphome/components/bl0942/* @dbuezas @dwmw2 esphome/components/ble_client/* @buxtronix @clydebarrow +esphome/components/ble_nus/* @tomaszduda23 esphome/components/bluetooth_proxy/* @bdraco @jesserockz esphome/components/bme280_base/* @esphome/core esphome/components/bme280_spi/* @apbodrov diff --git a/esphome/components/ble_nus/__init__.py b/esphome/components/ble_nus/__init__.py new file mode 100644 index 0000000000..9570005902 --- /dev/null +++ b/esphome/components/ble_nus/__init__.py @@ -0,0 +1,29 @@ +import esphome.codegen as cg +from esphome.components.zephyr import zephyr_add_prj_conf +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_LOGS, CONF_TYPE + +AUTO_LOAD = ["zephyr_ble_server"] +CODEOWNERS = ["@tomaszduda23"] + +ble_nus_ns = cg.esphome_ns.namespace("ble_nus") +BLENUS = ble_nus_ns.class_("BLENUS", cg.Component) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BLENUS), + cv.Optional(CONF_TYPE, default=CONF_LOGS): cv.one_of( + *[CONF_LOGS], lower=True + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_with_framework("zephyr"), +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + zephyr_add_prj_conf("BT_NUS", True) + cg.add(var.set_expose_log(config[CONF_TYPE] == CONF_LOGS)) + await cg.register_component(var, config) diff --git a/esphome/components/ble_nus/ble_nus.cpp b/esphome/components/ble_nus/ble_nus.cpp new file mode 100644 index 0000000000..9c4d0a3938 --- /dev/null +++ b/esphome/components/ble_nus/ble_nus.cpp @@ -0,0 +1,157 @@ +#ifdef USE_ZEPHYR +#include "ble_nus.h" +#include +#include +#include "esphome/core/log.h" +#ifdef USE_LOGGER +#include "esphome/components/logger/logger.h" +#include "esphome/core/application.h" +#endif +#include + +namespace esphome::ble_nus { + +constexpr size_t BLE_TX_BUF_SIZE = 2048; + +// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) +BLENUS *global_ble_nus; +RING_BUF_DECLARE(global_ble_tx_ring_buf, BLE_TX_BUF_SIZE); +// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) + +static const char *const TAG = "ble_nus"; + +size_t BLENUS::write_array(const uint8_t *data, size_t len) { + if (atomic_get(&this->tx_status_) == TX_DISABLED) { + return 0; + } + return ring_buf_put(&global_ble_tx_ring_buf, data, len); +} + +void BLENUS::connected(bt_conn *conn, uint8_t err) { + if (err == 0) { + global_ble_nus->conn_.store(bt_conn_ref(conn)); + } +} + +void BLENUS::disconnected(bt_conn *conn, uint8_t reason) { + if (global_ble_nus->conn_) { + bt_conn_unref(global_ble_nus->conn_.load()); + // Connection array is global static. + // Reference can be kept even if disconnected. + } +} + +void BLENUS::tx_callback(bt_conn *conn) { + atomic_cas(&global_ble_nus->tx_status_, TX_BUSY, TX_ENABLED); + ESP_LOGVV(TAG, "Sent operation completed"); +} + +void BLENUS::send_enabled_callback(bt_nus_send_status status) { + switch (status) { + case BT_NUS_SEND_STATUS_ENABLED: + atomic_set(&global_ble_nus->tx_status_, TX_ENABLED); +#ifdef USE_LOGGER + if (global_ble_nus->expose_log_) { + App.schedule_dump_config(); + } +#endif + ESP_LOGD(TAG, "NUS notification has been enabled"); + break; + case BT_NUS_SEND_STATUS_DISABLED: + atomic_set(&global_ble_nus->tx_status_, TX_DISABLED); + ESP_LOGD(TAG, "NUS notification has been disabled"); + break; + } +} + +void BLENUS::rx_callback(bt_conn *conn, const uint8_t *const data, uint16_t len) { + ESP_LOGD(TAG, "Received %d bytes.", len); +} + +void BLENUS::setup() { + bt_nus_cb callbacks = { + .received = rx_callback, + .sent = tx_callback, + .send_enabled = send_enabled_callback, + }; + + bt_nus_init(&callbacks); + + static bt_conn_cb conn_callbacks = { + .connected = BLENUS::connected, + .disconnected = BLENUS::disconnected, + }; + + bt_conn_cb_register(&conn_callbacks); + + global_ble_nus = this; +#ifdef USE_LOGGER + if (logger::global_logger != nullptr && this->expose_log_) { + logger::global_logger->add_on_log_callback( + [this](int level, const char *tag, const char *message, size_t message_len) { + this->write_array(reinterpret_cast(message), message_len); + const char c = '\n'; + this->write_array(reinterpret_cast(&c), 1); + }); + } + +#endif +} + +void BLENUS::dump_config() { + ESP_LOGCONFIG(TAG, "ble nus:"); + ESP_LOGCONFIG(TAG, " log: %s", YESNO(this->expose_log_)); + uint32_t mtu = 0; + bt_conn *conn = this->conn_.load(); + if (conn) { + mtu = bt_nus_get_mtu(conn); + } + ESP_LOGCONFIG(TAG, " MTU: %u", mtu); +} + +void BLENUS::loop() { + if (ring_buf_is_empty(&global_ble_tx_ring_buf)) { + return; + } + + if (!atomic_cas(&this->tx_status_, TX_ENABLED, TX_BUSY)) { + if (atomic_get(&this->tx_status_) == TX_DISABLED) { + ring_buf_reset(&global_ble_tx_ring_buf); + } + return; + } + + bt_conn *conn = this->conn_.load(); + if (conn) { + conn = bt_conn_ref(conn); + } + + if (nullptr == conn) { + atomic_cas(&this->tx_status_, TX_BUSY, TX_ENABLED); + return; + } + + uint32_t req_len = bt_nus_get_mtu(conn); + + uint8_t *buf; + uint32_t size = ring_buf_get_claim(&global_ble_tx_ring_buf, &buf, req_len); + + int err, err2; + + err = bt_nus_send(conn, buf, size); + err2 = ring_buf_get_finish(&global_ble_tx_ring_buf, size); + if (err2) { + // It should no happen. + ESP_LOGE(TAG, "Size %u exceeds valid bytes in the ring buffer (%d error)", size, err2); + } + if (err == 0) { + ESP_LOGVV(TAG, "Sent %d bytes", size); + } else { + ESP_LOGE(TAG, "Failed to send %d bytes (%d error)", size, err); + atomic_cas(&this->tx_status_, TX_BUSY, TX_ENABLED); + } + bt_conn_unref(conn); +} + +} // namespace esphome::ble_nus +#endif diff --git a/esphome/components/ble_nus/ble_nus.h b/esphome/components/ble_nus/ble_nus.h new file mode 100644 index 0000000000..e8cba32b4c --- /dev/null +++ b/esphome/components/ble_nus/ble_nus.h @@ -0,0 +1,37 @@ +#pragma once +#ifdef USE_ZEPHYR +#include "esphome/core/defines.h" +#include "esphome/core/component.h" +#include +#include + +namespace esphome::ble_nus { + +class BLENUS : public Component { + enum TxStatus { + TX_DISABLED, + TX_ENABLED, + TX_BUSY, + }; + + public: + void setup() override; + void dump_config() override; + void loop() override; + size_t write_array(const uint8_t *data, size_t len); + void set_expose_log(bool expose_log) { this->expose_log_ = expose_log; } + + protected: + static void send_enabled_callback(bt_nus_send_status status); + static void tx_callback(bt_conn *conn); + static void rx_callback(bt_conn *conn, const uint8_t *data, uint16_t len); + static void connected(bt_conn *conn, uint8_t err); + static void disconnected(bt_conn *conn, uint8_t reason); + + std::atomic conn_ = nullptr; + bool expose_log_ = false; + atomic_t tx_status_ = ATOMIC_INIT(TX_DISABLED); +}; + +} // namespace esphome::ble_nus +#endif diff --git a/esphome/components/zephyr_ble_server/__init__.py b/esphome/components/zephyr_ble_server/__init__.py new file mode 100644 index 0000000000..211941e984 --- /dev/null +++ b/esphome/components/zephyr_ble_server/__init__.py @@ -0,0 +1,34 @@ +import esphome.codegen as cg +from esphome.components.zephyr import zephyr_add_prj_conf +import esphome.config_validation as cv +from esphome.const import CONF_ESPHOME, CONF_ID, CONF_NAME, Framework +import esphome.final_validate as fv + +zephyr_ble_server_ns = cg.esphome_ns.namespace("zephyr_ble_server") +BLEServer = zephyr_ble_server_ns.class_("BLEServer", cg.Component) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BLEServer), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_with_framework(Framework.ZEPHYR), +) + + +def _final_validate(_): + full_config = fv.full_config.get() + zephyr_add_prj_conf("BT_DEVICE_NAME", full_config[CONF_ESPHOME][CONF_NAME]) + + +FINAL_VALIDATE_SCHEMA = _final_validate + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + zephyr_add_prj_conf("BT", True) + zephyr_add_prj_conf("BT_PERIPHERAL", True) + zephyr_add_prj_conf("BT_RX_STACK_SIZE", 1536) + # zephyr_add_prj_conf("BT_LL_SW_SPLIT", True) + await cg.register_component(var, config) diff --git a/esphome/components/zephyr_ble_server/ble_server.cpp b/esphome/components/zephyr_ble_server/ble_server.cpp new file mode 100644 index 0000000000..9f7e606a90 --- /dev/null +++ b/esphome/components/zephyr_ble_server/ble_server.cpp @@ -0,0 +1,100 @@ +#ifdef USE_ZEPHYR +#include "ble_server.h" +#include "esphome/core/defines.h" +#include "esphome/core/log.h" +#include +#include + +namespace esphome::zephyr_ble_server { + +static const char *const TAG = "zephyr_ble_server"; + +static struct k_work advertise_work; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +#define DEVICE_NAME CONFIG_BT_DEVICE_NAME +#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) + +static const struct bt_data AD[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), +}; + +static const struct bt_data SD[] = { +#ifdef USE_OTA + BT_DATA_BYTES(BT_DATA_UUID128_ALL, 0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86, 0xd3, 0x4c, 0xb7, 0x1d, 0x1d, + 0xdc, 0x53, 0x8d), +#endif +}; + +const struct bt_le_adv_param *const ADV_PARAM = BT_LE_ADV_CONN; + +static void advertise(struct k_work *work) { + int rc = bt_le_adv_stop(); + if (rc) { + ESP_LOGE(TAG, "Advertising failed to stop (rc %d)", rc); + } + + rc = bt_le_adv_start(ADV_PARAM, AD, ARRAY_SIZE(AD), SD, ARRAY_SIZE(SD)); + if (rc) { + ESP_LOGE(TAG, "Advertising failed to start (rc %d)", rc); + return; + } + ESP_LOGI(TAG, "Advertising successfully started"); +} + +static void connected(struct bt_conn *conn, uint8_t err) { + if (err) { + ESP_LOGE(TAG, "Connection failed (err 0x%02x)", err); + } else { + ESP_LOGI(TAG, "Connected"); + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) { + ESP_LOGI(TAG, "Disconnected (reason 0x%02x)", reason); + k_work_submit(&advertise_work); +} + +static void bt_ready(int err) { + if (err != 0) { + ESP_LOGE(TAG, "Bluetooth failed to initialise: %d", err); + } else { + k_work_submit(&advertise_work); + } +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = connected, + .disconnected = disconnected, +}; + +void BLEServer::setup() { + k_work_init(&advertise_work, advertise); + resume_(); +} + +void BLEServer::loop() { + if (this->suspended_) { + resume_(); + this->suspended_ = false; + } +} + +void BLEServer::resume_() { + int rc = bt_enable(bt_ready); + if (rc != 0) { + ESP_LOGE(TAG, "Bluetooth enable failed: %d", rc); + return; + } +} + +void BLEServer::on_shutdown() { + struct k_work_sync sync; + k_work_cancel_sync(&advertise_work, &sync); + bt_disable(); + this->suspended_ = true; +} + +} // namespace esphome::zephyr_ble_server + +#endif diff --git a/esphome/components/zephyr_ble_server/ble_server.h b/esphome/components/zephyr_ble_server/ble_server.h new file mode 100644 index 0000000000..1b32e9b58c --- /dev/null +++ b/esphome/components/zephyr_ble_server/ble_server.h @@ -0,0 +1,19 @@ +#pragma once +#ifdef USE_ZEPHYR +#include "esphome/core/component.h" + +namespace esphome::zephyr_ble_server { + +class BLEServer : public Component { + public: + void setup() override; + void loop() override; + void on_shutdown() override; + + protected: + void resume_(); + bool suspended_ = false; +}; + +} // namespace esphome::zephyr_ble_server +#endif diff --git a/script/helpers_zephyr.py b/script/helpers_zephyr.py index 922f1171b4..f72b335e64 100644 --- a/script/helpers_zephyr.py +++ b/script/helpers_zephyr.py @@ -25,6 +25,7 @@ int main() { return 0;} Path(zephyr_dir / "prj.conf").write_text( """ CONFIG_NEWLIB_LIBC=y +CONFIG_BT=y CONFIG_ADC=y """, encoding="utf-8", diff --git a/tests/components/ble_nus/test.nrf52-adafruit.yaml b/tests/components/ble_nus/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..20eec16956 --- /dev/null +++ b/tests/components/ble_nus/test.nrf52-adafruit.yaml @@ -0,0 +1,2 @@ +ble_nus: + type: logs diff --git a/tests/components/ble_nus/test.nrf52-mcumgr.yaml b/tests/components/ble_nus/test.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..20eec16956 --- /dev/null +++ b/tests/components/ble_nus/test.nrf52-mcumgr.yaml @@ -0,0 +1,2 @@ +ble_nus: + type: logs From 40823df7bc5ac6818c6b2513151a34a3eee2ac92 Mon Sep 17 00:00:00 2001 From: Juan Antonio Aldea Date: Sun, 19 Oct 2025 19:47:31 +0200 Subject: [PATCH 137/526] make types sensors_t and sensor_type_t internal to StatsdComponent. (#11345) --- esphome/components/statsd/statsd.h | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/esphome/components/statsd/statsd.h b/esphome/components/statsd/statsd.h index 34f84cbe00..eab77a7a6e 100644 --- a/esphome/components/statsd/statsd.h +++ b/esphome/components/statsd/statsd.h @@ -28,21 +28,6 @@ namespace esphome { namespace statsd { -using sensor_type_t = enum { TYPE_SENSOR, TYPE_BINARY_SENSOR }; - -using sensors_t = struct { - const char *name; - sensor_type_t type; - union { -#ifdef USE_SENSOR - esphome::sensor::Sensor *sensor; -#endif -#ifdef USE_BINARY_SENSOR - esphome::binary_sensor::BinarySensor *binary_sensor; -#endif - }; -}; - class StatsdComponent : public PollingComponent { public: ~StatsdComponent(); @@ -71,6 +56,20 @@ class StatsdComponent : public PollingComponent { const char *prefix_; uint16_t port_; + using sensor_type_t = enum { TYPE_SENSOR, TYPE_BINARY_SENSOR }; + using sensors_t = struct { + const char *name; + sensor_type_t type; + union { +#ifdef USE_SENSOR + esphome::sensor::Sensor *sensor; +#endif +#ifdef USE_BINARY_SENSOR + esphome::binary_sensor::BinarySensor *binary_sensor; +#endif + }; + }; + std::vector sensors_; #ifdef USE_ESP8266 From 5db07c2d708c2eb15a1fa7f032735ac1fc907d66 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 08:31:40 -1000 Subject: [PATCH 138/526] [api][time] Refactor timezone update logic for cleaner code (#11327) --- esphome/components/api/api_connection.cpp | 9 ++------- esphome/components/time/real_time_clock.h | 8 ++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index ea1e130092..7dfefedd54 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1082,13 +1082,8 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) { homeassistant::global_homeassistant_time->set_epoch_time(value.epoch_seconds); #ifdef USE_TIME_TIMEZONE if (value.timezone_len > 0) { - const std::string ¤t_tz = homeassistant::global_homeassistant_time->get_timezone(); - // Compare without allocating a string - if (current_tz.length() != value.timezone_len || - memcmp(current_tz.c_str(), value.timezone, value.timezone_len) != 0) { - homeassistant::global_homeassistant_time->set_timezone( - std::string(reinterpret_cast(value.timezone), value.timezone_len)); - } + homeassistant::global_homeassistant_time->set_timezone(reinterpret_cast(value.timezone), + value.timezone_len); } #endif } diff --git a/esphome/components/time/real_time_clock.h b/esphome/components/time/real_time_clock.h index 4b98a88975..7e60bbd234 100644 --- a/esphome/components/time/real_time_clock.h +++ b/esphome/components/time/real_time_clock.h @@ -27,6 +27,14 @@ class RealTimeClock : public PollingComponent { this->apply_timezone_(); } + /// Set the time zone from raw buffer, only if it differs from the current one. + void set_timezone(const char *tz, size_t len) { + if (this->timezone_.length() != len || memcmp(this->timezone_.c_str(), tz, len) != 0) { + this->timezone_.assign(tz, len); + this->apply_timezone_(); + } + } + /// Get the time zone currently in use. std::string get_timezone() { return this->timezone_; } #endif From f25af18655d626824fab1951041ac57b15accbde Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 08:34:34 -1000 Subject: [PATCH 139/526] [scheduler] Replace defer queue deque with vector to avoid 512-byte upfront allocation (#11305) --- esphome/core/scheduler.cpp | 36 +++++++++++++++++++------ esphome/core/scheduler.h | 54 ++++++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 402084f306..0d4715f621 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -328,17 +328,30 @@ void HOT Scheduler::call(uint32_t now) { // Single-core platforms don't use this queue and fall back to the heap-based approach. // // Note: Items cancelled via cancel_item_locked_() are marked with remove=true but still - // processed here. They are removed from the queue normally via pop_front() but skipped - // during execution by should_skip_item_(). This is intentional - no memory leak occurs. - while (!this->defer_queue_.empty()) { - // The outer check is done without a lock for performance. If the queue - // appears non-empty, we lock and process an item. We don't need to check - // empty() again inside the lock because only this thread can remove items. + // processed here. They are skipped during execution by should_skip_item_(). + // This is intentional - no memory leak occurs. + // + // We use an index (defer_queue_front_) to track the read position instead of calling + // erase() on every pop, which would be O(n). The queue is processed once per loop - + // any items added during processing are left for the next loop iteration. + + // Snapshot the queue end point - only process items that existed at loop start + // Items added during processing (by callbacks or other threads) run next loop + // No lock needed: single consumer (main loop), stale read just means we process less this iteration + size_t defer_queue_end = this->defer_queue_.size(); + + while (this->defer_queue_front_ < defer_queue_end) { std::unique_ptr item; { LockGuard lock(this->lock_); - item = std::move(this->defer_queue_.front()); - this->defer_queue_.pop_front(); + // SAFETY: Moving out the unique_ptr leaves a nullptr in the vector at defer_queue_front_. + // This is intentional and safe because: + // 1. The vector is only cleaned up by cleanup_defer_queue_locked_() at the end of this function + // 2. Any code iterating defer_queue_ MUST check for nullptr items (see mark_matching_items_removed_ + // and has_cancelled_timeout_in_container_ in scheduler.h) + // 3. The lock protects concurrent access, but the nullptr remains until cleanup + item = std::move(this->defer_queue_[this->defer_queue_front_]); + this->defer_queue_front_++; } // Execute callback without holding lock to prevent deadlocks @@ -349,6 +362,13 @@ void HOT Scheduler::call(uint32_t now) { // Recycle the defer item after execution this->recycle_item_(std::move(item)); } + + // If we've consumed all items up to the snapshot point, clean up the dead space + // Single consumer (main loop), so no lock needed for this check + if (this->defer_queue_front_ >= defer_queue_end) { + LockGuard lock(this->lock_); + this->cleanup_defer_queue_locked_(); + } #endif /* not ESPHOME_THREAD_SINGLE */ // Convert the fresh timestamp from main loop to 64-bit for scheduler operations diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index 2237915e07..ad0ec0284e 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -264,6 +264,36 @@ class Scheduler { // Helper to recycle a SchedulerItem void recycle_item_(std::unique_ptr item); +#ifndef ESPHOME_THREAD_SINGLE + // Helper to cleanup defer_queue_ after processing + // IMPORTANT: Caller must hold the scheduler lock before calling this function. + inline void cleanup_defer_queue_locked_() { + // Check if new items were added by producers during processing + if (this->defer_queue_front_ >= this->defer_queue_.size()) { + // Common case: no new items - clear everything + this->defer_queue_.clear(); + } else { + // Rare case: new items were added during processing - compact the vector + // This only happens when: + // 1. A deferred callback calls defer() again, or + // 2. Another thread calls defer() while we're processing + // + // Move unprocessed items (added during this loop) to the front for next iteration + // + // SAFETY: Compacted items may include cancelled items (marked for removal via + // cancel_item_locked_() during execution). This is safe because should_skip_item_() + // checks is_item_removed_() before executing, so cancelled items will be skipped + // and recycled on the next loop iteration. + size_t remaining = this->defer_queue_.size() - this->defer_queue_front_; + for (size_t i = 0; i < remaining; i++) { + this->defer_queue_[i] = std::move(this->defer_queue_[this->defer_queue_front_ + i]); + } + this->defer_queue_.resize(remaining); + } + this->defer_queue_front_ = 0; + } +#endif /* not ESPHOME_THREAD_SINGLE */ + // Helper to check if item is marked for removal (platform-specific) // Returns true if item should be skipped, handles platform-specific synchronization // For ESPHOME_THREAD_MULTI_NO_ATOMICS platforms, the caller must hold the scheduler lock before calling this @@ -282,13 +312,18 @@ class Scheduler { // Helper to mark matching items in a container as removed // Returns the number of items marked for removal - // For ESPHOME_THREAD_MULTI_NO_ATOMICS platforms, the caller must hold the scheduler lock before calling this - // function. + // IMPORTANT: Caller must hold the scheduler lock before calling this function. template size_t mark_matching_items_removed_(Container &container, Component *component, const char *name_cstr, SchedulerItem::Type type, bool match_retry) { size_t count = 0; for (auto &item : container) { + // Skip nullptr items (can happen in defer_queue_ when items are being processed) + // The defer_queue_ uses index-based processing: items are std::moved out but left in the + // vector as nullptr until cleanup. Even though this function is called with lock held, + // the vector can still contain nullptr items from the processing loop. This check prevents crashes. + if (!item) + continue; if (this->matches_item_(item, component, name_cstr, type, match_retry)) { // Mark item for removal (platform-specific) #ifdef ESPHOME_THREAD_MULTI_ATOMICS @@ -311,6 +346,12 @@ class Scheduler { bool has_cancelled_timeout_in_container_(const Container &container, Component *component, const char *name_cstr, bool match_retry) const { for (const auto &item : container) { + // Skip nullptr items (can happen in defer_queue_ when items are being processed) + // The defer_queue_ uses index-based processing: items are std::moved out but left in the + // vector as nullptr until cleanup. If this function is called during defer queue processing, + // it will iterate over these nullptr items. This check prevents crashes. + if (!item) + continue; if (is_item_removed_(item.get()) && this->matches_item_(item, component, name_cstr, SchedulerItem::TIMEOUT, match_retry, /* skip_removed= */ false)) { @@ -324,9 +365,12 @@ class Scheduler { std::vector> items_; std::vector> to_add_; #ifndef ESPHOME_THREAD_SINGLE - // Single-core platforms don't need the defer queue and save 40 bytes of RAM - std::deque> defer_queue_; // FIFO queue for defer() calls -#endif /* ESPHOME_THREAD_SINGLE */ + // Single-core platforms don't need the defer queue and save ~32 bytes of RAM + // Using std::vector instead of std::deque avoids 512-byte chunked allocations + // Index tracking avoids O(n) erase() calls when draining the queue each loop + std::vector> defer_queue_; // FIFO queue for defer() calls + size_t defer_queue_front_{0}; // Index of first valid item in defer_queue_ (tracks consumed items) +#endif /* ESPHOME_THREAD_SINGLE */ uint32_t to_remove_{0}; // Memory pool for recycling SchedulerItem objects to reduce heap churn. From a0922bc8b0d1bf6260b946a442cfea3253a776f5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 08:43:38 -1000 Subject: [PATCH 140/526] [ci] Add automated memory impact analysis for pull requests (#11242) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .coveragerc | 1 + .github/workflows/ci.yml | 291 ++++++ esphome/__main__.py | 4 +- esphome/analyze_memory/__init__.py | 502 ++++++++++ esphome/analyze_memory/__main__.py | 6 + esphome/analyze_memory/cli.py | 408 ++++++++ esphome/analyze_memory/const.py | 903 ++++++++++++++++++ esphome/analyze_memory/helpers.py | 121 +++ esphome/platformio_api.py | 20 + script/analyze_component_buses.py | 14 +- script/ci_helpers.py | 23 + script/ci_memory_impact_comment.py | 570 +++++++++++ script/ci_memory_impact_extract.py | 281 ++++++ script/determine-jobs.py | 191 +++- script/helpers.py | 79 +- script/list-components.py | 10 +- script/split_components_for_ci.py | 10 +- .../ci_memory_impact_comment_template.j2 | 27 + .../ci_memory_impact_component_breakdown.j2 | 15 + script/templates/ci_memory_impact_macros.j2 | 8 + .../ci_memory_impact_symbol_changes.j2 | 51 + script/test_build_components.py | 21 +- tests/script/test_determine_jobs.py | 229 ++++- tests/unit_tests/test_platformio_api.py | 36 + 24 files changed, 3772 insertions(+), 49 deletions(-) create mode 100644 esphome/analyze_memory/__init__.py create mode 100644 esphome/analyze_memory/__main__.py create mode 100644 esphome/analyze_memory/cli.py create mode 100644 esphome/analyze_memory/const.py create mode 100644 esphome/analyze_memory/helpers.py create mode 100755 script/ci_helpers.py create mode 100755 script/ci_memory_impact_comment.py create mode 100755 script/ci_memory_impact_extract.py create mode 100644 script/templates/ci_memory_impact_comment_template.j2 create mode 100644 script/templates/ci_memory_impact_component_breakdown.j2 create mode 100644 script/templates/ci_memory_impact_macros.j2 create mode 100644 script/templates/ci_memory_impact_symbol_changes.j2 diff --git a/.coveragerc b/.coveragerc index f23592be24..c15e79a31b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,4 +1,5 @@ [run] omit = esphome/components/* + esphome/analyze_memory/* tests/integration/* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87e182fe4d..42f934de9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -175,6 +175,7 @@ jobs: changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }} directly-changed-components-with-tests: ${{ steps.determine.outputs.directly-changed-components-with-tests }} component-test-count: ${{ steps.determine.outputs.component-test-count }} + memory_impact: ${{ steps.determine.outputs.memory-impact }} steps: - name: Check out code from GitHub uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -204,6 +205,7 @@ jobs: echo "changed-components-with-tests=$(echo "$output" | jq -c '.changed_components_with_tests')" >> $GITHUB_OUTPUT echo "directly-changed-components-with-tests=$(echo "$output" | jq -c '.directly_changed_components_with_tests')" >> $GITHUB_OUTPUT echo "component-test-count=$(echo "$output" | jq -r '.component_test_count')" >> $GITHUB_OUTPUT + echo "memory-impact=$(echo "$output" | jq -c '.memory_impact')" >> $GITHUB_OUTPUT integration-tests: name: Run integration tests @@ -521,6 +523,292 @@ jobs: - uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0 if: always() + memory-impact-target-branch: + name: Build target branch for memory impact + runs-on: ubuntu-24.04 + needs: + - common + - determine-jobs + if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.memory_impact).should_run == 'true' + outputs: + ram_usage: ${{ steps.extract.outputs.ram_usage }} + flash_usage: ${{ steps.extract.outputs.flash_usage }} + cache_hit: ${{ steps.cache-memory-analysis.outputs.cache-hit }} + skip: ${{ steps.check-script.outputs.skip }} + steps: + - name: Check out target branch + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + ref: ${{ github.base_ref }} + + # Check if memory impact extraction script exists on target branch + # If not, skip the analysis (this handles older branches that don't have the feature) + - name: Check for memory impact script + id: check-script + run: | + if [ -f "script/ci_memory_impact_extract.py" ]; then + echo "skip=false" >> $GITHUB_OUTPUT + else + echo "skip=true" >> $GITHUB_OUTPUT + echo "::warning::ci_memory_impact_extract.py not found on target branch, skipping memory impact analysis" + fi + + # All remaining steps only run if script exists + - name: Generate cache key + id: cache-key + if: steps.check-script.outputs.skip != 'true' + run: | + # Get the commit SHA of the target branch + target_sha=$(git rev-parse HEAD) + + # Hash the build infrastructure files (all files that affect build/analysis) + infra_hash=$(cat \ + script/test_build_components.py \ + script/ci_memory_impact_extract.py \ + script/analyze_component_buses.py \ + script/merge_component_configs.py \ + script/ci_helpers.py \ + .github/workflows/ci.yml \ + | sha256sum | cut -d' ' -f1) + + # Get platform and components from job inputs + platform="${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}" + components='${{ toJSON(fromJSON(needs.determine-jobs.outputs.memory_impact).components) }}' + components_hash=$(echo "$components" | sha256sum | cut -d' ' -f1) + + # Combine into cache key + cache_key="memory-analysis-target-${target_sha}-${infra_hash}-${platform}-${components_hash}" + echo "cache-key=${cache_key}" >> $GITHUB_OUTPUT + echo "Cache key: ${cache_key}" + + - name: Restore cached memory analysis + id: cache-memory-analysis + if: steps.check-script.outputs.skip != 'true' + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: memory-analysis-target.json + key: ${{ steps.cache-key.outputs.cache-key }} + + - name: Cache status + if: steps.check-script.outputs.skip != 'true' + run: | + if [ "${{ steps.cache-memory-analysis.outputs.cache-hit }}" == "true" ]; then + echo "✓ Cache hit! Using cached memory analysis results." + echo " Skipping build step to save time." + else + echo "✗ Cache miss. Will build and analyze memory usage." + fi + + - name: Restore Python + if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' + uses: ./.github/actions/restore-python + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache-key: ${{ needs.common.outputs.cache-key }} + + - name: Cache platformio + if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.platformio + key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }} + + - name: Build, compile, and analyze memory + if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' + id: build + run: | + . venv/bin/activate + components='${{ toJSON(fromJSON(needs.determine-jobs.outputs.memory_impact).components) }}' + platform="${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}" + + echo "Building with test_build_components.py for $platform with components:" + echo "$components" | jq -r '.[]' | sed 's/^/ - /' + + # Use test_build_components.py which handles grouping automatically + # Pass components as comma-separated list + component_list=$(echo "$components" | jq -r 'join(",")') + + echo "Compiling with test_build_components.py..." + + # Run build and extract memory with auto-detection of build directory for detailed analysis + # Use tee to show output in CI while also piping to extraction script + python script/test_build_components.py \ + -e compile \ + -c "$component_list" \ + -t "$platform" 2>&1 | \ + tee /dev/stderr | \ + python script/ci_memory_impact_extract.py \ + --output-env \ + --output-json memory-analysis-target.json + + - name: Save memory analysis to cache + if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' && steps.build.outcome == 'success' + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: memory-analysis-target.json + key: ${{ steps.cache-key.outputs.cache-key }} + + - name: Extract memory usage for outputs + id: extract + if: steps.check-script.outputs.skip != 'true' + run: | + if [ -f memory-analysis-target.json ]; then + ram=$(jq -r '.ram_bytes' memory-analysis-target.json) + flash=$(jq -r '.flash_bytes' memory-analysis-target.json) + echo "ram_usage=${ram}" >> $GITHUB_OUTPUT + echo "flash_usage=${flash}" >> $GITHUB_OUTPUT + echo "RAM: ${ram} bytes, Flash: ${flash} bytes" + else + echo "Error: memory-analysis-target.json not found" + exit 1 + fi + + - name: Upload memory analysis JSON + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: memory-analysis-target + path: memory-analysis-target.json + if-no-files-found: warn + retention-days: 1 + + memory-impact-pr-branch: + name: Build PR branch for memory impact + runs-on: ubuntu-24.04 + needs: + - common + - determine-jobs + if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.memory_impact).should_run == 'true' + outputs: + ram_usage: ${{ steps.extract.outputs.ram_usage }} + flash_usage: ${{ steps.extract.outputs.flash_usage }} + steps: + - name: Check out PR branch + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - name: Restore Python + uses: ./.github/actions/restore-python + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache-key: ${{ needs.common.outputs.cache-key }} + - name: Cache platformio + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.platformio + key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }} + - name: Build, compile, and analyze memory + id: extract + run: | + . venv/bin/activate + components='${{ toJSON(fromJSON(needs.determine-jobs.outputs.memory_impact).components) }}' + platform="${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}" + + echo "Building with test_build_components.py for $platform with components:" + echo "$components" | jq -r '.[]' | sed 's/^/ - /' + + # Use test_build_components.py which handles grouping automatically + # Pass components as comma-separated list + component_list=$(echo "$components" | jq -r 'join(",")') + + echo "Compiling with test_build_components.py..." + + # Run build and extract memory with auto-detection of build directory for detailed analysis + # Use tee to show output in CI while also piping to extraction script + python script/test_build_components.py \ + -e compile \ + -c "$component_list" \ + -t "$platform" 2>&1 | \ + tee /dev/stderr | \ + python script/ci_memory_impact_extract.py \ + --output-env \ + --output-json memory-analysis-pr.json + - name: Upload memory analysis JSON + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: memory-analysis-pr + path: memory-analysis-pr.json + if-no-files-found: warn + retention-days: 1 + + memory-impact-comment: + name: Comment memory impact + runs-on: ubuntu-24.04 + needs: + - common + - determine-jobs + - memory-impact-target-branch + - memory-impact-pr-branch + if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.memory_impact).should_run == 'true' && needs.memory-impact-target-branch.outputs.skip != 'true' + permissions: + contents: read + pull-requests: write + steps: + - name: Check out code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - name: Restore Python + uses: ./.github/actions/restore-python + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache-key: ${{ needs.common.outputs.cache-key }} + - name: Download target analysis JSON + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: memory-analysis-target + path: ./memory-analysis + continue-on-error: true + - name: Download PR analysis JSON + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: memory-analysis-pr + path: ./memory-analysis + continue-on-error: true + - name: Post or update PR comment + env: + GH_TOKEN: ${{ github.token }} + COMPONENTS: ${{ toJSON(fromJSON(needs.determine-jobs.outputs.memory_impact).components) }} + PLATFORM: ${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }} + TARGET_RAM: ${{ needs.memory-impact-target-branch.outputs.ram_usage }} + TARGET_FLASH: ${{ needs.memory-impact-target-branch.outputs.flash_usage }} + PR_RAM: ${{ needs.memory-impact-pr-branch.outputs.ram_usage }} + PR_FLASH: ${{ needs.memory-impact-pr-branch.outputs.flash_usage }} + TARGET_CACHE_HIT: ${{ needs.memory-impact-target-branch.outputs.cache_hit }} + run: | + . venv/bin/activate + + # Check if analysis JSON files exist + target_json_arg="" + pr_json_arg="" + + if [ -f ./memory-analysis/memory-analysis-target.json ]; then + echo "Found target analysis JSON" + target_json_arg="--target-json ./memory-analysis/memory-analysis-target.json" + else + echo "No target analysis JSON found" + fi + + if [ -f ./memory-analysis/memory-analysis-pr.json ]; then + echo "Found PR analysis JSON" + pr_json_arg="--pr-json ./memory-analysis/memory-analysis-pr.json" + else + echo "No PR analysis JSON found" + fi + + # Add cache flag if target was cached + cache_flag="" + if [ "$TARGET_CACHE_HIT" == "true" ]; then + cache_flag="--target-cache-hit" + fi + + python script/ci_memory_impact_comment.py \ + --pr-number "${{ github.event.pull_request.number }}" \ + --components "$COMPONENTS" \ + --platform "$PLATFORM" \ + --target-ram "$TARGET_RAM" \ + --target-flash "$TARGET_FLASH" \ + --pr-ram "$PR_RAM" \ + --pr-flash "$PR_FLASH" \ + $target_json_arg \ + $pr_json_arg \ + $cache_flag + ci-status: name: CI Status runs-on: ubuntu-24.04 @@ -535,6 +823,9 @@ jobs: - test-build-components-splitter - test-build-components-split - pre-commit-ci-lite + - memory-impact-target-branch + - memory-impact-pr-branch + - memory-impact-comment if: always() steps: - name: Success diff --git a/esphome/__main__.py b/esphome/__main__.py index d9bdfb175b..a0b7d16ae9 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -466,7 +466,9 @@ def write_cpp_file() -> int: def compile_program(args: ArgsProtocol, config: ConfigType) -> int: from esphome import platformio_api - _LOGGER.info("Compiling app...") + # NOTE: "Build path:" format is parsed by script/ci_memory_impact_extract.py + # If you change this format, update the regex in that script as well + _LOGGER.info("Compiling app... Build path: %s", CORE.build_path) rc = platformio_api.run_compile(config, CORE.verbose) if rc != 0: return rc diff --git a/esphome/analyze_memory/__init__.py b/esphome/analyze_memory/__init__.py new file mode 100644 index 0000000000..71e86e3788 --- /dev/null +++ b/esphome/analyze_memory/__init__.py @@ -0,0 +1,502 @@ +"""Memory usage analyzer for ESPHome compiled binaries.""" + +from collections import defaultdict +from dataclasses import dataclass, field +import logging +from pathlib import Path +import re +import subprocess +from typing import TYPE_CHECKING + +from .const import ( + CORE_SUBCATEGORY_PATTERNS, + DEMANGLED_PATTERNS, + ESPHOME_COMPONENT_PATTERN, + SECTION_TO_ATTR, + SYMBOL_PATTERNS, +) +from .helpers import ( + get_component_class_patterns, + get_esphome_components, + map_section_name, + parse_symbol_line, +) + +if TYPE_CHECKING: + from esphome.platformio_api import IDEData + +_LOGGER = logging.getLogger(__name__) + +# GCC global constructor/destructor prefix annotations +_GCC_PREFIX_ANNOTATIONS = { + "_GLOBAL__sub_I_": "global constructor for", + "_GLOBAL__sub_D_": "global destructor for", +} + +# GCC optimization suffix pattern (e.g., $isra$0, $part$1, $constprop$2) +_GCC_OPTIMIZATION_SUFFIX_PATTERN = re.compile(r"(\$(?:isra|part|constprop)\$\d+)") + +# C++ runtime patterns for categorization +_CPP_RUNTIME_PATTERNS = frozenset(["vtable", "typeinfo", "thunk"]) + +# libc printf/scanf family base names (used to detect variants like _printf_r, vfprintf, etc.) +_LIBC_PRINTF_SCANF_FAMILY = frozenset(["printf", "fprintf", "sprintf", "scanf"]) + +# Regex pattern for parsing readelf section headers +# Format: [ #] name type addr off size +_READELF_SECTION_PATTERN = re.compile( + r"\s*\[\s*\d+\]\s+([\.\w]+)\s+\w+\s+[\da-fA-F]+\s+[\da-fA-F]+\s+([\da-fA-F]+)" +) + +# Component category prefixes +_COMPONENT_PREFIX_ESPHOME = "[esphome]" +_COMPONENT_PREFIX_EXTERNAL = "[external]" +_COMPONENT_CORE = f"{_COMPONENT_PREFIX_ESPHOME}core" +_COMPONENT_API = f"{_COMPONENT_PREFIX_ESPHOME}api" + +# C++ namespace prefixes +_NAMESPACE_ESPHOME = "esphome::" +_NAMESPACE_STD = "std::" + +# Type alias for symbol information: (symbol_name, size, component) +SymbolInfoType = tuple[str, int, str] + + +@dataclass +class MemorySection: + """Represents a memory section with its symbols.""" + + name: str + symbols: list[SymbolInfoType] = field(default_factory=list) + total_size: int = 0 + + +@dataclass +class ComponentMemory: + """Tracks memory usage for a component.""" + + name: str + text_size: int = 0 # Code in flash + rodata_size: int = 0 # Read-only data in flash + data_size: int = 0 # Initialized data (flash + ram) + bss_size: int = 0 # Uninitialized data (ram only) + symbol_count: int = 0 + + @property + def flash_total(self) -> int: + """Total flash usage (text + rodata + data).""" + return self.text_size + self.rodata_size + self.data_size + + @property + def ram_total(self) -> int: + """Total RAM usage (data + bss).""" + return self.data_size + self.bss_size + + +class MemoryAnalyzer: + """Analyzes memory usage from ELF files.""" + + def __init__( + self, + elf_path: str, + objdump_path: str | None = None, + readelf_path: str | None = None, + external_components: set[str] | None = None, + idedata: "IDEData | None" = None, + ) -> None: + """Initialize memory analyzer. + + Args: + elf_path: Path to ELF file to analyze + objdump_path: Path to objdump binary (auto-detected from idedata if not provided) + readelf_path: Path to readelf binary (auto-detected from idedata if not provided) + external_components: Set of external component names + idedata: Optional PlatformIO IDEData object to auto-detect toolchain paths + """ + self.elf_path = Path(elf_path) + if not self.elf_path.exists(): + raise FileNotFoundError(f"ELF file not found: {elf_path}") + + # Auto-detect toolchain paths from idedata if not provided + if idedata is not None and (objdump_path is None or readelf_path is None): + objdump_path = objdump_path or idedata.objdump_path + readelf_path = readelf_path or idedata.readelf_path + _LOGGER.debug("Using toolchain paths from PlatformIO idedata") + + self.objdump_path = objdump_path or "objdump" + self.readelf_path = readelf_path or "readelf" + self.external_components = external_components or set() + + self.sections: dict[str, MemorySection] = {} + self.components: dict[str, ComponentMemory] = defaultdict( + lambda: ComponentMemory("") + ) + self._demangle_cache: dict[str, str] = {} + self._uncategorized_symbols: list[tuple[str, str, int]] = [] + self._esphome_core_symbols: list[ + tuple[str, str, int] + ] = [] # Track core symbols + self._component_symbols: dict[str, list[tuple[str, str, int]]] = defaultdict( + list + ) # Track symbols for all components + + def analyze(self) -> dict[str, ComponentMemory]: + """Analyze the ELF file and return component memory usage.""" + self._parse_sections() + self._parse_symbols() + self._categorize_symbols() + return dict(self.components) + + def _parse_sections(self) -> None: + """Parse section headers from ELF file.""" + result = subprocess.run( + [self.readelf_path, "-S", str(self.elf_path)], + capture_output=True, + text=True, + check=True, + ) + + # Parse section headers + for line in result.stdout.splitlines(): + # Look for section entries + if not (match := _READELF_SECTION_PATTERN.match(line)): + continue + + section_name = match.group(1) + size_hex = match.group(2) + size = int(size_hex, 16) + + # Map to standard section name + mapped_section = map_section_name(section_name) + if not mapped_section: + continue + + if mapped_section not in self.sections: + self.sections[mapped_section] = MemorySection(mapped_section) + self.sections[mapped_section].total_size += size + + def _parse_symbols(self) -> None: + """Parse symbols from ELF file.""" + result = subprocess.run( + [self.objdump_path, "-t", str(self.elf_path)], + capture_output=True, + text=True, + check=True, + ) + + # Track seen addresses to avoid duplicates + seen_addresses: set[str] = set() + + for line in result.stdout.splitlines(): + if not (symbol_info := parse_symbol_line(line)): + continue + + section, name, size, address = symbol_info + + # Skip duplicate symbols at the same address (e.g., C1/C2 constructors) + if address in seen_addresses or section not in self.sections: + continue + + self.sections[section].symbols.append((name, size, "")) + seen_addresses.add(address) + + def _categorize_symbols(self) -> None: + """Categorize symbols by component.""" + # First, collect all unique symbol names for batch demangling + all_symbols = { + symbol_name + for section in self.sections.values() + for symbol_name, _, _ in section.symbols + } + + # Batch demangle all symbols at once + self._batch_demangle_symbols(list(all_symbols)) + + # Now categorize with cached demangled names + for section_name, section in self.sections.items(): + for symbol_name, size, _ in section.symbols: + component = self._identify_component(symbol_name) + + if component not in self.components: + self.components[component] = ComponentMemory(component) + + comp_mem = self.components[component] + comp_mem.symbol_count += 1 + + # Update the appropriate size attribute based on section + if attr_name := SECTION_TO_ATTR.get(section_name): + setattr(comp_mem, attr_name, getattr(comp_mem, attr_name) + size) + + # Track uncategorized symbols + if component == "other" and size > 0: + demangled = self._demangle_symbol(symbol_name) + self._uncategorized_symbols.append((symbol_name, demangled, size)) + + # Track ESPHome core symbols for detailed analysis + if component == _COMPONENT_CORE and size > 0: + demangled = self._demangle_symbol(symbol_name) + self._esphome_core_symbols.append((symbol_name, demangled, size)) + + # Track all component symbols for detailed analysis + if size > 0: + demangled = self._demangle_symbol(symbol_name) + self._component_symbols[component].append( + (symbol_name, demangled, size) + ) + + def _identify_component(self, symbol_name: str) -> str: + """Identify which component a symbol belongs to.""" + # Demangle C++ names if needed + demangled = self._demangle_symbol(symbol_name) + + # Check for special component classes first (before namespace pattern) + # This handles cases like esphome::ESPHomeOTAComponent which should map to ota + if _NAMESPACE_ESPHOME in demangled: + # Check for special component classes that include component name in the class + # For example: esphome::ESPHomeOTAComponent -> ota component + for component_name in get_esphome_components(): + patterns = get_component_class_patterns(component_name) + if any(pattern in demangled for pattern in patterns): + return f"{_COMPONENT_PREFIX_ESPHOME}{component_name}" + + # Check for ESPHome component namespaces + match = ESPHOME_COMPONENT_PATTERN.search(demangled) + if match: + component_name = match.group(1) + # Strip trailing underscore if present (e.g., switch_ -> switch) + component_name = component_name.rstrip("_") + + # Check if this is an actual component in the components directory + if component_name in get_esphome_components(): + return f"{_COMPONENT_PREFIX_ESPHOME}{component_name}" + # Check if this is a known external component from the config + if component_name in self.external_components: + return f"{_COMPONENT_PREFIX_EXTERNAL}{component_name}" + # Everything else in esphome:: namespace is core + return _COMPONENT_CORE + + # Check for esphome core namespace (no component namespace) + if _NAMESPACE_ESPHOME in demangled: + # If no component match found, it's core + return _COMPONENT_CORE + + # Check against symbol patterns + for component, patterns in SYMBOL_PATTERNS.items(): + if any(pattern in symbol_name for pattern in patterns): + return component + + # Check against demangled patterns + for component, patterns in DEMANGLED_PATTERNS.items(): + if any(pattern in demangled for pattern in patterns): + return component + + # Special cases that need more complex logic + + # Check if spi_flash vs spi_driver + if "spi_" in symbol_name or "SPI" in symbol_name: + return "spi_flash" if "spi_flash" in symbol_name else "spi_driver" + + # libc special printf variants + if ( + symbol_name.startswith("_") + and symbol_name[1:].replace("_r", "").replace("v", "").replace("s", "") + in _LIBC_PRINTF_SCANF_FAMILY + ): + return "libc" + + # Track uncategorized symbols for analysis + return "other" + + def _batch_demangle_symbols(self, symbols: list[str]) -> None: + """Batch demangle C++ symbol names for efficiency.""" + if not symbols: + return + + # Try to find the appropriate c++filt for the platform + cppfilt_cmd = "c++filt" + + _LOGGER.info("Demangling %d symbols", len(symbols)) + _LOGGER.debug("objdump_path = %s", self.objdump_path) + + # Check if we have a toolchain-specific c++filt + if self.objdump_path and self.objdump_path != "objdump": + # Replace objdump with c++filt in the path + potential_cppfilt = self.objdump_path.replace("objdump", "c++filt") + _LOGGER.info("Checking for toolchain c++filt at: %s", potential_cppfilt) + if Path(potential_cppfilt).exists(): + cppfilt_cmd = potential_cppfilt + _LOGGER.info("✓ Using toolchain c++filt: %s", cppfilt_cmd) + else: + _LOGGER.info( + "✗ Toolchain c++filt not found at %s, using system c++filt", + potential_cppfilt, + ) + else: + _LOGGER.info("✗ Using system c++filt (objdump_path=%s)", self.objdump_path) + + # Strip GCC optimization suffixes and prefixes before demangling + # Suffixes like $isra$0, $part$0, $constprop$0 confuse c++filt + # Prefixes like _GLOBAL__sub_I_ need to be removed and tracked + symbols_stripped: list[str] = [] + symbols_prefixes: list[str] = [] # Track removed prefixes + for symbol in symbols: + # Remove GCC optimization markers + stripped = _GCC_OPTIMIZATION_SUFFIX_PATTERN.sub("", symbol) + + # Handle GCC global constructor/initializer prefixes + # _GLOBAL__sub_I_ -> extract for demangling + prefix = "" + for gcc_prefix in _GCC_PREFIX_ANNOTATIONS: + if stripped.startswith(gcc_prefix): + prefix = gcc_prefix + stripped = stripped[len(prefix) :] + break + + symbols_stripped.append(stripped) + symbols_prefixes.append(prefix) + + try: + # Send all symbols to c++filt at once + result = subprocess.run( + [cppfilt_cmd], + input="\n".join(symbols_stripped), + capture_output=True, + text=True, + check=False, + ) + except (subprocess.SubprocessError, OSError, UnicodeDecodeError) as e: + # On error, cache originals + _LOGGER.warning("Failed to batch demangle symbols: %s", e) + for symbol in symbols: + self._demangle_cache[symbol] = symbol + return + + if result.returncode != 0: + _LOGGER.warning( + "c++filt exited with code %d: %s", + result.returncode, + result.stderr[:200] if result.stderr else "(no error output)", + ) + # Cache originals on failure + for symbol in symbols: + self._demangle_cache[symbol] = symbol + return + + # Process demangled output + self._process_demangled_output( + symbols, symbols_stripped, symbols_prefixes, result.stdout, cppfilt_cmd + ) + + def _process_demangled_output( + self, + symbols: list[str], + symbols_stripped: list[str], + symbols_prefixes: list[str], + demangled_output: str, + cppfilt_cmd: str, + ) -> None: + """Process demangled symbol output and populate cache. + + Args: + symbols: Original symbol names + symbols_stripped: Stripped symbol names sent to c++filt + symbols_prefixes: Removed prefixes to restore + demangled_output: Output from c++filt + cppfilt_cmd: Path to c++filt command (for logging) + """ + demangled_lines = demangled_output.strip().split("\n") + failed_count = 0 + + for original, stripped, prefix, demangled in zip( + symbols, symbols_stripped, symbols_prefixes, demangled_lines + ): + # Add back any prefix that was removed + demangled = self._restore_symbol_prefix(prefix, stripped, demangled) + + # If we stripped a suffix, add it back to the demangled name for clarity + if original != stripped and not prefix: + demangled = self._restore_symbol_suffix(original, demangled) + + self._demangle_cache[original] = demangled + + # Log symbols that failed to demangle (stayed the same as stripped version) + if stripped == demangled and stripped.startswith("_Z"): + failed_count += 1 + if failed_count <= 5: # Only log first 5 failures + _LOGGER.warning("Failed to demangle: %s", original) + + if failed_count == 0: + _LOGGER.info("Successfully demangled all %d symbols", len(symbols)) + return + + _LOGGER.warning( + "Failed to demangle %d/%d symbols using %s", + failed_count, + len(symbols), + cppfilt_cmd, + ) + + @staticmethod + def _restore_symbol_prefix(prefix: str, stripped: str, demangled: str) -> str: + """Restore prefix that was removed before demangling. + + Args: + prefix: Prefix that was removed (e.g., "_GLOBAL__sub_I_") + stripped: Stripped symbol name + demangled: Demangled symbol name + + Returns: + Demangled name with prefix restored/annotated + """ + if not prefix: + return demangled + + # Successfully demangled - add descriptive prefix + if demangled != stripped and ( + annotation := _GCC_PREFIX_ANNOTATIONS.get(prefix) + ): + return f"[{annotation}: {demangled}]" + + # Failed to demangle - restore original prefix + return prefix + demangled + + @staticmethod + def _restore_symbol_suffix(original: str, demangled: str) -> str: + """Restore GCC optimization suffix that was removed before demangling. + + Args: + original: Original symbol name with suffix + demangled: Demangled symbol name without suffix + + Returns: + Demangled name with suffix annotation + """ + if suffix_match := _GCC_OPTIMIZATION_SUFFIX_PATTERN.search(original): + return f"{demangled} [{suffix_match.group(1)}]" + return demangled + + def _demangle_symbol(self, symbol: str) -> str: + """Get demangled C++ symbol name from cache.""" + return self._demangle_cache.get(symbol, symbol) + + def _categorize_esphome_core_symbol(self, demangled: str) -> str: + """Categorize ESPHome core symbols into subcategories.""" + # Special patterns that need to be checked separately + if any(pattern in demangled for pattern in _CPP_RUNTIME_PATTERNS): + return "C++ Runtime (vtables/RTTI)" + + if demangled.startswith(_NAMESPACE_STD): + return "C++ STL" + + # Check against patterns from const.py + for category, patterns in CORE_SUBCATEGORY_PATTERNS.items(): + if any(pattern in demangled for pattern in patterns): + return category + + return "Other Core" + + +if __name__ == "__main__": + from .cli import main + + main() diff --git a/esphome/analyze_memory/__main__.py b/esphome/analyze_memory/__main__.py new file mode 100644 index 0000000000..aa772c3ad4 --- /dev/null +++ b/esphome/analyze_memory/__main__.py @@ -0,0 +1,6 @@ +"""Main entry point for running the memory analyzer as a module.""" + +from .cli import main + +if __name__ == "__main__": + main() diff --git a/esphome/analyze_memory/cli.py b/esphome/analyze_memory/cli.py new file mode 100644 index 0000000000..1695a00c19 --- /dev/null +++ b/esphome/analyze_memory/cli.py @@ -0,0 +1,408 @@ +"""CLI interface for memory analysis with report generation.""" + +from collections import defaultdict +import sys + +from . import ( + _COMPONENT_API, + _COMPONENT_CORE, + _COMPONENT_PREFIX_ESPHOME, + _COMPONENT_PREFIX_EXTERNAL, + MemoryAnalyzer, +) + + +class MemoryAnalyzerCLI(MemoryAnalyzer): + """Memory analyzer with CLI-specific report generation.""" + + # Column width constants + COL_COMPONENT: int = 29 + COL_FLASH_TEXT: int = 14 + COL_FLASH_DATA: int = 14 + COL_RAM_DATA: int = 12 + COL_RAM_BSS: int = 12 + COL_TOTAL_FLASH: int = 15 + COL_TOTAL_RAM: int = 12 + COL_SEPARATOR: int = 3 # " | " + + # Core analysis column widths + COL_CORE_SUBCATEGORY: int = 30 + COL_CORE_SIZE: int = 12 + COL_CORE_COUNT: int = 6 + COL_CORE_PERCENT: int = 10 + + # Calculate table width once at class level + TABLE_WIDTH: int = ( + COL_COMPONENT + + COL_SEPARATOR + + COL_FLASH_TEXT + + COL_SEPARATOR + + COL_FLASH_DATA + + COL_SEPARATOR + + COL_RAM_DATA + + COL_SEPARATOR + + COL_RAM_BSS + + COL_SEPARATOR + + COL_TOTAL_FLASH + + COL_SEPARATOR + + COL_TOTAL_RAM + ) + + @staticmethod + def _make_separator_line(*widths: int) -> str: + """Create a separator line with given column widths. + + Args: + widths: Column widths to create separators for + + Returns: + Separator line like "----+---------+-----" + """ + return "-+-".join("-" * width for width in widths) + + # Pre-computed separator lines + MAIN_TABLE_SEPARATOR: str = _make_separator_line( + COL_COMPONENT, + COL_FLASH_TEXT, + COL_FLASH_DATA, + COL_RAM_DATA, + COL_RAM_BSS, + COL_TOTAL_FLASH, + COL_TOTAL_RAM, + ) + + CORE_TABLE_SEPARATOR: str = _make_separator_line( + COL_CORE_SUBCATEGORY, + COL_CORE_SIZE, + COL_CORE_COUNT, + COL_CORE_PERCENT, + ) + + def generate_report(self, detailed: bool = False) -> str: + """Generate a formatted memory report.""" + components = sorted( + self.components.items(), key=lambda x: x[1].flash_total, reverse=True + ) + + # Calculate totals + total_flash = sum(c.flash_total for _, c in components) + total_ram = sum(c.ram_total for _, c in components) + + # Build report + lines: list[str] = [] + + lines.append("=" * self.TABLE_WIDTH) + lines.append("Component Memory Analysis".center(self.TABLE_WIDTH)) + lines.append("=" * self.TABLE_WIDTH) + lines.append("") + + # Main table - fixed column widths + lines.append( + f"{'Component':<{self.COL_COMPONENT}} | {'Flash (text)':>{self.COL_FLASH_TEXT}} | {'Flash (data)':>{self.COL_FLASH_DATA}} | {'RAM (data)':>{self.COL_RAM_DATA}} | {'RAM (bss)':>{self.COL_RAM_BSS}} | {'Total Flash':>{self.COL_TOTAL_FLASH}} | {'Total RAM':>{self.COL_TOTAL_RAM}}" + ) + lines.append(self.MAIN_TABLE_SEPARATOR) + + for name, mem in components: + if mem.flash_total > 0 or mem.ram_total > 0: + flash_rodata = mem.rodata_size + mem.data_size + lines.append( + f"{name:<{self.COL_COMPONENT}} | {mem.text_size:>{self.COL_FLASH_TEXT - 2},} B | {flash_rodata:>{self.COL_FLASH_DATA - 2},} B | " + f"{mem.data_size:>{self.COL_RAM_DATA - 2},} B | {mem.bss_size:>{self.COL_RAM_BSS - 2},} B | " + f"{mem.flash_total:>{self.COL_TOTAL_FLASH - 2},} B | {mem.ram_total:>{self.COL_TOTAL_RAM - 2},} B" + ) + + lines.append(self.MAIN_TABLE_SEPARATOR) + lines.append( + f"{'TOTAL':<{self.COL_COMPONENT}} | {' ':>{self.COL_FLASH_TEXT}} | {' ':>{self.COL_FLASH_DATA}} | " + f"{' ':>{self.COL_RAM_DATA}} | {' ':>{self.COL_RAM_BSS}} | " + f"{total_flash:>{self.COL_TOTAL_FLASH - 2},} B | {total_ram:>{self.COL_TOTAL_RAM - 2},} B" + ) + + # Top consumers + lines.append("") + lines.append("Top Flash Consumers:") + for i, (name, mem) in enumerate(components[:25]): + if mem.flash_total > 0: + percentage = ( + (mem.flash_total / total_flash * 100) if total_flash > 0 else 0 + ) + lines.append( + f"{i + 1}. {name} ({mem.flash_total:,} B) - {percentage:.1f}% of analyzed flash" + ) + + lines.append("") + lines.append("Top RAM Consumers:") + ram_components = sorted(components, key=lambda x: x[1].ram_total, reverse=True) + for i, (name, mem) in enumerate(ram_components[:25]): + if mem.ram_total > 0: + percentage = (mem.ram_total / total_ram * 100) if total_ram > 0 else 0 + lines.append( + f"{i + 1}. {name} ({mem.ram_total:,} B) - {percentage:.1f}% of analyzed RAM" + ) + + lines.append("") + lines.append( + "Note: This analysis covers symbols in the ELF file. Some runtime allocations may not be included." + ) + lines.append("=" * self.TABLE_WIDTH) + + # Add ESPHome core detailed analysis if there are core symbols + if self._esphome_core_symbols: + lines.append("") + lines.append("=" * self.TABLE_WIDTH) + lines.append( + f"{_COMPONENT_CORE} Detailed Analysis".center(self.TABLE_WIDTH) + ) + lines.append("=" * self.TABLE_WIDTH) + lines.append("") + + # Group core symbols by subcategory + core_subcategories: dict[str, list[tuple[str, str, int]]] = defaultdict( + list + ) + + for symbol, demangled, size in self._esphome_core_symbols: + # Categorize based on demangled name patterns + subcategory = self._categorize_esphome_core_symbol(demangled) + core_subcategories[subcategory].append((symbol, demangled, size)) + + # Sort subcategories by total size + sorted_subcategories = sorted( + [ + (name, symbols, sum(s[2] for s in symbols)) + for name, symbols in core_subcategories.items() + ], + key=lambda x: x[2], + reverse=True, + ) + + lines.append( + f"{'Subcategory':<{self.COL_CORE_SUBCATEGORY}} | {'Size':>{self.COL_CORE_SIZE}} | " + f"{'Count':>{self.COL_CORE_COUNT}} | {'% of Core':>{self.COL_CORE_PERCENT}}" + ) + lines.append(self.CORE_TABLE_SEPARATOR) + + core_total = sum(size for _, _, size in self._esphome_core_symbols) + + for subcategory, symbols, total_size in sorted_subcategories: + percentage = (total_size / core_total * 100) if core_total > 0 else 0 + lines.append( + f"{subcategory:<{self.COL_CORE_SUBCATEGORY}} | {total_size:>{self.COL_CORE_SIZE - 2},} B | " + f"{len(symbols):>{self.COL_CORE_COUNT}} | {percentage:>{self.COL_CORE_PERCENT - 1}.1f}%" + ) + + # Top 15 largest core symbols + lines.append("") + lines.append(f"Top 15 Largest {_COMPONENT_CORE} Symbols:") + sorted_core_symbols = sorted( + self._esphome_core_symbols, key=lambda x: x[2], reverse=True + ) + + for i, (symbol, demangled, size) in enumerate(sorted_core_symbols[:15]): + lines.append(f"{i + 1}. {demangled} ({size:,} B)") + + lines.append("=" * self.TABLE_WIDTH) + + # Add detailed analysis for top ESPHome and external components + esphome_components = [ + (name, mem) + for name, mem in components + if name.startswith(_COMPONENT_PREFIX_ESPHOME) and name != _COMPONENT_CORE + ] + external_components = [ + (name, mem) + for name, mem in components + if name.startswith(_COMPONENT_PREFIX_EXTERNAL) + ] + + top_esphome_components = sorted( + esphome_components, key=lambda x: x[1].flash_total, reverse=True + )[:30] + + # Include all external components (they're usually important) + top_external_components = sorted( + external_components, key=lambda x: x[1].flash_total, reverse=True + ) + + # Check if API component exists and ensure it's included + api_component = None + for name, mem in components: + if name == _COMPONENT_API: + api_component = (name, mem) + break + + # Combine all components to analyze: top ESPHome + all external + API if not already included + components_to_analyze = list(top_esphome_components) + list( + top_external_components + ) + if api_component and api_component not in components_to_analyze: + components_to_analyze.append(api_component) + + if components_to_analyze: + for comp_name, comp_mem in components_to_analyze: + if not (comp_symbols := self._component_symbols.get(comp_name, [])): + continue + lines.append("") + lines.append("=" * self.TABLE_WIDTH) + lines.append(f"{comp_name} Detailed Analysis".center(self.TABLE_WIDTH)) + lines.append("=" * self.TABLE_WIDTH) + lines.append("") + + # Sort symbols by size + sorted_symbols = sorted(comp_symbols, key=lambda x: x[2], reverse=True) + + lines.append(f"Total symbols: {len(sorted_symbols)}") + lines.append(f"Total size: {comp_mem.flash_total:,} B") + lines.append("") + + # Show all symbols > 100 bytes for better visibility + large_symbols = [ + (sym, dem, size) for sym, dem, size in sorted_symbols if size > 100 + ] + + lines.append( + f"{comp_name} Symbols > 100 B ({len(large_symbols)} symbols):" + ) + for i, (symbol, demangled, size) in enumerate(large_symbols): + lines.append(f"{i + 1}. {demangled} ({size:,} B)") + + lines.append("=" * self.TABLE_WIDTH) + + return "\n".join(lines) + + def dump_uncategorized_symbols(self, output_file: str | None = None) -> None: + """Dump uncategorized symbols for analysis.""" + # Sort by size descending + sorted_symbols = sorted( + self._uncategorized_symbols, key=lambda x: x[2], reverse=True + ) + + lines = ["Uncategorized Symbols Analysis", "=" * 80] + lines.append(f"Total uncategorized symbols: {len(sorted_symbols)}") + lines.append( + f"Total uncategorized size: {sum(s[2] for s in sorted_symbols):,} bytes" + ) + lines.append("") + lines.append(f"{'Size':>10} | {'Symbol':<60} | Demangled") + lines.append("-" * 10 + "-+-" + "-" * 60 + "-+-" + "-" * 40) + + for symbol, demangled, size in sorted_symbols[:100]: # Top 100 + demangled_display = ( + demangled[:100] if symbol != demangled else "[not demangled]" + ) + lines.append(f"{size:>10,} | {symbol[:60]:<60} | {demangled_display}") + + if len(sorted_symbols) > 100: + lines.append(f"\n... and {len(sorted_symbols) - 100} more symbols") + + content = "\n".join(lines) + + if output_file: + with open(output_file, "w", encoding="utf-8") as f: + f.write(content) + else: + print(content) + + +def analyze_elf( + elf_path: str, + objdump_path: str | None = None, + readelf_path: str | None = None, + detailed: bool = False, + external_components: set[str] | None = None, +) -> str: + """Analyze an ELF file and return a memory report.""" + analyzer = MemoryAnalyzerCLI( + elf_path, objdump_path, readelf_path, external_components + ) + analyzer.analyze() + return analyzer.generate_report(detailed) + + +def main(): + """CLI entrypoint for memory analysis.""" + if len(sys.argv) < 2: + print("Usage: python -m esphome.analyze_memory ") + print("\nAnalyze memory usage from an ESPHome build directory.") + print("The build directory should contain firmware.elf and idedata will be") + print("loaded from ~/.esphome/.internal/idedata/.json") + print("\nExamples:") + print(" python -m esphome.analyze_memory ~/.esphome/build/my-device") + print(" python -m esphome.analyze_memory .esphome/build/my-device") + print(" python -m esphome.analyze_memory my-device # Short form") + sys.exit(1) + + build_dir = sys.argv[1] + + # Load build directory + import json + from pathlib import Path + + from esphome.platformio_api import IDEData + + build_path = Path(build_dir) + + # If no path separator in name, assume it's a device name + if "/" not in build_dir and not build_path.is_dir(): + # Try current directory first + cwd_path = Path.cwd() / ".esphome" / "build" / build_dir + if cwd_path.is_dir(): + build_path = cwd_path + print(f"Using build directory: {build_path}", file=sys.stderr) + else: + # Fall back to home directory + build_path = Path.home() / ".esphome" / "build" / build_dir + print(f"Using build directory: {build_path}", file=sys.stderr) + + if not build_path.is_dir(): + print(f"Error: {build_path} is not a directory", file=sys.stderr) + sys.exit(1) + + # Find firmware.elf + elf_file = None + for elf_candidate in [ + build_path / "firmware.elf", + build_path / ".pioenvs" / build_path.name / "firmware.elf", + ]: + if elf_candidate.exists(): + elf_file = str(elf_candidate) + break + + if not elf_file: + print(f"Error: firmware.elf not found in {build_dir}", file=sys.stderr) + sys.exit(1) + + # Find idedata.json - check current directory first, then home + device_name = build_path.name + idedata_candidates = [ + Path.cwd() / ".esphome" / "idedata" / f"{device_name}.json", + Path.home() / ".esphome" / "idedata" / f"{device_name}.json", + ] + + idedata = None + for idedata_path in idedata_candidates: + if not idedata_path.exists(): + continue + try: + with open(idedata_path, encoding="utf-8") as f: + raw_data = json.load(f) + idedata = IDEData(raw_data) + print(f"Loaded idedata from: {idedata_path}", file=sys.stderr) + break + except (json.JSONDecodeError, OSError) as e: + print(f"Warning: Failed to load idedata: {e}", file=sys.stderr) + + if not idedata: + print( + f"Warning: idedata not found (searched {idedata_candidates[0]} and {idedata_candidates[1]})", + file=sys.stderr, + ) + + analyzer = MemoryAnalyzerCLI(elf_file, idedata=idedata) + analyzer.analyze() + report = analyzer.generate_report() + print(report) + + +if __name__ == "__main__": + main() diff --git a/esphome/analyze_memory/const.py b/esphome/analyze_memory/const.py new file mode 100644 index 0000000000..c60b70aeec --- /dev/null +++ b/esphome/analyze_memory/const.py @@ -0,0 +1,903 @@ +"""Constants for memory analysis symbol pattern matching.""" + +import re + +# Pattern to extract ESPHome component namespaces dynamically +ESPHOME_COMPONENT_PATTERN = re.compile(r"esphome::([a-zA-Z0-9_]+)::") + +# Section mapping for ELF file sections +# Maps standard section names to their various platform-specific variants +SECTION_MAPPING = { + ".text": frozenset([".text", ".iram"]), + ".rodata": frozenset([".rodata"]), + ".data": frozenset([".data", ".dram"]), + ".bss": frozenset([".bss"]), +} + +# Section to ComponentMemory attribute mapping +# Maps section names to the attribute name in ComponentMemory dataclass +SECTION_TO_ATTR = { + ".text": "text_size", + ".rodata": "rodata_size", + ".data": "data_size", + ".bss": "bss_size", +} + +# Component identification rules +# Symbol patterns: patterns found in raw symbol names +SYMBOL_PATTERNS = { + "freertos": [ + "vTask", + "xTask", + "xQueue", + "pvPort", + "vPort", + "uxTask", + "pcTask", + "prvTimerTask", + "prvAddNewTaskToReadyList", + "pxReadyTasksLists", + "prvAddCurrentTaskToDelayedList", + "xEventGroupWaitBits", + "xRingbufferSendFromISR", + "prvSendItemDoneNoSplit", + "prvReceiveGeneric", + "prvSendAcquireGeneric", + "prvCopyItemAllowSplit", + "xEventGroup", + "xRingbuffer", + "prvSend", + "prvReceive", + "prvCopy", + "xPort", + "ulTaskGenericNotifyTake", + "prvIdleTask", + "prvInitialiseNewTask", + "prvIsYieldRequiredSMP", + "prvGetItemByteBuf", + "prvInitializeNewRingbuffer", + "prvAcquireItemNoSplit", + "prvNotifyQueueSetContainer", + "ucStaticTimerQueueStorage", + "eTaskGetState", + "main_task", + "do_system_init_fn", + "xSemaphoreCreateGenericWithCaps", + "vListInsert", + "uxListRemove", + "vRingbufferReturnItem", + "vRingbufferReturnItemFromISR", + "prvCheckItemFitsByteBuffer", + "prvGetCurMaxSizeAllowSplit", + "tick_hook", + "sys_sem_new", + "sys_arch_mbox_fetch", + "sys_arch_sem_wait", + "prvDeleteTCB", + "vQueueDeleteWithCaps", + "vRingbufferDeleteWithCaps", + "vSemaphoreDeleteWithCaps", + "prvCheckItemAvail", + "prvCheckTaskCanBeScheduledSMP", + "prvGetCurMaxSizeNoSplit", + "prvResetNextTaskUnblockTime", + "prvReturnItemByteBuf", + "vApplicationStackOverflowHook", + "vApplicationGetIdleTaskMemory", + "sys_init", + "sys_mbox_new", + "sys_arch_mbox_tryfetch", + ], + "xtensa": ["xt_", "_xt_", "xPortEnterCriticalTimeout"], + "heap": ["heap_", "multi_heap"], + "spi_flash": ["spi_flash"], + "rtc": ["rtc_", "rtcio_ll_"], + "gpio_driver": ["gpio_", "pins"], + "uart_driver": ["uart", "_uart", "UART"], + "timer": ["timer_", "esp_timer"], + "peripherals": ["periph_", "periman"], + "network_stack": [ + "vj_compress", + "raw_sendto", + "raw_input", + "etharp_", + "icmp_input", + "socket_ipv6", + "ip_napt", + "socket_ipv4_multicast", + "socket_ipv6_multicast", + "netconn_", + "recv_raw", + "accept_function", + "netconn_recv_data", + "netconn_accept", + "netconn_write_vectors_partly", + "netconn_drain", + "raw_connect", + "raw_bind", + "icmp_send_response", + "sockets", + "icmp_dest_unreach", + "inet_chksum_pseudo", + "alloc_socket", + "done_socket", + "set_global_fd_sets", + "inet_chksum_pbuf", + "tryget_socket_unconn_locked", + "tryget_socket_unconn", + "cs_create_ctrl_sock", + "netbuf_alloc", + ], + "ipv6_stack": ["nd6_", "ip6_", "mld6_", "icmp6_", "icmp6_input"], + "wifi_stack": [ + "ieee80211", + "hostap", + "sta_", + "ap_", + "scan_", + "wifi_", + "wpa_", + "wps_", + "esp_wifi", + "cnx_", + "wpa3_", + "sae_", + "wDev_", + "ic_", + "mac_", + "esf_buf", + "gWpaSm", + "sm_WPA", + "eapol_", + "owe_", + "wifiLowLevelInit", + "s_do_mapping", + "gScanStruct", + "ppSearchTxframe", + "ppMapWaitTxq", + "ppFillAMPDUBar", + "ppCheckTxConnTrafficIdle", + "ppCalTkipMic", + ], + "bluetooth": ["bt_", "ble_", "l2c_", "gatt_", "gap_", "hci_", "BT_init"], + "wifi_bt_coex": ["coex"], + "bluetooth_rom": ["r_ble", "r_lld", "r_llc", "r_llm"], + "bluedroid_bt": [ + "bluedroid", + "btc_", + "bta_", + "btm_", + "btu_", + "BTM_", + "GATT", + "L2CA_", + "smp_", + "gatts_", + "attp_", + "l2cu_", + "l2cb", + "smp_cb", + "BTA_GATTC_", + "SMP_", + "BTU_", + "BTA_Dm", + "GAP_Ble", + "BT_tx_if", + "host_recv_pkt_cb", + "saved_local_oob_data", + "string_to_bdaddr", + "string_is_bdaddr", + "CalConnectParamTimeout", + "transmit_fragment", + "transmit_data", + "event_command_ready", + "read_command_complete_header", + "parse_read_local_extended_features_response", + "parse_read_local_version_info_response", + "should_request_high", + "btdm_wakeup_request", + "BTA_SetAttributeValue", + "BTA_EnableBluetooth", + "transmit_command_futured", + "transmit_command", + "get_waiting_command", + "make_command", + "transmit_downward", + "host_recv_adv_packet", + "copy_extra_byte_in_db", + "parse_read_local_supported_commands_response", + ], + "crypto_math": [ + "ecp_", + "bignum_", + "mpi_", + "sswu", + "modp", + "dragonfly_", + "gcm_mult", + "__multiply", + "quorem", + "__mdiff", + "__lshift", + "__mprec_tens", + "ECC_", + "multiprecision_", + "mix_sub_columns", + "sbox", + "gfm2_sbox", + "gfm3_sbox", + "curve_p256", + "curve", + "p_256_init_curve", + "shift_sub_rows", + "rshift", + ], + "hw_crypto": ["esp_aes", "esp_sha", "esp_rsa", "esp_bignum", "esp_mpi"], + "libc": [ + "printf", + "scanf", + "malloc", + "free", + "memcpy", + "memset", + "strcpy", + "strlen", + "_dtoa", + "_fopen", + "__sfvwrite_r", + "qsort", + "__sf", + "__sflush_r", + "__srefill_r", + "_impure_data", + "_reclaim_reent", + "_open_r", + "strncpy", + "_strtod_l", + "__gethex", + "__hexnan", + "_setenv_r", + "_tzset_unlocked_r", + "__tzcalc_limits", + "select", + "scalbnf", + "strtof", + "strtof_l", + "__d2b", + "__b2d", + "__s2b", + "_Balloc", + "__multadd", + "__lo0bits", + "__atexit0", + "__smakebuf_r", + "__swhatbuf_r", + "_sungetc_r", + "_close_r", + "_link_r", + "_unsetenv_r", + "_rename_r", + "__month_lengths", + "tzinfo", + "__ratio", + "__hi0bits", + "__ulp", + "__any_on", + "__copybits", + "L_shift", + "_fcntl_r", + "_lseek_r", + "_read_r", + "_write_r", + "_unlink_r", + "_fstat_r", + "access", + "fsync", + "tcsetattr", + "tcgetattr", + "tcflush", + "tcdrain", + "__ssrefill_r", + "_stat_r", + "__hexdig_fun", + "__mcmp", + "_fwalk_sglue", + "__fpclassifyf", + "_setlocale_r", + "_mbrtowc_r", + "fcntl", + "__match", + "_lock_close", + "__c$", + "__func__$", + "__FUNCTION__$", + "DAYS_IN_MONTH", + "_DAYS_BEFORE_MONTH", + "CSWTCH$", + "dst$", + "sulp", + ], + "string_ops": ["strcmp", "strncmp", "strchr", "strstr", "strtok", "strdup"], + "memory_alloc": ["malloc", "calloc", "realloc", "free", "_sbrk"], + "file_io": [ + "fread", + "fwrite", + "fopen", + "fclose", + "fseek", + "ftell", + "fflush", + "s_fd_table", + ], + "string_formatting": [ + "snprintf", + "vsnprintf", + "sprintf", + "vsprintf", + "sscanf", + "vsscanf", + ], + "cpp_anonymous": ["_GLOBAL__N_", "n$"], + "cpp_runtime": ["__cxx", "_ZN", "_ZL", "_ZSt", "__gxx_personality", "_Z16"], + "exception_handling": ["__cxa_", "_Unwind_", "__gcc_personality", "uw_frame_state"], + "static_init": ["_GLOBAL__sub_I_"], + "mdns_lib": ["mdns"], + "phy_radio": [ + "phy_", + "rf_", + "chip_", + "register_chipv7", + "pbus_", + "bb_", + "fe_", + "rfcal_", + "ram_rfcal", + "tx_pwctrl", + "rx_chan", + "set_rx_gain", + "set_chan", + "agc_reg", + "ram_txiq", + "ram_txdc", + "ram_gen_rx_gain", + "rx_11b_opt", + "set_rx_sense", + "set_rx_gain_cal", + "set_chan_dig_gain", + "tx_pwctrl_init_cal", + "rfcal_txiq", + "set_tx_gain_table", + "correct_rfpll_offset", + "pll_correct_dcap", + "txiq_cal_init", + "pwdet_sar", + "pwdet_sar2_init", + "ram_iq_est_enable", + "ram_rfpll_set_freq", + "ant_wifirx_cfg", + "ant_btrx_cfg", + "force_txrxoff", + "force_txrx_off", + "tx_paon_set", + "opt_11b_resart", + "rfpll_1p2_opt", + "ram_dc_iq_est", + "ram_start_tx_tone", + "ram_en_pwdet", + "ram_cbw2040_cfg", + "rxdc_est_min", + "i2cmst_reg_init", + "temprature_sens_read", + "ram_restart_cal", + "ram_write_gain_mem", + "ram_wait_rfpll_cal_end", + "txcal_debuge_mode", + "ant_wifitx_cfg", + "reg_init_begin", + ], + "wifi_phy_pp": ["pp_", "ppT", "ppR", "ppP", "ppInstall", "ppCalTxAMPDULength"], + "wifi_lmac": ["lmac"], + "wifi_device": ["wdev", "wDev_"], + "power_mgmt": [ + "pm_", + "sleep", + "rtc_sleep", + "light_sleep", + "deep_sleep", + "power_down", + "g_pm", + ], + "memory_mgmt": [ + "mem_", + "memory_", + "tlsf_", + "memp_", + "pbuf_", + "pbuf_alloc", + "pbuf_copy_partial_pbuf", + ], + "hal_layer": ["hal_"], + "clock_mgmt": [ + "clk_", + "clock_", + "rtc_clk", + "apb_", + "cpu_freq", + "setCpuFrequencyMhz", + ], + "cache_mgmt": ["cache"], + "flash_ops": ["flash", "image_load"], + "interrupt_handlers": [ + "isr", + "interrupt", + "intr_", + "exc_", + "exception", + "port_IntStack", + ], + "wrapper_functions": ["_wrapper"], + "error_handling": ["panic", "abort", "assert", "error_", "fault"], + "authentication": ["auth"], + "ppp_protocol": ["ppp", "ipcp_", "lcp_", "chap_", "LcpEchoCheck"], + "dhcp": ["dhcp", "handle_dhcp"], + "ethernet_phy": [ + "emac_", + "eth_phy_", + "phy_tlk110", + "phy_lan87", + "phy_ip101", + "phy_rtl", + "phy_dp83", + "phy_ksz", + "lan87xx_", + "rtl8201_", + "ip101_", + "ksz80xx_", + "jl1101_", + "dp83848_", + "eth_on_state_changed", + ], + "threading": ["pthread_", "thread_", "_task_"], + "pthread": ["pthread"], + "synchronization": ["mutex", "semaphore", "spinlock", "portMUX"], + "math_lib": [ + "sin", + "cos", + "tan", + "sqrt", + "pow", + "exp", + "log", + "atan", + "asin", + "acos", + "floor", + "ceil", + "fabs", + "round", + ], + "random": ["rand", "random", "rng_", "prng"], + "time_lib": [ + "time", + "clock", + "gettimeofday", + "settimeofday", + "localtime", + "gmtime", + "mktime", + "strftime", + ], + "console_io": ["console_", "uart_tx", "uart_rx", "puts", "putchar", "getchar"], + "rom_functions": ["r_", "rom_"], + "compiler_runtime": [ + "__divdi3", + "__udivdi3", + "__moddi3", + "__muldi3", + "__ashldi3", + "__ashrdi3", + "__lshrdi3", + "__cmpdi2", + "__fixdfdi", + "__floatdidf", + ], + "libgcc": ["libgcc", "_divdi3", "_udivdi3"], + "boot_startup": ["boot", "start_cpu", "call_start", "startup", "bootloader"], + "bootloader": ["bootloader_", "esp_bootloader"], + "app_framework": ["app_", "initArduino", "setup", "loop", "Update"], + "weak_symbols": ["__weak_"], + "compiler_builtins": ["__builtin_"], + "vfs": ["vfs_", "VFS"], + "esp32_sdk": ["esp32_", "esp32c", "esp32s"], + "usb": ["usb_", "USB", "cdc_", "CDC"], + "i2c_driver": ["i2c_", "I2C"], + "i2s_driver": ["i2s_", "I2S"], + "spi_driver": ["spi_", "SPI"], + "adc_driver": ["adc_", "ADC"], + "dac_driver": ["dac_", "DAC"], + "touch_driver": ["touch_", "TOUCH"], + "pwm_driver": ["pwm_", "PWM", "ledc_", "LEDC"], + "rmt_driver": ["rmt_", "RMT"], + "pcnt_driver": ["pcnt_", "PCNT"], + "can_driver": ["can_", "CAN", "twai_", "TWAI"], + "sdmmc_driver": ["sdmmc_", "SDMMC", "sdcard", "sd_card"], + "temp_sensor": ["temp_sensor", "tsens_"], + "watchdog": ["wdt_", "WDT", "watchdog"], + "brownout": ["brownout", "bod_"], + "ulp": ["ulp_", "ULP"], + "psram": ["psram", "PSRAM", "spiram", "SPIRAM"], + "efuse": ["efuse", "EFUSE"], + "partition": ["partition", "esp_partition"], + "esp_event": ["esp_event", "event_loop", "event_callback"], + "esp_console": ["esp_console", "console_"], + "chip_specific": ["chip_", "esp_chip"], + "esp_system_utils": ["esp_system", "esp_hw", "esp_clk", "esp_sleep"], + "ipc": ["esp_ipc", "ipc_"], + "wifi_config": [ + "g_cnxMgr", + "gChmCxt", + "g_ic", + "TxRxCxt", + "s_dp", + "s_ni", + "s_reg_dump", + "packet$", + "d_mult_table", + "K", + "fcstab", + ], + "smartconfig": ["sc_ack_send"], + "rc_calibration": ["rc_cal", "rcUpdate"], + "noise_floor": ["noise_check"], + "rf_calibration": [ + "set_rx_sense", + "set_rx_gain_cal", + "set_chan_dig_gain", + "tx_pwctrl_init_cal", + "rfcal_txiq", + "set_tx_gain_table", + "correct_rfpll_offset", + "pll_correct_dcap", + "txiq_cal_init", + "pwdet_sar", + "rx_11b_opt", + ], + "wifi_crypto": [ + "pk_use_ecparams", + "process_segments", + "ccmp_", + "rc4_", + "aria_", + "mgf_mask", + "dh_group", + "ccmp_aad_nonce", + "ccmp_encrypt", + "rc4_skip", + "aria_sb1", + "aria_sb2", + "aria_is1", + "aria_is2", + "aria_sl", + "aria_a", + ], + "radio_control": ["fsm_input", "fsm_sconfreq"], + "pbuf": [ + "pbuf_", + ], + "event_group": ["xEventGroup"], + "ringbuffer": ["xRingbuffer", "prvSend", "prvReceive", "prvCopy"], + "provisioning": ["prov_", "prov_stop_and_notify"], + "scan": ["gScanStruct"], + "port": ["xPort"], + "elf_loader": [ + "elf_add", + "elf_add_note", + "elf_add_segment", + "process_image", + "read_encoded", + "read_encoded_value", + "read_encoded_value_with_base", + "process_image_header", + ], + "socket_api": [ + "sockets", + "netconn_", + "accept_function", + "recv_raw", + "socket_ipv4_multicast", + "socket_ipv6_multicast", + ], + "igmp": ["igmp_", "igmp_send", "igmp_input"], + "icmp6": ["icmp6_"], + "arp": ["arp_table"], + "ampdu": [ + "ampdu_", + "rcAmpdu", + "trc_onAmpduOp", + "rcAmpduLowerRate", + "ampdu_dispatch_upto", + ], + "ieee802_11": ["ieee802_11_", "ieee802_11_parse_elems"], + "rate_control": ["rssi_margin", "rcGetSched", "get_rate_fcc_index"], + "nan": ["nan_dp_", "nan_dp_post_tx", "nan_dp_delete_peer"], + "channel_mgmt": ["chm_init", "chm_set_current_channel"], + "trace": ["trc_init", "trc_onAmpduOp"], + "country_code": ["country_info", "country_info_24ghz"], + "multicore": ["do_multicore_settings"], + "Update_lib": ["Update"], + "stdio": [ + "__sf", + "__sflush_r", + "__srefill_r", + "_impure_data", + "_reclaim_reent", + "_open_r", + ], + "strncpy_ops": ["strncpy"], + "math_internal": ["__mdiff", "__lshift", "__mprec_tens", "quorem"], + "character_class": ["__chclass"], + "camellia": ["camellia_", "camellia_feistel"], + "crypto_tables": ["FSb", "FSb2", "FSb3", "FSb4"], + "event_buffer": ["g_eb_list_desc", "eb_space"], + "base_node": ["base_node_", "base_node_add_handler"], + "file_descriptor": ["s_fd_table"], + "tx_delay": ["tx_delay_cfg"], + "deinit": ["deinit_functions"], + "lcp_echo": ["LcpEchoCheck"], + "raw_api": ["raw_bind", "raw_connect"], + "checksum": ["process_checksum"], + "entry_management": ["add_entry"], + "esp_ota": ["esp_ota", "ota_", "read_otadata"], + "http_server": [ + "httpd_", + "parse_url_char", + "cb_headers_complete", + "delete_entry", + "validate_structure", + "config_save", + "config_new", + "verify_url", + "cb_url", + ], + "misc_system": [ + "alarm_cbs", + "start_up", + "tokens", + "unhex", + "osi_funcs_ro", + "enum_function", + "fragment_and_dispatch", + "alarm_set", + "osi_alarm_new", + "config_set_string", + "config_update_newest_section", + "config_remove_key", + "method_strings", + "interop_match", + "interop_database", + "__state_table", + "__action_table", + "s_stub_table", + "s_context", + "s_mmu_ctx", + "s_get_bus_mask", + "hli_queue_put", + "list_remove", + "list_delete", + "lock_acquire_generic", + "is_vect_desc_usable", + "io_mode_str", + "__c$20233", + "interface", + "read_id_core", + "subscribe_idle", + "unsubscribe_idle", + "s_clkout_handle", + "lock_release_generic", + "config_set_int", + "config_get_int", + "config_get_string", + "config_has_key", + "config_remove_section", + "osi_alarm_init", + "osi_alarm_deinit", + "fixed_queue_enqueue", + "fixed_queue_dequeue", + "fixed_queue_new", + "fixed_pkt_queue_enqueue", + "fixed_pkt_queue_new", + "list_append", + "list_prepend", + "list_insert_after", + "list_contains", + "list_get_node", + "hash_function_blob", + "cb_no_body", + "cb_on_body", + "profile_tab", + "get_arg", + "trim", + "buf$", + "process_appended_hash_and_sig$constprop$0", + "uuidType", + "allocate_svc_db_buf", + "_hostname_is_ours", + "s_hli_handlers", + "tick_cb", + "idle_cb", + "input", + "entry_find", + "section_find", + "find_bucket_entry_", + "config_has_section", + "hli_queue_create", + "hli_queue_get", + "hli_c_handler", + "future_ready", + "future_await", + "future_new", + "pkt_queue_enqueue", + "pkt_queue_dequeue", + "pkt_queue_cleanup", + "pkt_queue_create", + "pkt_queue_destroy", + "fixed_pkt_queue_dequeue", + "osi_alarm_cancel", + "osi_alarm_is_active", + "osi_sem_take", + "osi_event_create", + "osi_event_bind", + "alarm_cb_handler", + "list_foreach", + "list_back", + "list_front", + "list_clear", + "fixed_queue_try_peek_first", + "translate_path", + "get_idx", + "find_key", + "init", + "end", + "start", + "set_read_value", + "copy_address_list", + "copy_and_key", + "sdk_cfg_opts", + "leftshift_onebit", + "config_section_end", + "config_section_begin", + "find_entry_and_check_all_reset", + "image_validate", + "xPendingReadyList", + "vListInitialise", + "lock_init_generic", + "ant_bttx_cfg", + "ant_dft_cfg", + "cs_send_to_ctrl_sock", + "config_llc_util_funcs_reset", + "make_set_adv_report_flow_control", + "make_set_event_mask", + "raw_new", + "raw_remove", + "BTE_InitStack", + "parse_read_local_supported_features_response", + "__math_invalidf", + "tinytens", + "__mprec_tinytens", + "__mprec_bigtens", + "vRingbufferDelete", + "vRingbufferDeleteWithCaps", + "vRingbufferReturnItem", + "vRingbufferReturnItemFromISR", + "get_acl_data_size_ble", + "get_features_ble", + "get_features_classic", + "get_acl_packet_size_ble", + "get_acl_packet_size_classic", + "supports_extended_inquiry_response", + "supports_rssi_with_inquiry_results", + "supports_interlaced_inquiry_scan", + "supports_reading_remote_extended_features", + ], + "bluetooth_ll": [ + "lld_pdu_", + "ld_acl_", + "lld_stop_ind_handler", + "lld_evt_winsize_change", + "config_lld_evt_funcs_reset", + "config_lld_funcs_reset", + "config_llm_funcs_reset", + "llm_set_long_adv_data", + "lld_retry_tx_prog", + "llc_link_sup_to_ind_handler", + "config_llc_funcs_reset", + "lld_evt_rxwin_compute", + "config_btdm_funcs_reset", + "config_ea_funcs_reset", + "llc_defalut_state_tab_reset", + "config_rwip_funcs_reset", + "ke_lmp_rx_flooding_detect", + ], +} + +# Demangled patterns: patterns found in demangled C++ names +DEMANGLED_PATTERNS = { + "gpio_driver": ["GPIO"], + "uart_driver": ["UART"], + "network_stack": [ + "lwip", + "tcp", + "udp", + "ip4", + "ip6", + "dhcp", + "dns", + "netif", + "ethernet", + "ppp", + "slip", + ], + "wifi_stack": ["NetworkInterface"], + "nimble_bt": [ + "nimble", + "NimBLE", + "ble_hs", + "ble_gap", + "ble_gatt", + "ble_att", + "ble_l2cap", + "ble_sm", + ], + "crypto": ["mbedtls", "crypto", "sha", "aes", "rsa", "ecc", "tls", "ssl"], + "cpp_stdlib": ["std::", "__gnu_cxx::", "__cxxabiv"], + "static_init": ["__static_initialization"], + "rtti": ["__type_info", "__class_type_info"], + "web_server_lib": ["AsyncWebServer", "AsyncWebHandler", "WebServer"], + "async_tcp": ["AsyncClient", "AsyncServer"], + "mdns_lib": ["mdns"], + "json_lib": [ + "ArduinoJson", + "JsonDocument", + "JsonArray", + "JsonObject", + "deserialize", + "serialize", + ], + "http_lib": ["HTTP", "http_", "Request", "Response", "Uri", "WebSocket"], + "logging": ["log", "Log", "print", "Print", "diag_"], + "authentication": ["checkDigestAuthentication"], + "libgcc": ["libgcc"], + "esp_system": ["esp_", "ESP"], + "arduino": ["arduino"], + "nvs": ["nvs_", "_ZTVN3nvs", "nvs::"], + "filesystem": ["spiffs", "vfs"], + "libc": ["newlib"], +} + +# Patterns for categorizing ESPHome core symbols into subcategories +CORE_SUBCATEGORY_PATTERNS = { + "Component Framework": ["Component"], + "Application Core": ["Application"], + "Scheduler": ["Scheduler"], + "Component Iterator": ["ComponentIterator"], + "Helper Functions": ["Helpers", "helpers"], + "Preferences/Storage": ["Preferences", "ESPPreferences"], + "I/O Utilities": ["HighFrequencyLoopRequester"], + "String Utilities": ["str_"], + "Bit Utilities": ["reverse_bits"], + "Data Conversion": ["convert_"], + "Network Utilities": ["network", "IPAddress"], + "API Protocol": ["api::"], + "WiFi Manager": ["wifi::"], + "MQTT Client": ["mqtt::"], + "Logger": ["logger::"], + "OTA Updates": ["ota::"], + "Web Server": ["web_server::"], + "Time Management": ["time::"], + "Sensor Framework": ["sensor::"], + "Binary Sensor": ["binary_sensor::"], + "Switch Framework": ["switch_::"], + "Light Framework": ["light::"], + "Climate Framework": ["climate::"], + "Cover Framework": ["cover::"], +} diff --git a/esphome/analyze_memory/helpers.py b/esphome/analyze_memory/helpers.py new file mode 100644 index 0000000000..cb503b37c5 --- /dev/null +++ b/esphome/analyze_memory/helpers.py @@ -0,0 +1,121 @@ +"""Helper functions for memory analysis.""" + +from functools import cache +from pathlib import Path + +from .const import SECTION_MAPPING + +# Import namespace constant from parent module +# Note: This would create a circular import if done at module level, +# so we'll define it locally here as well +_NAMESPACE_ESPHOME = "esphome::" + + +# Get the list of actual ESPHome components by scanning the components directory +@cache +def get_esphome_components(): + """Get set of actual ESPHome components from the components directory.""" + # Find the components directory relative to this file + # Go up two levels from analyze_memory/helpers.py to esphome/ + current_dir = Path(__file__).parent.parent + components_dir = current_dir / "components" + + if not components_dir.exists() or not components_dir.is_dir(): + return frozenset() + + return frozenset( + item.name + for item in components_dir.iterdir() + if item.is_dir() + and not item.name.startswith(".") + and not item.name.startswith("__") + ) + + +@cache +def get_component_class_patterns(component_name: str) -> list[str]: + """Generate component class name patterns for symbol matching. + + Args: + component_name: The component name (e.g., "ota", "wifi", "api") + + Returns: + List of pattern strings to match against demangled symbols + """ + component_upper = component_name.upper() + component_camel = component_name.replace("_", "").title() + return [ + f"{_NAMESPACE_ESPHOME}{component_upper}Component", # e.g., esphome::OTAComponent + f"{_NAMESPACE_ESPHOME}ESPHome{component_upper}Component", # e.g., esphome::ESPHomeOTAComponent + f"{_NAMESPACE_ESPHOME}{component_camel}Component", # e.g., esphome::OtaComponent + f"{_NAMESPACE_ESPHOME}ESPHome{component_camel}Component", # e.g., esphome::ESPHomeOtaComponent + ] + + +def map_section_name(raw_section: str) -> str | None: + """Map raw section name to standard section. + + Args: + raw_section: Raw section name from ELF file (e.g., ".iram0.text", ".rodata.str1.1") + + Returns: + Standard section name (".text", ".rodata", ".data", ".bss") or None + """ + for standard_section, patterns in SECTION_MAPPING.items(): + if any(pattern in raw_section for pattern in patterns): + return standard_section + return None + + +def parse_symbol_line(line: str) -> tuple[str, str, int, str] | None: + """Parse a single symbol line from objdump output. + + Args: + line: Line from objdump -t output + + Returns: + Tuple of (section, name, size, address) or None if not a valid symbol. + Format: address l/g w/d F/O section size name + Example: 40084870 l F .iram0.text 00000000 _xt_user_exc + """ + parts = line.split() + if len(parts) < 5: + return None + + try: + # Validate and extract address + address = parts[0] + int(address, 16) + except ValueError: + return None + + # Look for F (function) or O (object) flag + if "F" not in parts and "O" not in parts: + return None + + # Find section, size, and name + for i, part in enumerate(parts): + if not part.startswith("."): + continue + + section = map_section_name(part) + if not section: + break + + # Need at least size field after section + if i + 1 >= len(parts): + break + + try: + size = int(parts[i + 1], 16) + except ValueError: + break + + # Need symbol name and non-zero size + if i + 2 >= len(parts) or size == 0: + break + + name = " ".join(parts[i + 2 :]) + return (section, name, size, address) + + return None diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 9418c1c7d3..d59523a74a 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -374,3 +374,23 @@ class IDEData: return f"{self.cc_path[:-7]}addr2line.exe" return f"{self.cc_path[:-3]}addr2line" + + @property + def objdump_path(self) -> str: + # replace gcc at end with objdump + path = self.cc_path + return ( + f"{path[:-7]}objdump.exe" + if path.endswith(".exe") + else f"{path[:-3]}objdump" + ) + + @property + def readelf_path(self) -> str: + # replace gcc at end with readelf + path = self.cc_path + return ( + f"{path[:-7]}readelf.exe" + if path.endswith(".exe") + else f"{path[:-3]}readelf" + ) diff --git a/script/analyze_component_buses.py b/script/analyze_component_buses.py index d0882e22e9..78f5ca3344 100755 --- a/script/analyze_component_buses.py +++ b/script/analyze_component_buses.py @@ -34,6 +34,8 @@ from typing import Any # Add esphome to path sys.path.insert(0, str(Path(__file__).parent.parent)) +from helpers import BASE_BUS_COMPONENTS + from esphome import yaml_util from esphome.config_helpers import Extend, Remove @@ -67,18 +69,6 @@ NO_BUSES_SIGNATURE = "no_buses" # Isolated components have unique signatures and cannot be merged with others ISOLATED_SIGNATURE_PREFIX = "isolated_" -# Base bus components - these ARE the bus implementations and should not -# be flagged as needing migration since they are the platform/base components -BASE_BUS_COMPONENTS = { - "i2c", - "spi", - "uart", - "modbus", - "canbus", - "remote_transmitter", - "remote_receiver", -} - # Components that must be tested in isolation (not grouped or batched with others) # These have known build issues that prevent grouping # NOTE: This should be kept in sync with both test_build_components and split_components_for_ci.py diff --git a/script/ci_helpers.py b/script/ci_helpers.py new file mode 100755 index 0000000000..48b0e4bbfe --- /dev/null +++ b/script/ci_helpers.py @@ -0,0 +1,23 @@ +"""Common helper functions for CI scripts.""" + +from __future__ import annotations + +import os + + +def write_github_output(outputs: dict[str, str | int]) -> None: + """Write multiple outputs to GITHUB_OUTPUT or stdout. + + When running in GitHub Actions, writes to the GITHUB_OUTPUT file. + When running locally, writes to stdout for debugging. + + Args: + outputs: Dictionary of key-value pairs to write + """ + github_output = os.environ.get("GITHUB_OUTPUT") + if github_output: + with open(github_output, "a", encoding="utf-8") as f: + f.writelines(f"{key}={value}\n" for key, value in outputs.items()) + else: + for key, value in outputs.items(): + print(f"{key}={value}") diff --git a/script/ci_memory_impact_comment.py b/script/ci_memory_impact_comment.py new file mode 100755 index 0000000000..4e3fbb9086 --- /dev/null +++ b/script/ci_memory_impact_comment.py @@ -0,0 +1,570 @@ +#!/usr/bin/env python3 +"""Post or update a PR comment with memory impact analysis results. + +This script creates or updates a GitHub PR comment with memory usage changes. +It uses the GitHub CLI (gh) to manage comments and maintains a single comment +that gets updated on subsequent runs. +""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import subprocess +import sys + +from jinja2 import Environment, FileSystemLoader + +# Add esphome to path for analyze_memory import +sys.path.insert(0, str(Path(__file__).parent.parent)) + +# pylint: disable=wrong-import-position + +# Comment marker to identify our memory impact comments +COMMENT_MARKER = "" + +# Thresholds for emoji significance indicators (percentage) +OVERALL_CHANGE_THRESHOLD = 1.0 # Overall RAM/Flash changes +COMPONENT_CHANGE_THRESHOLD = 3.0 # Component breakdown changes + +# Display limits for tables +MAX_COMPONENT_BREAKDOWN_ROWS = 20 # Maximum components to show in breakdown table +MAX_CHANGED_SYMBOLS_ROWS = 30 # Maximum changed symbols to show +MAX_NEW_SYMBOLS_ROWS = 15 # Maximum new symbols to show +MAX_REMOVED_SYMBOLS_ROWS = 15 # Maximum removed symbols to show + +# Symbol display formatting +SYMBOL_DISPLAY_MAX_LENGTH = 100 # Max length before using
tag +SYMBOL_DISPLAY_TRUNCATE_LENGTH = 97 # Length to truncate in summary + +# Component change noise threshold +COMPONENT_CHANGE_NOISE_THRESHOLD = 2 # Ignore component changes ≤ this many bytes + +# Template directory +TEMPLATE_DIR = Path(__file__).parent / "templates" + + +def load_analysis_json(json_path: str) -> dict | None: + """Load memory analysis results from JSON file. + + Args: + json_path: Path to analysis JSON file + + Returns: + Dictionary with analysis results or None if file doesn't exist/can't be loaded + """ + json_file = Path(json_path) + if not json_file.exists(): + print(f"Analysis JSON not found: {json_path}", file=sys.stderr) + return None + + try: + with open(json_file, encoding="utf-8") as f: + return json.load(f) + except (json.JSONDecodeError, OSError) as e: + print(f"Failed to load analysis JSON: {e}", file=sys.stderr) + return None + + +def format_bytes(bytes_value: int) -> str: + """Format bytes value with comma separators. + + Args: + bytes_value: Number of bytes + + Returns: + Formatted string with comma separators (e.g., "1,234 bytes") + """ + return f"{bytes_value:,} bytes" + + +def format_change(before: int, after: int, threshold: float | None = None) -> str: + """Format memory change with delta and percentage. + + Args: + before: Memory usage before change (in bytes) + after: Memory usage after change (in bytes) + threshold: Optional percentage threshold for "significant" change. + If provided, adds supplemental emoji (🎉/🚨/🔸/✅) to chart icons. + If None, only shows chart icons (📈/📉/➡️). + + Returns: + Formatted string with delta and percentage + """ + delta = after - before + percentage = 0.0 if before == 0 else (delta / before) * 100 + + # Always use chart icons to show direction + if delta > 0: + delta_str = f"+{delta:,} bytes" + trend_icon = "📈" + # Add supplemental emoji based on threshold if provided + if threshold is not None: + significance = "🚨" if abs(percentage) > threshold else "🔸" + emoji = f"{trend_icon} {significance}" + else: + emoji = trend_icon + elif delta < 0: + delta_str = f"{delta:,} bytes" + trend_icon = "📉" + # Add supplemental emoji based on threshold if provided + if threshold is not None: + significance = "🎉" if abs(percentage) > threshold else "✅" + emoji = f"{trend_icon} {significance}" + else: + emoji = trend_icon + else: + delta_str = "+0 bytes" + emoji = "➡️" + + # Format percentage with sign + if percentage > 0: + pct_str = f"+{percentage:.2f}%" + elif percentage < 0: + pct_str = f"{percentage:.2f}%" + else: + pct_str = "0.00%" + + return f"{emoji} {delta_str} ({pct_str})" + + +def prepare_symbol_changes_data( + target_symbols: dict | None, pr_symbols: dict | None +) -> dict | None: + """Prepare symbol changes data for template rendering. + + Args: + target_symbols: Symbol name to size mapping for target branch + pr_symbols: Symbol name to size mapping for PR branch + + Returns: + Dictionary with changed, new, and removed symbols, or None if no changes + """ + if not target_symbols or not pr_symbols: + return None + + # Find all symbols that exist in both branches or only in one + all_symbols = set(target_symbols.keys()) | set(pr_symbols.keys()) + + # Track changes + changed_symbols: list[ + tuple[str, int, int, int] + ] = [] # (symbol, target_size, pr_size, delta) + new_symbols: list[tuple[str, int]] = [] # (symbol, size) + removed_symbols: list[tuple[str, int]] = [] # (symbol, size) + + for symbol in all_symbols: + target_size = target_symbols.get(symbol, 0) + pr_size = pr_symbols.get(symbol, 0) + + if target_size == 0 and pr_size > 0: + # New symbol + new_symbols.append((symbol, pr_size)) + elif target_size > 0 and pr_size == 0: + # Removed symbol + removed_symbols.append((symbol, target_size)) + elif target_size != pr_size: + # Changed symbol + delta = pr_size - target_size + changed_symbols.append((symbol, target_size, pr_size, delta)) + + if not changed_symbols and not new_symbols and not removed_symbols: + return None + + # Sort by size/delta + changed_symbols.sort(key=lambda x: abs(x[3]), reverse=True) + new_symbols.sort(key=lambda x: x[1], reverse=True) + removed_symbols.sort(key=lambda x: x[1], reverse=True) + + return { + "changed_symbols": changed_symbols, + "new_symbols": new_symbols, + "removed_symbols": removed_symbols, + } + + +def prepare_component_breakdown_data( + target_analysis: dict | None, pr_analysis: dict | None +) -> list[tuple[str, int, int, int]] | None: + """Prepare component breakdown data for template rendering. + + Args: + target_analysis: Component memory breakdown for target branch + pr_analysis: Component memory breakdown for PR branch + + Returns: + List of tuples (component, target_flash, pr_flash, delta), or None if no changes + """ + if not target_analysis or not pr_analysis: + return None + + # Combine all components from both analyses + all_components = set(target_analysis.keys()) | set(pr_analysis.keys()) + + # Filter to components that have changed (ignoring noise) + changed_components: list[ + tuple[str, int, int, int] + ] = [] # (comp, target_flash, pr_flash, delta) + for comp in all_components: + target_mem = target_analysis.get(comp, {}) + pr_mem = pr_analysis.get(comp, {}) + + target_flash = target_mem.get("flash_total", 0) + pr_flash = pr_mem.get("flash_total", 0) + + # Only include if component has meaningful change (above noise threshold) + delta = pr_flash - target_flash + if abs(delta) > COMPONENT_CHANGE_NOISE_THRESHOLD: + changed_components.append((comp, target_flash, pr_flash, delta)) + + if not changed_components: + return None + + # Sort by absolute delta (largest changes first) + changed_components.sort(key=lambda x: abs(x[3]), reverse=True) + + return changed_components + + +def create_comment_body( + components: list[str], + platform: str, + target_ram: int, + target_flash: int, + pr_ram: int, + pr_flash: int, + target_analysis: dict | None = None, + pr_analysis: dict | None = None, + target_symbols: dict | None = None, + pr_symbols: dict | None = None, + target_cache_hit: bool = False, +) -> str: + """Create the comment body with memory impact analysis using Jinja2 templates. + + Args: + components: List of component names (merged config) + platform: Platform name + target_ram: RAM usage in target branch + target_flash: Flash usage in target branch + pr_ram: RAM usage in PR branch + pr_flash: Flash usage in PR branch + target_analysis: Optional component breakdown for target branch + pr_analysis: Optional component breakdown for PR branch + target_symbols: Optional symbol map for target branch + pr_symbols: Optional symbol map for PR branch + target_cache_hit: Whether target branch analysis was loaded from cache + + Returns: + Formatted comment body + """ + # Set up Jinja2 environment + env = Environment( + loader=FileSystemLoader(TEMPLATE_DIR), + trim_blocks=True, + lstrip_blocks=True, + ) + + # Register custom filters + env.filters["format_bytes"] = format_bytes + env.filters["format_change"] = format_change + + # Prepare template context + context = { + "comment_marker": COMMENT_MARKER, + "platform": platform, + "target_ram": format_bytes(target_ram), + "pr_ram": format_bytes(pr_ram), + "target_flash": format_bytes(target_flash), + "pr_flash": format_bytes(pr_flash), + "ram_change": format_change( + target_ram, pr_ram, threshold=OVERALL_CHANGE_THRESHOLD + ), + "flash_change": format_change( + target_flash, pr_flash, threshold=OVERALL_CHANGE_THRESHOLD + ), + "target_cache_hit": target_cache_hit, + "component_change_threshold": COMPONENT_CHANGE_THRESHOLD, + } + + # Format components list + if len(components) == 1: + context["components_str"] = f"`{components[0]}`" + context["config_note"] = "a representative test configuration" + else: + context["components_str"] = ", ".join(f"`{c}`" for c in sorted(components)) + context["config_note"] = ( + f"a merged configuration with {len(components)} components" + ) + + # Prepare component breakdown if available + component_breakdown = "" + if target_analysis and pr_analysis: + changed_components = prepare_component_breakdown_data( + target_analysis, pr_analysis + ) + if changed_components: + template = env.get_template("ci_memory_impact_component_breakdown.j2") + component_breakdown = template.render( + changed_components=changed_components, + format_bytes=format_bytes, + format_change=format_change, + component_change_threshold=COMPONENT_CHANGE_THRESHOLD, + max_rows=MAX_COMPONENT_BREAKDOWN_ROWS, + ) + + # Prepare symbol changes if available + symbol_changes = "" + if target_symbols and pr_symbols: + symbol_data = prepare_symbol_changes_data(target_symbols, pr_symbols) + if symbol_data: + template = env.get_template("ci_memory_impact_symbol_changes.j2") + symbol_changes = template.render( + **symbol_data, + format_bytes=format_bytes, + format_change=format_change, + max_changed_rows=MAX_CHANGED_SYMBOLS_ROWS, + max_new_rows=MAX_NEW_SYMBOLS_ROWS, + max_removed_rows=MAX_REMOVED_SYMBOLS_ROWS, + symbol_max_length=SYMBOL_DISPLAY_MAX_LENGTH, + symbol_truncate_length=SYMBOL_DISPLAY_TRUNCATE_LENGTH, + ) + + if not target_analysis or not pr_analysis: + print("No ELF files provided, skipping detailed analysis", file=sys.stderr) + + context["component_breakdown"] = component_breakdown + context["symbol_changes"] = symbol_changes + + # Render main template + template = env.get_template("ci_memory_impact_comment_template.j2") + return template.render(**context) + + +def find_existing_comment(pr_number: str) -> str | None: + """Find existing memory impact comment on the PR. + + Args: + pr_number: PR number + + Returns: + Comment numeric ID if found, None otherwise + + Raises: + subprocess.CalledProcessError: If gh command fails + """ + print(f"DEBUG: Looking for existing comment on PR #{pr_number}", file=sys.stderr) + + # Use gh api to get comments directly - this returns the numeric id field + result = subprocess.run( + [ + "gh", + "api", + f"/repos/{{owner}}/{{repo}}/issues/{pr_number}/comments", + "--jq", + ".[] | {id, body}", + ], + capture_output=True, + text=True, + check=True, + ) + + print( + f"DEBUG: gh api comments output (first 500 chars):\n{result.stdout[:500]}", + file=sys.stderr, + ) + + # Parse comments and look for our marker + comment_count = 0 + for line in result.stdout.strip().split("\n"): + if not line: + continue + + try: + comment = json.loads(line) + comment_count += 1 + comment_id = comment.get("id") + print( + f"DEBUG: Checking comment {comment_count}: id={comment_id}", + file=sys.stderr, + ) + + body = comment.get("body", "") + if COMMENT_MARKER in body: + print( + f"DEBUG: Found existing comment with id={comment_id}", + file=sys.stderr, + ) + # Return the numeric id + return str(comment_id) + print("DEBUG: Comment does not contain marker", file=sys.stderr) + except json.JSONDecodeError as e: + print(f"DEBUG: JSON decode error: {e}", file=sys.stderr) + continue + + print( + f"DEBUG: No existing comment found (checked {comment_count} comments)", + file=sys.stderr, + ) + return None + + +def update_existing_comment(comment_id: str, comment_body: str) -> None: + """Update an existing comment. + + Args: + comment_id: Comment ID to update + comment_body: New comment body text + + Raises: + subprocess.CalledProcessError: If gh command fails + """ + print(f"DEBUG: Updating existing comment {comment_id}", file=sys.stderr) + result = subprocess.run( + [ + "gh", + "api", + f"/repos/{{owner}}/{{repo}}/issues/comments/{comment_id}", + "-X", + "PATCH", + "-f", + f"body={comment_body}", + ], + check=True, + capture_output=True, + text=True, + ) + print(f"DEBUG: Update response: {result.stdout}", file=sys.stderr) + + +def create_new_comment(pr_number: str, comment_body: str) -> None: + """Create a new PR comment. + + Args: + pr_number: PR number + comment_body: Comment body text + + Raises: + subprocess.CalledProcessError: If gh command fails + """ + print(f"DEBUG: Posting new comment on PR #{pr_number}", file=sys.stderr) + result = subprocess.run( + ["gh", "pr", "comment", pr_number, "--body", comment_body], + check=True, + capture_output=True, + text=True, + ) + print(f"DEBUG: Post response: {result.stdout}", file=sys.stderr) + + +def post_or_update_comment(pr_number: str, comment_body: str) -> None: + """Post a new comment or update existing one. + + Args: + pr_number: PR number + comment_body: Comment body text + + Raises: + subprocess.CalledProcessError: If gh command fails + """ + # Look for existing comment + existing_comment_id = find_existing_comment(pr_number) + + if existing_comment_id and existing_comment_id != "None": + update_existing_comment(existing_comment_id, comment_body) + else: + create_new_comment(pr_number, comment_body) + + print("Comment posted/updated successfully", file=sys.stderr) + + +def main() -> int: + """Main entry point.""" + parser = argparse.ArgumentParser( + description="Post or update PR comment with memory impact analysis" + ) + parser.add_argument("--pr-number", required=True, help="PR number") + parser.add_argument( + "--components", + required=True, + help='JSON array of component names (e.g., \'["api", "wifi"]\')', + ) + parser.add_argument("--platform", required=True, help="Platform name") + parser.add_argument( + "--target-ram", type=int, required=True, help="Target branch RAM usage" + ) + parser.add_argument( + "--target-flash", type=int, required=True, help="Target branch flash usage" + ) + parser.add_argument("--pr-ram", type=int, required=True, help="PR branch RAM usage") + parser.add_argument( + "--pr-flash", type=int, required=True, help="PR branch flash usage" + ) + parser.add_argument( + "--target-json", + help="Optional path to target branch analysis JSON (for detailed analysis)", + ) + parser.add_argument( + "--pr-json", + help="Optional path to PR branch analysis JSON (for detailed analysis)", + ) + parser.add_argument( + "--target-cache-hit", + action="store_true", + help="Indicates that target branch analysis was loaded from cache", + ) + + args = parser.parse_args() + + # Parse components from JSON + try: + components = json.loads(args.components) + if not isinstance(components, list): + print("Error: --components must be a JSON array", file=sys.stderr) + sys.exit(1) + except json.JSONDecodeError as e: + print(f"Error parsing --components JSON: {e}", file=sys.stderr) + sys.exit(1) + + # Load analysis JSON files + target_analysis = None + pr_analysis = None + target_symbols = None + pr_symbols = None + + if args.target_json: + target_data = load_analysis_json(args.target_json) + if target_data and target_data.get("detailed_analysis"): + target_analysis = target_data["detailed_analysis"].get("components") + target_symbols = target_data["detailed_analysis"].get("symbols") + + if args.pr_json: + pr_data = load_analysis_json(args.pr_json) + if pr_data and pr_data.get("detailed_analysis"): + pr_analysis = pr_data["detailed_analysis"].get("components") + pr_symbols = pr_data["detailed_analysis"].get("symbols") + + # Create comment body + # Note: Memory totals (RAM/Flash) are summed across all builds if multiple were run. + comment_body = create_comment_body( + components=components, + platform=args.platform, + target_ram=args.target_ram, + target_flash=args.target_flash, + pr_ram=args.pr_ram, + pr_flash=args.pr_flash, + target_analysis=target_analysis, + pr_analysis=pr_analysis, + target_symbols=target_symbols, + pr_symbols=pr_symbols, + target_cache_hit=args.target_cache_hit, + ) + + # Post or update comment + post_or_update_comment(args.pr_number, comment_body) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/script/ci_memory_impact_extract.py b/script/ci_memory_impact_extract.py new file mode 100755 index 0000000000..77d59417e3 --- /dev/null +++ b/script/ci_memory_impact_extract.py @@ -0,0 +1,281 @@ +#!/usr/bin/env python3 +"""Extract memory usage statistics from ESPHome build output. + +This script parses the PlatformIO build output to extract RAM and flash +usage statistics for a compiled component. It's used by the CI workflow to +compare memory usage between branches. + +The script reads compile output from stdin and looks for the standard +PlatformIO output format: + RAM: [==== ] 36.1% (used 29548 bytes from 81920 bytes) + Flash: [=== ] 34.0% (used 348511 bytes from 1023984 bytes) + +Optionally performs detailed memory analysis if a build directory is provided. +""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import re +import sys + +# Add esphome to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +# pylint: disable=wrong-import-position +from esphome.analyze_memory import MemoryAnalyzer +from esphome.platformio_api import IDEData +from script.ci_helpers import write_github_output + +# Regex patterns for extracting memory usage from PlatformIO output +_RAM_PATTERN = re.compile(r"RAM:\s+\[.*?\]\s+\d+\.\d+%\s+\(used\s+(\d+)\s+bytes") +_FLASH_PATTERN = re.compile(r"Flash:\s+\[.*?\]\s+\d+\.\d+%\s+\(used\s+(\d+)\s+bytes") +_BUILD_PATH_PATTERN = re.compile(r"Build path: (.+)") + + +def extract_from_compile_output( + output_text: str, +) -> tuple[int | None, int | None, str | None]: + """Extract memory usage and build directory from PlatformIO compile output. + + Supports multiple builds (for component groups or isolated components). + When test_build_components.py creates multiple builds, this sums the + memory usage across all builds. + + Looks for lines like: + RAM: [==== ] 36.1% (used 29548 bytes from 81920 bytes) + Flash: [=== ] 34.0% (used 348511 bytes from 1023984 bytes) + + Also extracts build directory from lines like: + INFO Compiling app... Build path: /path/to/build + + Args: + output_text: Compile output text (may contain multiple builds) + + Returns: + Tuple of (total_ram_bytes, total_flash_bytes, build_dir) or (None, None, None) if not found + """ + # Find all RAM and Flash matches (may be multiple builds) + ram_matches = _RAM_PATTERN.findall(output_text) + flash_matches = _FLASH_PATTERN.findall(output_text) + + if not ram_matches or not flash_matches: + return None, None, None + + # Sum all builds (handles multiple component groups) + total_ram = sum(int(match) for match in ram_matches) + total_flash = sum(int(match) for match in flash_matches) + + # Extract build directory from ESPHome's explicit build path output + # Look for: INFO Compiling app... Build path: /path/to/build + # Note: Multiple builds reuse the same build path (each overwrites the previous) + build_dir = None + if match := _BUILD_PATH_PATTERN.search(output_text): + build_dir = match.group(1).strip() + + return total_ram, total_flash, build_dir + + +def run_detailed_analysis(build_dir: str) -> dict | None: + """Run detailed memory analysis on build directory. + + Args: + build_dir: Path to ESPHome build directory + + Returns: + Dictionary with analysis results or None if analysis fails + """ + build_path = Path(build_dir) + if not build_path.exists(): + print(f"Build directory not found: {build_dir}", file=sys.stderr) + return None + + # Find firmware.elf + elf_path = None + for elf_candidate in [ + build_path / "firmware.elf", + build_path / ".pioenvs" / build_path.name / "firmware.elf", + ]: + if elf_candidate.exists(): + elf_path = str(elf_candidate) + break + + if not elf_path: + print(f"firmware.elf not found in {build_dir}", file=sys.stderr) + return None + + # Find idedata.json - check multiple locations + device_name = build_path.name + idedata_candidates = [ + # In .pioenvs for test builds + build_path / ".pioenvs" / device_name / "idedata.json", + # In .esphome/idedata for regular builds + Path.home() / ".esphome" / "idedata" / f"{device_name}.json", + # Check parent directories for .esphome/idedata (for test_build_components) + build_path.parent.parent.parent / "idedata" / f"{device_name}.json", + ] + + idedata = None + for idedata_path in idedata_candidates: + if not idedata_path.exists(): + continue + try: + with open(idedata_path, encoding="utf-8") as f: + raw_data = json.load(f) + idedata = IDEData(raw_data) + print(f"Loaded idedata from: {idedata_path}", file=sys.stderr) + break + except (json.JSONDecodeError, OSError) as e: + print( + f"Warning: Failed to load idedata from {idedata_path}: {e}", + file=sys.stderr, + ) + + analyzer = MemoryAnalyzer(elf_path, idedata=idedata) + components = analyzer.analyze() + + # Convert to JSON-serializable format + result = { + "components": { + name: { + "text": mem.text_size, + "rodata": mem.rodata_size, + "data": mem.data_size, + "bss": mem.bss_size, + "flash_total": mem.flash_total, + "ram_total": mem.ram_total, + "symbol_count": mem.symbol_count, + } + for name, mem in components.items() + }, + "symbols": {}, + } + + # Build symbol map + for section in analyzer.sections.values(): + for symbol_name, size, _ in section.symbols: + if size > 0: + demangled = analyzer._demangle_symbol(symbol_name) + result["symbols"][demangled] = size + + return result + + +def main() -> int: + """Main entry point.""" + parser = argparse.ArgumentParser( + description="Extract memory usage from ESPHome build output" + ) + parser.add_argument( + "--output-env", + action="store_true", + help="Output to GITHUB_OUTPUT environment file", + ) + parser.add_argument( + "--build-dir", + help="Optional build directory for detailed memory analysis (overrides auto-detection)", + ) + parser.add_argument( + "--output-json", + help="Optional path to save detailed analysis JSON", + ) + parser.add_argument( + "--output-build-dir", + help="Optional path to write the detected build directory", + ) + + args = parser.parse_args() + + # Read compile output from stdin + compile_output = sys.stdin.read() + + # Extract memory usage and build directory + ram_bytes, flash_bytes, detected_build_dir = extract_from_compile_output( + compile_output + ) + + if ram_bytes is None or flash_bytes is None: + print("Failed to extract memory usage from compile output", file=sys.stderr) + print("Expected lines like:", file=sys.stderr) + print( + " RAM: [==== ] 36.1% (used 29548 bytes from 81920 bytes)", + file=sys.stderr, + ) + print( + " Flash: [=== ] 34.0% (used 348511 bytes from 1023984 bytes)", + file=sys.stderr, + ) + return 1 + + # Count how many builds were found + num_builds = len(_RAM_PATTERN.findall(compile_output)) + + if num_builds > 1: + print( + f"Found {num_builds} builds - summing memory usage across all builds", + file=sys.stderr, + ) + print( + "WARNING: Detailed analysis will only cover the last build", + file=sys.stderr, + ) + + print(f"Total RAM: {ram_bytes} bytes", file=sys.stderr) + print(f"Total Flash: {flash_bytes} bytes", file=sys.stderr) + + # Determine which build directory to use (explicit arg overrides auto-detection) + build_dir = args.build_dir or detected_build_dir + + if detected_build_dir: + print(f"Detected build directory: {detected_build_dir}", file=sys.stderr) + if num_builds > 1: + print( + f" (using last of {num_builds} builds for detailed analysis)", + file=sys.stderr, + ) + + # Write build directory to file if requested + if args.output_build_dir and build_dir: + build_dir_path = Path(args.output_build_dir) + build_dir_path.parent.mkdir(parents=True, exist_ok=True) + build_dir_path.write_text(build_dir) + print(f"Wrote build directory to {args.output_build_dir}", file=sys.stderr) + + # Run detailed analysis if build directory available + detailed_analysis = None + if build_dir: + print(f"Running detailed analysis on {build_dir}", file=sys.stderr) + detailed_analysis = run_detailed_analysis(build_dir) + + # Save JSON output if requested + if args.output_json: + output_data = { + "ram_bytes": ram_bytes, + "flash_bytes": flash_bytes, + "detailed_analysis": detailed_analysis, + } + + output_path = Path(args.output_json) + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, "w", encoding="utf-8") as f: + json.dump(output_data, f, indent=2) + print(f"Saved analysis to {args.output_json}", file=sys.stderr) + + if args.output_env: + # Output to GitHub Actions + write_github_output( + { + "ram_usage": ram_bytes, + "flash_usage": flash_bytes, + } + ) + else: + print(f"{ram_bytes},{flash_bytes}") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index b000ecee3b..570b1a762c 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -10,7 +10,13 @@ what files have changed. It outputs JSON with the following structure: "clang_format": true/false, "python_linters": true/false, "changed_components": ["component1", "component2", ...], - "component_test_count": 5 + "component_test_count": 5, + "memory_impact": { + "should_run": "true/false", + "components": ["component1", "component2", ...], + "platform": "esp32-idf", + "use_merged_config": "true" + } } The CI workflow uses this information to: @@ -20,6 +26,7 @@ The CI workflow uses this information to: - Skip or run Python linters (ruff, flake8, pylint, pyupgrade) - Determine which components to test individually - Decide how to split component tests (if there are many) +- Run memory impact analysis whenever there are changed components (merged config), and also for core-only changes Usage: python script/determine-jobs.py [-b BRANCH] @@ -31,6 +38,8 @@ Options: from __future__ import annotations import argparse +from collections import Counter +from enum import StrEnum from functools import cache import json import os @@ -40,16 +49,47 @@ import sys from typing import Any from helpers import ( + BASE_BUS_COMPONENTS, CPP_FILE_EXTENSIONS, - ESPHOME_COMPONENTS_PATH, PYTHON_FILE_EXTENSIONS, changed_files, get_all_dependencies, + get_component_from_path, + get_component_test_files, get_components_from_integration_fixtures, + parse_test_filename, root_path, ) +class Platform(StrEnum): + """Platform identifiers for memory impact analysis.""" + + ESP8266_ARD = "esp8266-ard" + ESP32_IDF = "esp32-idf" + ESP32_C3_IDF = "esp32-c3-idf" + ESP32_C6_IDF = "esp32-c6-idf" + ESP32_S2_IDF = "esp32-s2-idf" + ESP32_S3_IDF = "esp32-s3-idf" + + +# Memory impact analysis constants +MEMORY_IMPACT_FALLBACK_COMPONENT = "api" # Representative component for core changes +MEMORY_IMPACT_FALLBACK_PLATFORM = Platform.ESP32_IDF # Most representative platform + +# Platform preference order for memory impact analysis +# Prefer newer platforms first as they represent the future of ESPHome +# ESP8266 is most constrained but many new features don't support it +MEMORY_IMPACT_PLATFORM_PREFERENCE = [ + Platform.ESP32_C6_IDF, # ESP32-C6 IDF (newest, supports Thread/Zigbee) + Platform.ESP8266_ARD, # ESP8266 Arduino (most memory constrained - best for impact analysis) + Platform.ESP32_IDF, # ESP32 IDF platform (primary ESP32 platform, most representative) + Platform.ESP32_C3_IDF, # ESP32-C3 IDF + Platform.ESP32_S2_IDF, # ESP32-S2 IDF + Platform.ESP32_S3_IDF, # ESP32-S3 IDF +] + + def should_run_integration_tests(branch: str | None = None) -> bool: """Determine if integration tests should run based on changed files. @@ -105,12 +145,9 @@ def should_run_integration_tests(branch: str | None = None) -> bool: # Check if any required components changed for file in files: - if file.startswith(ESPHOME_COMPONENTS_PATH): - parts = file.split("/") - if len(parts) >= 3: - component = parts[2] - if component in all_required_components: - return True + component = get_component_from_path(file) + if component and component in all_required_components: + return True return False @@ -224,10 +261,136 @@ def _component_has_tests(component: str) -> bool: Returns: True if the component has test YAML files """ - tests_dir = Path(root_path) / "tests" / "components" / component - if not tests_dir.exists(): - return False - return any(tests_dir.glob("test.*.yaml")) + return bool(get_component_test_files(component)) + + +def detect_memory_impact_config( + branch: str | None = None, +) -> dict[str, Any]: + """Determine memory impact analysis configuration. + + Always runs memory impact analysis when there are changed components, + building a merged configuration with all changed components (like + test_build_components.py does) to get comprehensive memory analysis. + + Args: + branch: Branch to compare against + + Returns: + Dictionary with memory impact analysis parameters: + - should_run: "true" or "false" + - components: list of component names to analyze + - platform: platform name for the merged build + - use_merged_config: "true" (always use merged config) + """ + + # Get actually changed files (not dependencies) + files = changed_files(branch) + + # Find all changed components (excluding core and base bus components) + changed_component_set: set[str] = set() + has_core_changes = False + + for file in files: + component = get_component_from_path(file) + if component: + # Skip base bus components as they're used across many builds + if component not in BASE_BUS_COMPONENTS: + changed_component_set.add(component) + elif file.startswith("esphome/"): + # Core ESPHome files changed (not component-specific) + has_core_changes = True + + # If no components changed but core changed, test representative component + force_fallback_platform = False + if not changed_component_set and has_core_changes: + print( + f"Memory impact: No components changed, but core files changed. " + f"Testing {MEMORY_IMPACT_FALLBACK_COMPONENT} component on {MEMORY_IMPACT_FALLBACK_PLATFORM}.", + file=sys.stderr, + ) + changed_component_set.add(MEMORY_IMPACT_FALLBACK_COMPONENT) + force_fallback_platform = True # Use fallback platform (most representative) + elif not changed_component_set: + # No components and no core changes + return {"should_run": "false"} + + # Find components that have tests and collect their supported platforms + components_with_tests: list[str] = [] + component_platforms_map: dict[ + str, set[Platform] + ] = {} # Track which platforms each component supports + + for component in sorted(changed_component_set): + # Look for test files on preferred platforms + test_files = get_component_test_files(component) + if not test_files: + continue + + # Check if component has tests for any preferred platform + available_platforms = [ + platform + for test_file in test_files + if (platform := parse_test_filename(test_file)[1]) != "all" + and platform in MEMORY_IMPACT_PLATFORM_PREFERENCE + ] + + if not available_platforms: + continue + + component_platforms_map[component] = set(available_platforms) + components_with_tests.append(component) + + # If no components have tests, don't run memory impact + if not components_with_tests: + return {"should_run": "false"} + + # Find common platforms supported by ALL components + # This ensures we can build all components together in a merged config + common_platforms = set(MEMORY_IMPACT_PLATFORM_PREFERENCE) + for component, platforms in component_platforms_map.items(): + common_platforms &= platforms + + # Select the most preferred platform from the common set + # Exception: for core changes, use fallback platform (most representative of codebase) + if force_fallback_platform: + platform = MEMORY_IMPACT_FALLBACK_PLATFORM + elif common_platforms: + # Pick the most preferred platform that all components support + platform = min(common_platforms, key=MEMORY_IMPACT_PLATFORM_PREFERENCE.index) + else: + # No common platform - pick the most commonly supported platform + # This allows testing components individually even if they can't be merged + # Count how many components support each platform + platform_counts = Counter( + p for platforms in component_platforms_map.values() for p in platforms + ) + # Pick the platform supported by most components, preferring earlier in MEMORY_IMPACT_PLATFORM_PREFERENCE + platform = max( + platform_counts.keys(), + key=lambda p: ( + platform_counts[p], + -MEMORY_IMPACT_PLATFORM_PREFERENCE.index(p), + ), + ) + + # Debug output + print("Memory impact analysis:", file=sys.stderr) + print(f" Changed components: {sorted(changed_component_set)}", file=sys.stderr) + print(f" Components with tests: {components_with_tests}", file=sys.stderr) + print( + f" Component platforms: {dict(sorted(component_platforms_map.items()))}", + file=sys.stderr, + ) + print(f" Common platforms: {sorted(common_platforms)}", file=sys.stderr) + print(f" Selected platform: {platform}", file=sys.stderr) + + return { + "should_run": "true", + "components": components_with_tests, + "platform": platform, + "use_merged_config": "true", + } def main() -> None: @@ -279,6 +442,9 @@ def main() -> None: if component not in directly_changed_components ] + # Detect components for memory impact analysis (merged config) + memory_impact = detect_memory_impact_config(args.branch) + # Build output output: dict[str, Any] = { "integration_tests": run_integration, @@ -292,6 +458,7 @@ def main() -> None: "component_test_count": len(changed_components_with_tests), "directly_changed_count": len(directly_changed_with_tests), "dependency_only_count": len(dependency_only_components), + "memory_impact": memory_impact, } # Output as JSON diff --git a/script/helpers.py b/script/helpers.py index 61306b9489..edde3d78af 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -29,6 +29,18 @@ YAML_FILE_EXTENSIONS = (".yaml", ".yml") # Component path prefix ESPHOME_COMPONENTS_PATH = "esphome/components/" +# Base bus components - these ARE the bus implementations and should not +# be flagged as needing migration since they are the platform/base components +BASE_BUS_COMPONENTS = { + "i2c", + "spi", + "uart", + "modbus", + "canbus", + "remote_transmitter", + "remote_receiver", +} + def parse_list_components_output(output: str) -> list[str]: """Parse the output from list-components.py script. @@ -46,6 +58,65 @@ def parse_list_components_output(output: str) -> list[str]: return [c.strip() for c in output.strip().split("\n") if c.strip()] +def parse_test_filename(test_file: Path) -> tuple[str, str]: + """Parse test filename to extract test name and platform. + + Test files follow the naming pattern: test..yaml or test-..yaml + + Args: + test_file: Path to test file + + Returns: + Tuple of (test_name, platform) + """ + parts = test_file.stem.split(".") + if len(parts) == 2: + return parts[0], parts[1] # test, platform + return parts[0], "all" + + +def get_component_from_path(file_path: str) -> str | None: + """Extract component name from a file path. + + Args: + file_path: Path to a file (e.g., "esphome/components/wifi/wifi.cpp") + + Returns: + Component name if path is in components directory, None otherwise + """ + if not file_path.startswith(ESPHOME_COMPONENTS_PATH): + return None + parts = file_path.split("/") + if len(parts) >= 3: + return parts[2] + return None + + +def get_component_test_files( + component: str, *, all_variants: bool = False +) -> list[Path]: + """Get test files for a component. + + Args: + component: Component name (e.g., "wifi") + all_variants: If True, returns all test files including variants (test-*.yaml). + If False, returns only base test files (test.*.yaml). + Default is False. + + Returns: + List of test file paths for the component, or empty list if none exist + """ + tests_dir = Path(root_path) / "tests" / "components" / component + if not tests_dir.exists(): + return [] + + if all_variants: + # Match both test.*.yaml and test-*.yaml patterns + return list(tests_dir.glob("test[.-]*.yaml")) + # Match only test.*.yaml (base tests) + return list(tests_dir.glob("test.*.yaml")) + + def styled(color: str | tuple[str, ...], msg: str, reset: bool = True) -> str: prefix = "".join(color) if isinstance(color, tuple) else color suffix = colorama.Style.RESET_ALL if reset else "" @@ -314,11 +385,9 @@ def _filter_changed_ci(files: list[str]) -> list[str]: # because changes in one file can affect other files in the same component. filtered_files = [] for f in files: - if f.startswith(ESPHOME_COMPONENTS_PATH): - # Check if file belongs to any of the changed components - parts = f.split("/") - if len(parts) >= 3 and parts[2] in component_set: - filtered_files.append(f) + component = get_component_from_path(f) + if component and component in component_set: + filtered_files.append(f) return filtered_files diff --git a/script/list-components.py b/script/list-components.py index 9abb2bc345..11533ceb30 100755 --- a/script/list-components.py +++ b/script/list-components.py @@ -4,7 +4,7 @@ from collections.abc import Callable from pathlib import Path import sys -from helpers import changed_files, git_ls_files +from helpers import changed_files, get_component_from_path, git_ls_files from esphome.const import ( KEY_CORE, @@ -30,11 +30,9 @@ def get_all_component_files() -> list[str]: def extract_component_names_array_from_files_array(files): components = [] for file in files: - file_parts = file.split("/") - if len(file_parts) >= 4: - component_name = file_parts[2] - if component_name not in components: - components.append(component_name) + component_name = get_component_from_path(file) + if component_name and component_name not in components: + components.append(component_name) return components diff --git a/script/split_components_for_ci.py b/script/split_components_for_ci.py index dff46d3619..6ba2598eda 100755 --- a/script/split_components_for_ci.py +++ b/script/split_components_for_ci.py @@ -28,6 +28,7 @@ from script.analyze_component_buses import ( create_grouping_signature, merge_compatible_bus_groups, ) +from script.helpers import get_component_test_files # Weighting for batch creation # Isolated components can't be grouped/merged, so they count as 10x @@ -45,17 +46,12 @@ def has_test_files(component_name: str, tests_dir: Path) -> bool: Args: component_name: Name of the component - tests_dir: Path to tests/components directory + tests_dir: Path to tests/components directory (unused, kept for compatibility) Returns: True if the component has test.*.yaml files """ - component_dir = tests_dir / component_name - if not component_dir.exists() or not component_dir.is_dir(): - return False - - # Check for test.*.yaml files - return any(component_dir.glob("test.*.yaml")) + return bool(get_component_test_files(component_name)) def create_intelligent_batches( diff --git a/script/templates/ci_memory_impact_comment_template.j2 b/script/templates/ci_memory_impact_comment_template.j2 new file mode 100644 index 0000000000..9fbf78e99f --- /dev/null +++ b/script/templates/ci_memory_impact_comment_template.j2 @@ -0,0 +1,27 @@ +{{ comment_marker }} +## Memory Impact Analysis + +**Components:** {{ components_str }} +**Platform:** `{{ platform }}` + +| Metric | Target Branch | This PR | Change | +|--------|--------------|---------|--------| +| **RAM** | {{ target_ram }} | {{ pr_ram }} | {{ ram_change }} | +| **Flash** | {{ target_flash }} | {{ pr_flash }} | {{ flash_change }} | +{% if component_breakdown %} +{{ component_breakdown }} +{% endif %} +{% if symbol_changes %} +{{ symbol_changes }} +{% endif %} +{%- if target_cache_hit %} + +> ⚡ Target branch analysis was loaded from cache (build skipped for faster CI). +{%- endif %} + +--- +> **Note:** This analysis measures **static RAM and Flash usage** only (compile-time allocation). +> **Dynamic memory (heap)** cannot be measured automatically. +> **⚠️ You must test this PR on a real device** to measure free heap and ensure no runtime memory issues. + +*This analysis runs automatically when components change. Memory usage is measured from {{ config_note }}.* diff --git a/script/templates/ci_memory_impact_component_breakdown.j2 b/script/templates/ci_memory_impact_component_breakdown.j2 new file mode 100644 index 0000000000..a781e5c546 --- /dev/null +++ b/script/templates/ci_memory_impact_component_breakdown.j2 @@ -0,0 +1,15 @@ + +
+📊 Component Memory Breakdown + +| Component | Target Flash | PR Flash | Change | +|-----------|--------------|----------|--------| +{% for comp, target_flash, pr_flash, delta in changed_components[:max_rows] -%} +{% set threshold = component_change_threshold if comp.startswith("[esphome]") else none -%} +| `{{ comp }}` | {{ target_flash|format_bytes }} | {{ pr_flash|format_bytes }} | {{ format_change(target_flash, pr_flash, threshold=threshold) }} | +{% endfor -%} +{% if changed_components|length > max_rows -%} +| ... | ... | ... | *({{ changed_components|length - max_rows }} more components not shown)* | +{% endif -%} + +
diff --git a/script/templates/ci_memory_impact_macros.j2 b/script/templates/ci_memory_impact_macros.j2 new file mode 100644 index 0000000000..9fb346a7c5 --- /dev/null +++ b/script/templates/ci_memory_impact_macros.j2 @@ -0,0 +1,8 @@ +{#- Macro for formatting symbol names in tables -#} +{%- macro format_symbol(symbol, max_length, truncate_length) -%} +{%- if symbol|length <= max_length -%} +`{{ symbol }}` +{%- else -%} +
{{ symbol[:truncate_length] }}...{{ symbol }}
+{%- endif -%} +{%- endmacro -%} diff --git a/script/templates/ci_memory_impact_symbol_changes.j2 b/script/templates/ci_memory_impact_symbol_changes.j2 new file mode 100644 index 0000000000..60f2f50e48 --- /dev/null +++ b/script/templates/ci_memory_impact_symbol_changes.j2 @@ -0,0 +1,51 @@ +{%- from 'ci_memory_impact_macros.j2' import format_symbol -%} + +
+🔍 Symbol-Level Changes (click to expand) + +{% if changed_symbols %} + +### Changed Symbols + +| Symbol | Target Size | PR Size | Change | +|--------|-------------|---------|--------| +{% for symbol, target_size, pr_size, delta in changed_symbols[:max_changed_rows] -%} +| {{ format_symbol(symbol, symbol_max_length, symbol_truncate_length) }} | {{ target_size|format_bytes }} | {{ pr_size|format_bytes }} | {{ format_change(target_size, pr_size) }} | +{% endfor -%} +{% if changed_symbols|length > max_changed_rows -%} +| ... | ... | ... | *({{ changed_symbols|length - max_changed_rows }} more changed symbols not shown)* | +{% endif -%} + +{% endif %} +{% if new_symbols %} + +### New Symbols (top {{ max_new_rows }}) + +| Symbol | Size | +|--------|------| +{% for symbol, size in new_symbols[:max_new_rows] -%} +| {{ format_symbol(symbol, symbol_max_length, symbol_truncate_length) }} | {{ size|format_bytes }} | +{% endfor -%} +{% if new_symbols|length > max_new_rows -%} +{% set total_new_size = new_symbols|sum(attribute=1) -%} +| *{{ new_symbols|length - max_new_rows }} more new symbols...* | *Total: {{ total_new_size|format_bytes }}* | +{% endif -%} + +{% endif %} +{% if removed_symbols %} + +### Removed Symbols (top {{ max_removed_rows }}) + +| Symbol | Size | +|--------|------| +{% for symbol, size in removed_symbols[:max_removed_rows] -%} +| {{ format_symbol(symbol, symbol_max_length, symbol_truncate_length) }} | {{ size|format_bytes }} | +{% endfor -%} +{% if removed_symbols|length > max_removed_rows -%} +{% set total_removed_size = removed_symbols|sum(attribute=1) -%} +| *{{ removed_symbols|length - max_removed_rows }} more removed symbols...* | *Total: {{ total_removed_size|format_bytes }}* | +{% endif -%} + +{% endif %} + +
diff --git a/script/test_build_components.py b/script/test_build_components.py index df092c091d..77c97a8773 100755 --- a/script/test_build_components.py +++ b/script/test_build_components.py @@ -39,6 +39,7 @@ from script.analyze_component_buses import ( merge_compatible_bus_groups, uses_local_file_references, ) +from script.helpers import get_component_test_files from script.merge_component_configs import merge_component_configs @@ -82,13 +83,14 @@ def show_disk_space_if_ci(esphome_command: str) -> None: def find_component_tests( - components_dir: Path, component_pattern: str = "*" + components_dir: Path, component_pattern: str = "*", base_only: bool = False ) -> dict[str, list[Path]]: """Find all component test files. Args: components_dir: Path to tests/components directory component_pattern: Glob pattern for component names + base_only: If True, only find base test files (test.*.yaml), not variant files (test-*.yaml) Returns: Dictionary mapping component name to list of test files @@ -99,9 +101,10 @@ def find_component_tests( if not comp_dir.is_dir(): continue - # Find test files matching test.*.yaml or test-*.yaml patterns - for test_file in comp_dir.glob("test[.-]*.yaml"): - component_tests[comp_dir.name].append(test_file) + # Get test files using helper function + test_files = get_component_test_files(comp_dir.name, all_variants=not base_only) + if test_files: + component_tests[comp_dir.name] = test_files return dict(component_tests) @@ -931,6 +934,7 @@ def test_components( continue_on_fail: bool, enable_grouping: bool = True, isolated_components: set[str] | None = None, + base_only: bool = False, ) -> int: """Test components with optional intelligent grouping. @@ -944,6 +948,7 @@ def test_components( These are tested WITHOUT --testing-mode to enable full validation (pin conflicts, etc). This is used in CI for directly changed components to catch issues that would be missed with --testing-mode. + base_only: If True, only test base test files (test.*.yaml), not variant files (test-*.yaml) Returns: Exit code (0 for success, 1 for failure) @@ -961,7 +966,7 @@ def test_components( # Find all component tests all_tests = {} for pattern in component_patterns: - all_tests.update(find_component_tests(tests_dir, pattern)) + all_tests.update(find_component_tests(tests_dir, pattern, base_only)) if not all_tests: print(f"No components found matching: {component_patterns}") @@ -1122,6 +1127,11 @@ def main() -> int: "These are tested WITHOUT --testing-mode to enable full validation. " "Used in CI for directly changed components to catch pin conflicts and other issues.", ) + parser.add_argument( + "--base-only", + action="store_true", + help="Only test base test files (test.*.yaml), not variant files (test-*.yaml)", + ) args = parser.parse_args() @@ -1140,6 +1150,7 @@ def main() -> int: continue_on_fail=args.continue_on_fail, enable_grouping=not args.no_grouping, isolated_components=isolated_components, + base_only=args.base_only, ) diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 0559d116be..b479fc03c5 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -17,6 +17,9 @@ script_dir = os.path.abspath( ) sys.path.insert(0, script_dir) +# Import helpers module for patching +import helpers # noqa: E402 + spec = importlib.util.spec_from_file_location( "determine_jobs", os.path.join(script_dir, "determine-jobs.py") ) @@ -59,15 +62,29 @@ def mock_subprocess_run() -> Generator[Mock, None, None]: yield mock +@pytest.fixture +def mock_changed_files() -> Generator[Mock, None, None]: + """Mock changed_files for memory impact detection.""" + with patch.object(determine_jobs, "changed_files") as mock: + # Default to empty list + mock.return_value = [] + yield mock + + def test_main_all_tests_should_run( mock_should_run_integration_tests: Mock, mock_should_run_clang_tidy: Mock, mock_should_run_clang_format: Mock, mock_should_run_python_linters: Mock, mock_subprocess_run: Mock, + mock_changed_files: Mock, capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, ) -> None: """Test when all tests should run.""" + # Ensure we're not in GITHUB_ACTIONS mode for this test + monkeypatch.delenv("GITHUB_ACTIONS", raising=False) + mock_should_run_integration_tests.return_value = True mock_should_run_clang_tidy.return_value = True mock_should_run_clang_format.return_value = True @@ -100,6 +117,9 @@ def test_main_all_tests_should_run( assert output["component_test_count"] == len( output["changed_components_with_tests"] ) + # memory_impact should be present + assert "memory_impact" in output + assert output["memory_impact"]["should_run"] == "false" # No files changed def test_main_no_tests_should_run( @@ -108,9 +128,14 @@ def test_main_no_tests_should_run( mock_should_run_clang_format: Mock, mock_should_run_python_linters: Mock, mock_subprocess_run: Mock, + mock_changed_files: Mock, capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, ) -> None: """Test when no tests should run.""" + # Ensure we're not in GITHUB_ACTIONS mode for this test + monkeypatch.delenv("GITHUB_ACTIONS", raising=False) + mock_should_run_integration_tests.return_value = False mock_should_run_clang_tidy.return_value = False mock_should_run_clang_format.return_value = False @@ -136,6 +161,9 @@ def test_main_no_tests_should_run( assert output["changed_components"] == [] assert output["changed_components_with_tests"] == [] assert output["component_test_count"] == 0 + # memory_impact should be present + assert "memory_impact" in output + assert output["memory_impact"]["should_run"] == "false" def test_main_list_components_fails( @@ -169,9 +197,14 @@ def test_main_with_branch_argument( mock_should_run_clang_format: Mock, mock_should_run_python_linters: Mock, mock_subprocess_run: Mock, + mock_changed_files: Mock, capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, ) -> None: """Test with branch argument.""" + # Ensure we're not in GITHUB_ACTIONS mode for this test + monkeypatch.delenv("GITHUB_ACTIONS", raising=False) + mock_should_run_integration_tests.return_value = False mock_should_run_clang_tidy.return_value = True mock_should_run_clang_format.return_value = False @@ -216,6 +249,9 @@ def test_main_with_branch_argument( assert output["component_test_count"] == len( output["changed_components_with_tests"] ) + # memory_impact should be present + assert "memory_impact" in output + assert output["memory_impact"]["should_run"] == "false" def test_should_run_integration_tests( @@ -403,10 +439,15 @@ def test_main_filters_components_without_tests( mock_should_run_clang_format: Mock, mock_should_run_python_linters: Mock, mock_subprocess_run: Mock, + mock_changed_files: Mock, capsys: pytest.CaptureFixture[str], tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, ) -> None: """Test that components without test files are filtered out.""" + # Ensure we're not in GITHUB_ACTIONS mode for this test + monkeypatch.delenv("GITHUB_ACTIONS", raising=False) + mock_should_run_integration_tests.return_value = False mock_should_run_clang_tidy.return_value = False mock_should_run_clang_format.return_value = False @@ -440,9 +481,10 @@ def test_main_filters_components_without_tests( airthings_dir = tests_dir / "airthings_ble" airthings_dir.mkdir(parents=True) - # Mock root_path to use tmp_path + # Mock root_path to use tmp_path (need to patch both determine_jobs and helpers) with ( patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), patch("sys.argv", ["determine-jobs.py"]), ): # Clear the cache since we're mocking root_path @@ -459,3 +501,188 @@ def test_main_filters_components_without_tests( assert set(output["changed_components_with_tests"]) == {"wifi", "sensor"} # component_test_count should be based on components with tests assert output["component_test_count"] == 2 + # memory_impact should be present + assert "memory_impact" in output + assert output["memory_impact"]["should_run"] == "false" + + +# Tests for detect_memory_impact_config function + + +def test_detect_memory_impact_config_with_common_platform(tmp_path: Path) -> None: + """Test memory impact detection when components share a common platform.""" + # Create test directory structure + tests_dir = tmp_path / "tests" / "components" + + # wifi component with esp32-idf test + wifi_dir = tests_dir / "wifi" + wifi_dir.mkdir(parents=True) + (wifi_dir / "test.esp32-idf.yaml").write_text("test: wifi") + + # api component with esp32-idf test + api_dir = tests_dir / "api" + api_dir.mkdir(parents=True) + (api_dir / "test.esp32-idf.yaml").write_text("test: api") + + # Mock changed_files to return wifi and api component changes + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + ): + mock_changed_files.return_value = [ + "esphome/components/wifi/wifi.cpp", + "esphome/components/api/api.cpp", + ] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + assert result["should_run"] == "true" + assert set(result["components"]) == {"wifi", "api"} + assert result["platform"] == "esp32-idf" # Common platform + assert result["use_merged_config"] == "true" + + +def test_detect_memory_impact_config_core_only_changes(tmp_path: Path) -> None: + """Test memory impact detection with core-only changes (no component changes).""" + # Create test directory structure with fallback component + tests_dir = tmp_path / "tests" / "components" + + # api component (fallback component) with esp32-idf test + api_dir = tests_dir / "api" + api_dir.mkdir(parents=True) + (api_dir / "test.esp32-idf.yaml").write_text("test: api") + + # Mock changed_files to return only core files (no component files) + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + ): + mock_changed_files.return_value = [ + "esphome/core/application.cpp", + "esphome/core/component.h", + ] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + assert result["should_run"] == "true" + assert result["components"] == ["api"] # Fallback component + assert result["platform"] == "esp32-idf" # Fallback platform + assert result["use_merged_config"] == "true" + + +def test_detect_memory_impact_config_no_common_platform(tmp_path: Path) -> None: + """Test memory impact detection when components have no common platform.""" + # Create test directory structure + tests_dir = tmp_path / "tests" / "components" + + # wifi component only has esp32-idf test + wifi_dir = tests_dir / "wifi" + wifi_dir.mkdir(parents=True) + (wifi_dir / "test.esp32-idf.yaml").write_text("test: wifi") + + # logger component only has esp8266-ard test + logger_dir = tests_dir / "logger" + logger_dir.mkdir(parents=True) + (logger_dir / "test.esp8266-ard.yaml").write_text("test: logger") + + # Mock changed_files to return both components + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + ): + mock_changed_files.return_value = [ + "esphome/components/wifi/wifi.cpp", + "esphome/components/logger/logger.cpp", + ] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + # Should pick the most frequently supported platform + assert result["should_run"] == "true" + assert set(result["components"]) == {"wifi", "logger"} + # When no common platform, picks most commonly supported + # esp8266-ard is preferred over esp32-idf in the preference list + assert result["platform"] in ["esp32-idf", "esp8266-ard"] + assert result["use_merged_config"] == "true" + + +def test_detect_memory_impact_config_no_changes(tmp_path: Path) -> None: + """Test memory impact detection when no files changed.""" + # Mock changed_files to return empty list + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + ): + mock_changed_files.return_value = [] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + assert result["should_run"] == "false" + + +def test_detect_memory_impact_config_no_components_with_tests(tmp_path: Path) -> None: + """Test memory impact detection when changed components have no tests.""" + # Create test directory structure + tests_dir = tmp_path / "tests" / "components" + + # Create component directory but no test files + custom_component_dir = tests_dir / "my_custom_component" + custom_component_dir.mkdir(parents=True) + + # Mock changed_files to return component without tests + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + ): + mock_changed_files.return_value = [ + "esphome/components/my_custom_component/component.cpp", + ] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + assert result["should_run"] == "false" + + +def test_detect_memory_impact_config_skips_base_bus_components(tmp_path: Path) -> None: + """Test that base bus components (i2c, spi, uart) are skipped.""" + # Create test directory structure + tests_dir = tmp_path / "tests" / "components" + + # i2c component (should be skipped as it's a base bus component) + i2c_dir = tests_dir / "i2c" + i2c_dir.mkdir(parents=True) + (i2c_dir / "test.esp32-idf.yaml").write_text("test: i2c") + + # wifi component (should not be skipped) + wifi_dir = tests_dir / "wifi" + wifi_dir.mkdir(parents=True) + (wifi_dir / "test.esp32-idf.yaml").write_text("test: wifi") + + # Mock changed_files to return both i2c and wifi + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + ): + mock_changed_files.return_value = [ + "esphome/components/i2c/i2c.cpp", + "esphome/components/wifi/wifi.cpp", + ] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + # Should only include wifi, not i2c + assert result["should_run"] == "true" + assert result["components"] == ["wifi"] + assert "i2c" not in result["components"] diff --git a/tests/unit_tests/test_platformio_api.py b/tests/unit_tests/test_platformio_api.py index 07948cc6ad..13ef3516e4 100644 --- a/tests/unit_tests/test_platformio_api.py +++ b/tests/unit_tests/test_platformio_api.py @@ -387,6 +387,42 @@ def test_idedata_addr2line_path_unix(setup_core: Path) -> None: assert result == "/usr/bin/addr2line" +def test_idedata_objdump_path_windows(setup_core: Path) -> None: + """Test IDEData.objdump_path on Windows.""" + raw_data = {"prog_path": "/path/to/firmware.elf", "cc_path": "C:\\tools\\gcc.exe"} + idedata = platformio_api.IDEData(raw_data) + + result = idedata.objdump_path + assert result == "C:\\tools\\objdump.exe" + + +def test_idedata_objdump_path_unix(setup_core: Path) -> None: + """Test IDEData.objdump_path on Unix.""" + raw_data = {"prog_path": "/path/to/firmware.elf", "cc_path": "/usr/bin/gcc"} + idedata = platformio_api.IDEData(raw_data) + + result = idedata.objdump_path + assert result == "/usr/bin/objdump" + + +def test_idedata_readelf_path_windows(setup_core: Path) -> None: + """Test IDEData.readelf_path on Windows.""" + raw_data = {"prog_path": "/path/to/firmware.elf", "cc_path": "C:\\tools\\gcc.exe"} + idedata = platformio_api.IDEData(raw_data) + + result = idedata.readelf_path + assert result == "C:\\tools\\readelf.exe" + + +def test_idedata_readelf_path_unix(setup_core: Path) -> None: + """Test IDEData.readelf_path on Unix.""" + raw_data = {"prog_path": "/path/to/firmware.elf", "cc_path": "/usr/bin/gcc"} + idedata = platformio_api.IDEData(raw_data) + + result = idedata.readelf_path + assert result == "/usr/bin/readelf" + + def test_patch_structhash(setup_core: Path) -> None: """Test patch_structhash monkey patches platformio functions.""" # Create simple namespace objects to act as modules From fdecda3d65a5474be4d9ccaf0ea3ee56f84548d2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 08:48:14 -1000 Subject: [PATCH 141/526] [light] Use bitmask instead of std::set for color modes (#11348) --- esphome/components/api/api.proto | 2 +- esphome/components/api/api_connection.cpp | 4 +- esphome/components/api/api_options.proto | 10 + esphome/components/api/api_pb2.h | 2 +- esphome/components/light/color_mode.h | 195 ++++++++++++++++++ esphome/components/light/light_call.cpp | 53 ++--- esphome/components/light/light_call.h | 5 +- .../components/light/light_json_schema.cpp | 1 - esphome/components/light/light_state.cpp | 3 - esphome/components/light/light_traits.h | 30 +-- script/api_protobuf/api_protobuf.py | 18 +- tests/integration/test_light_calls.py | 44 +++- 12 files changed, 308 insertions(+), 59 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 753adc3592..d202486cfa 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -506,7 +506,7 @@ message ListEntitiesLightResponse { string name = 3; reserved 4; // Deprecated: was string unique_id - repeated ColorMode supported_color_modes = 12 [(container_pointer) = "std::set"]; + repeated ColorMode supported_color_modes = 12 [(container_pointer_no_template) = "light::ColorModeMask"]; // next four supports_* are for legacy clients, newer clients should use color modes // Deprecated in API version 1.6 bool legacy_supports_brightness = 5 [deprecated=true]; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 7dfefedd54..6334815678 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -453,7 +453,6 @@ uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection * bool is_single) { auto *light = static_cast(entity); LightStateResponse resp; - auto traits = light->get_traits(); auto values = light->remote_values; auto color_mode = values.get_color_mode(); resp.state = values.is_on(); @@ -477,7 +476,8 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c auto *light = static_cast(entity); ListEntitiesLightResponse msg; auto traits = light->get_traits(); - msg.supported_color_modes = &traits.get_supported_color_modes_for_api_(); + // Pass pointer to ColorModeMask so the iterator can encode actual ColorMode enum values + msg.supported_color_modes = &traits.get_supported_color_modes(); if (traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) || traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE)) { msg.min_mireds = traits.get_min_mireds(); diff --git a/esphome/components/api/api_options.proto b/esphome/components/api/api_options.proto index ead8ac0bbc..6b33408e2f 100644 --- a/esphome/components/api/api_options.proto +++ b/esphome/components/api/api_options.proto @@ -70,4 +70,14 @@ extend google.protobuf.FieldOptions { // init(size) before adding elements. This eliminates std::vector template overhead // and is ideal when the exact size is known before populating the array. optional bool fixed_vector = 50013 [default=false]; + + // container_pointer_no_template: Use a non-template container type for repeated fields + // Similar to container_pointer, but for containers that don't take template parameters. + // The container type is used as-is without appending element type. + // The container must have: + // - begin() and end() methods returning iterators + // - empty() method + // Example: [(container_pointer_no_template) = "light::ColorModeMask"] + // generates: const light::ColorModeMask *supported_color_modes{}; + optional string container_pointer_no_template = 50014; } diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 5603204801..ed49498176 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -790,7 +790,7 @@ class ListEntitiesLightResponse final : public InfoResponseProtoMessage { #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_light_response"; } #endif - const std::set *supported_color_modes{}; + const light::ColorModeMask *supported_color_modes{}; float min_mireds{0.0f}; float max_mireds{0.0f}; std::vector effects{}; diff --git a/esphome/components/light/color_mode.h b/esphome/components/light/color_mode.h index e524763c9f..a26f917167 100644 --- a/esphome/components/light/color_mode.h +++ b/esphome/components/light/color_mode.h @@ -104,5 +104,200 @@ constexpr ColorModeHelper operator|(ColorModeHelper lhs, ColorMode rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } +// Type alias for raw color mode bitmask values +using color_mode_bitmask_t = uint16_t; + +// Constants for ColorMode count and bit range +static constexpr int COLOR_MODE_COUNT = 10; // UNKNOWN through RGB_COLD_WARM_WHITE +static constexpr int MAX_BIT_INDEX = sizeof(color_mode_bitmask_t) * 8; // Number of bits in bitmask type + +// Compile-time array of all ColorMode values in declaration order +// Bit positions (0-9) map directly to enum declaration order +static constexpr ColorMode COLOR_MODES[COLOR_MODE_COUNT] = { + ColorMode::UNKNOWN, // bit 0 + ColorMode::ON_OFF, // bit 1 + ColorMode::BRIGHTNESS, // bit 2 + ColorMode::WHITE, // bit 3 + ColorMode::COLOR_TEMPERATURE, // bit 4 + ColorMode::COLD_WARM_WHITE, // bit 5 + ColorMode::RGB, // bit 6 + ColorMode::RGB_WHITE, // bit 7 + ColorMode::RGB_COLOR_TEMPERATURE, // bit 8 + ColorMode::RGB_COLD_WARM_WHITE, // bit 9 +}; + +/// Map ColorMode enum values to bit positions (0-9) +/// Bit positions follow the enum declaration order +static constexpr int mode_to_bit(ColorMode mode) { + // Linear search through COLOR_MODES array + // Compiler optimizes this to efficient code since array is constexpr + for (int i = 0; i < COLOR_MODE_COUNT; ++i) { + if (COLOR_MODES[i] == mode) + return i; + } + return 0; +} + +/// Map bit positions (0-9) to ColorMode enum values +/// Bit positions follow the enum declaration order +static constexpr ColorMode bit_to_mode(int bit) { + // Direct lookup in COLOR_MODES array + return (bit >= 0 && bit < COLOR_MODE_COUNT) ? COLOR_MODES[bit] : ColorMode::UNKNOWN; +} + +/// Helper to compute capability bitmask at compile time +static constexpr color_mode_bitmask_t compute_capability_bitmask(ColorCapability capability) { + color_mode_bitmask_t mask = 0; + uint8_t cap_bit = static_cast(capability); + + // Check each ColorMode to see if it has this capability + for (int bit = 0; bit < COLOR_MODE_COUNT; ++bit) { + uint8_t mode_val = static_cast(bit_to_mode(bit)); + if ((mode_val & cap_bit) != 0) { + mask |= (1 << bit); + } + } + return mask; +} + +// Number of ColorCapability enum values +static constexpr int COLOR_CAPABILITY_COUNT = 6; + +/// Compile-time lookup table mapping ColorCapability to bitmask +/// This array is computed at compile time using constexpr +static constexpr color_mode_bitmask_t CAPABILITY_BITMASKS[] = { + compute_capability_bitmask(ColorCapability::ON_OFF), // 1 << 0 + compute_capability_bitmask(ColorCapability::BRIGHTNESS), // 1 << 1 + compute_capability_bitmask(ColorCapability::WHITE), // 1 << 2 + compute_capability_bitmask(ColorCapability::COLOR_TEMPERATURE), // 1 << 3 + compute_capability_bitmask(ColorCapability::COLD_WARM_WHITE), // 1 << 4 + compute_capability_bitmask(ColorCapability::RGB), // 1 << 5 +}; + +/// Bitmask for storing a set of ColorMode values efficiently. +/// Replaces std::set to eliminate red-black tree overhead (~586 bytes). +class ColorModeMask { + public: + constexpr ColorModeMask() = default; + + /// Support initializer list syntax: {ColorMode::RGB, ColorMode::WHITE} + constexpr ColorModeMask(std::initializer_list modes) { + for (auto mode : modes) { + this->add(mode); + } + } + + constexpr void add(ColorMode mode) { this->mask_ |= (1 << mode_to_bit(mode)); } + + /// Add multiple modes at once using initializer list + constexpr void add(std::initializer_list modes) { + for (auto mode : modes) { + this->add(mode); + } + } + + constexpr bool contains(ColorMode mode) const { return (this->mask_ & (1 << mode_to_bit(mode))) != 0; } + + constexpr size_t size() const { + // Count set bits using Brian Kernighan's algorithm + // More efficient for sparse bitmasks (typical case: 2-4 modes out of 10) + uint16_t n = this->mask_; + size_t count = 0; + while (n) { + n &= n - 1; // Clear the least significant set bit + count++; + } + return count; + } + + constexpr bool empty() const { return this->mask_ == 0; } + + /// Iterator support for API encoding + class Iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = ColorMode; + using difference_type = std::ptrdiff_t; + using pointer = const ColorMode *; + using reference = ColorMode; + + constexpr Iterator(color_mode_bitmask_t mask, int bit) : mask_(mask), bit_(bit) { advance_to_next_set_bit_(); } + + constexpr ColorMode operator*() const { return bit_to_mode(bit_); } + + constexpr Iterator &operator++() { + ++bit_; + advance_to_next_set_bit_(); + return *this; + } + + constexpr bool operator==(const Iterator &other) const { return bit_ == other.bit_; } + + constexpr bool operator!=(const Iterator &other) const { return !(*this == other); } + + private: + constexpr void advance_to_next_set_bit_() { bit_ = ColorModeMask::find_next_set_bit(mask_, bit_); } + + color_mode_bitmask_t mask_; + int bit_; + }; + + constexpr Iterator begin() const { return Iterator(mask_, 0); } + constexpr Iterator end() const { return Iterator(mask_, MAX_BIT_INDEX); } + + /// Get the raw bitmask value for API encoding + constexpr color_mode_bitmask_t get_mask() const { return this->mask_; } + + /// Find the next set bit in a bitmask starting from a given position + /// Returns the bit position, or MAX_BIT_INDEX if no more bits are set + static constexpr int find_next_set_bit(color_mode_bitmask_t mask, int start_bit) { + int bit = start_bit; + while (bit < MAX_BIT_INDEX && !(mask & (1 << bit))) { + ++bit; + } + return bit; + } + + /// Find the first set bit in a bitmask and return the corresponding ColorMode + /// Used for optimizing compute_color_mode_() intersection logic + static constexpr ColorMode first_mode_from_mask(color_mode_bitmask_t mask) { + return bit_to_mode(find_next_set_bit(mask, 0)); + } + + /// Check if a ColorMode is present in a raw bitmask value + /// Useful for checking intersection results without creating a temporary ColorModeMask + static constexpr bool mask_contains(color_mode_bitmask_t mask, ColorMode mode) { + return (mask & (1 << mode_to_bit(mode))) != 0; + } + + /// Check if any mode in the bitmask has a specific capability + /// Used for checking if a light supports a capability (e.g., BRIGHTNESS, RGB) + bool has_capability(ColorCapability capability) const { + // Lookup the pre-computed bitmask for this capability and check intersection with our mask + // ColorCapability values: 1, 2, 4, 8, 16, 32 -> array indices: 0, 1, 2, 3, 4, 5 + // We need to convert the power-of-2 value to an index + uint8_t cap_val = static_cast(capability); +#if defined(__GNUC__) || defined(__clang__) + // Use compiler intrinsic for efficient bit position lookup (O(1) vs O(log n)) + int index = __builtin_ctz(cap_val); +#else + // Fallback for compilers without __builtin_ctz + int index = 0; + while (cap_val > 1) { + cap_val >>= 1; + ++index; + } +#endif + return (this->mask_ & CAPABILITY_BITMASKS[index]) != 0; + } + + private: + // Using uint16_t instead of uint32_t for more efficient iteration (fewer bits to scan). + // Currently only 10 ColorMode values exist, so 16 bits is sufficient. + // Can be changed to uint32_t if more than 16 color modes are needed in the future. + // Note: Due to struct padding, uint16_t and uint32_t result in same LightTraits size (12 bytes). + color_mode_bitmask_t mask_{0}; +}; + } // namespace light } // namespace esphome diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index 915b8fdf89..af193e1f11 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -406,7 +406,7 @@ void LightCall::transform_parameters_() { } } ColorMode LightCall::compute_color_mode_() { - auto supported_modes = this->parent_->get_traits().get_supported_color_modes(); + const auto &supported_modes = this->parent_->get_traits().get_supported_color_modes(); int supported_count = supported_modes.size(); // Some lights don't support any color modes (e.g. monochromatic light), leave it at unknown. @@ -425,20 +425,19 @@ ColorMode LightCall::compute_color_mode_() { // If no color mode is specified, we try to guess the color mode. This is needed for backward compatibility to // pre-colormode clients and automations, but also for the MQTT API, where HA doesn't let us know which color mode // was used for some reason. - std::set suitable_modes = this->get_suitable_color_modes_(); + // Compute intersection of suitable and supported modes using bitwise AND + color_mode_bitmask_t intersection = this->get_suitable_color_modes_mask_() & supported_modes.get_mask(); - // Don't change if the current mode is suitable. - if (suitable_modes.count(current_mode) > 0) { + // Don't change if the current mode is in the intersection (suitable AND supported) + if (ColorModeMask::mask_contains(intersection, current_mode)) { ESP_LOGI(TAG, "'%s': color mode not specified; retaining %s", this->parent_->get_name().c_str(), LOG_STR_ARG(color_mode_to_human(current_mode))); return current_mode; } // Use the preferred suitable mode. - for (auto mode : suitable_modes) { - if (supported_modes.count(mode) == 0) - continue; - + if (intersection != 0) { + ColorMode mode = ColorModeMask::first_mode_from_mask(intersection); ESP_LOGI(TAG, "'%s': color mode not specified; using %s", this->parent_->get_name().c_str(), LOG_STR_ARG(color_mode_to_human(mode))); return mode; @@ -451,7 +450,7 @@ ColorMode LightCall::compute_color_mode_() { LOG_STR_ARG(color_mode_to_human(color_mode))); return color_mode; } -std::set LightCall::get_suitable_color_modes_() { +color_mode_bitmask_t LightCall::get_suitable_color_modes_mask_() { bool has_white = this->has_white() && this->white_ > 0.0f; bool has_ct = this->has_color_temperature(); bool has_cwww = @@ -459,36 +458,44 @@ std::set LightCall::get_suitable_color_modes_() { bool has_rgb = (this->has_color_brightness() && this->color_brightness_ > 0.0f) || (this->has_red() || this->has_green() || this->has_blue()); -// Build key from flags: [rgb][cwww][ct][white] + // Build key from flags: [rgb][cwww][ct][white] #define KEY(white, ct, cwww, rgb) ((white) << 0 | (ct) << 1 | (cwww) << 2 | (rgb) << 3) uint8_t key = KEY(has_white, has_ct, has_cwww, has_rgb); switch (key) { case KEY(true, false, false, false): // white only - return {ColorMode::WHITE, ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE, - ColorMode::RGB_COLD_WARM_WHITE}; + return ColorModeMask({ColorMode::WHITE, ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, + ColorMode::COLD_WARM_WHITE, ColorMode::RGB_COLD_WARM_WHITE}) + .get_mask(); case KEY(false, true, false, false): // ct only - return {ColorMode::COLOR_TEMPERATURE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE, - ColorMode::RGB_COLD_WARM_WHITE}; + return ColorModeMask({ColorMode::COLOR_TEMPERATURE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE, + ColorMode::RGB_COLD_WARM_WHITE}) + .get_mask(); case KEY(true, true, false, false): // white + ct - return {ColorMode::COLD_WARM_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}; + return ColorModeMask( + {ColorMode::COLD_WARM_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}) + .get_mask(); case KEY(false, false, true, false): // cwww only - return {ColorMode::COLD_WARM_WHITE, ColorMode::RGB_COLD_WARM_WHITE}; + return ColorModeMask({ColorMode::COLD_WARM_WHITE, ColorMode::RGB_COLD_WARM_WHITE}).get_mask(); case KEY(false, false, false, false): // none - return {ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE, ColorMode::RGB, - ColorMode::WHITE, ColorMode::COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE}; + return ColorModeMask({ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE, + ColorMode::RGB, ColorMode::WHITE, ColorMode::COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE}) + .get_mask(); case KEY(true, false, false, true): // rgb + white - return {ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}; + return ColorModeMask({ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}) + .get_mask(); case KEY(false, true, false, true): // rgb + ct case KEY(true, true, false, true): // rgb + white + ct - return {ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}; + return ColorModeMask({ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}).get_mask(); case KEY(false, false, true, true): // rgb + cwww - return {ColorMode::RGB_COLD_WARM_WHITE}; + return ColorModeMask({ColorMode::RGB_COLD_WARM_WHITE}).get_mask(); case KEY(false, false, false, true): // rgb only - return {ColorMode::RGB, ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}; + return ColorModeMask({ColorMode::RGB, ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, + ColorMode::RGB_COLD_WARM_WHITE}) + .get_mask(); default: - return {}; // conflicting flags + return 0; // conflicting flags } #undef KEY diff --git a/esphome/components/light/light_call.h b/esphome/components/light/light_call.h index d3a526b136..6931b58b9d 100644 --- a/esphome/components/light/light_call.h +++ b/esphome/components/light/light_call.h @@ -1,7 +1,6 @@ #pragma once #include "light_color_values.h" -#include namespace esphome { @@ -186,8 +185,8 @@ class LightCall { //// Compute the color mode that should be used for this call. ColorMode compute_color_mode_(); - /// Get potential color modes for this light call. - std::set get_suitable_color_modes_(); + /// Get potential color modes bitmask for this light call. + color_mode_bitmask_t get_suitable_color_modes_mask_(); /// Some color modes also can be set using non-native parameters, transform those calls. void transform_parameters_(); diff --git a/esphome/components/light/light_json_schema.cpp b/esphome/components/light/light_json_schema.cpp index 010e130612..e754c453b5 100644 --- a/esphome/components/light/light_json_schema.cpp +++ b/esphome/components/light/light_json_schema.cpp @@ -43,7 +43,6 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) { } auto values = state.remote_values; - auto traits = state.get_output()->get_traits(); const auto color_mode = values.get_color_mode(); const char *mode_str = get_color_mode_json_str(color_mode); diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 1d139e49e7..979dc2f5a1 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -191,11 +191,9 @@ void LightState::current_values_as_brightness(float *brightness) { this->current_values.as_brightness(brightness, this->gamma_correct_); } void LightState::current_values_as_rgb(float *red, float *green, float *blue, bool color_interlock) { - auto traits = this->get_traits(); this->current_values.as_rgb(red, green, blue, this->gamma_correct_, false); } void LightState::current_values_as_rgbw(float *red, float *green, float *blue, float *white, bool color_interlock) { - auto traits = this->get_traits(); this->current_values.as_rgbw(red, green, blue, white, this->gamma_correct_, false); } void LightState::current_values_as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white, @@ -209,7 +207,6 @@ void LightState::current_values_as_rgbct(float *red, float *green, float *blue, white_brightness, this->gamma_correct_); } void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bool constant_brightness) { - auto traits = this->get_traits(); this->current_values.as_cwww(cold_white, warm_white, this->gamma_correct_, constant_brightness); } void LightState::current_values_as_ct(float *color_temperature, float *white_brightness) { diff --git a/esphome/components/light/light_traits.h b/esphome/components/light/light_traits.h index a45301d148..c83d8ad2a9 100644 --- a/esphome/components/light/light_traits.h +++ b/esphome/components/light/light_traits.h @@ -2,7 +2,6 @@ #include "esphome/core/helpers.h" #include "color_mode.h" -#include namespace esphome { @@ -19,18 +18,17 @@ class LightTraits { public: LightTraits() = default; - const std::set &get_supported_color_modes() const { return this->supported_color_modes_; } - void set_supported_color_modes(std::set supported_color_modes) { - this->supported_color_modes_ = std::move(supported_color_modes); + const ColorModeMask &get_supported_color_modes() const { return this->supported_color_modes_; } + void set_supported_color_modes(ColorModeMask supported_color_modes) { + this->supported_color_modes_ = supported_color_modes; + } + void set_supported_color_modes(std::initializer_list modes) { + this->supported_color_modes_ = ColorModeMask(modes); } - bool supports_color_mode(ColorMode color_mode) const { return this->supported_color_modes_.count(color_mode); } + bool supports_color_mode(ColorMode color_mode) const { return this->supported_color_modes_.contains(color_mode); } bool supports_color_capability(ColorCapability color_capability) const { - for (auto mode : this->supported_color_modes_) { - if (mode & color_capability) - return true; - } - return false; + return this->supported_color_modes_.has_capability(color_capability); } ESPDEPRECATED("get_supports_brightness() is deprecated, use color modes instead.", "v1.21") @@ -59,19 +57,9 @@ class LightTraits { void set_max_mireds(float max_mireds) { this->max_mireds_ = max_mireds; } protected: -#ifdef USE_API - // The API connection is a friend class to access internal methods - friend class api::APIConnection; - // This method returns a reference to the internal color modes set. - // It is used by the API to avoid copying data when encoding messages. - // Warning: Do not use this method outside of the API connection code. - // It returns a reference to internal data that can be invalidated. - const std::set &get_supported_color_modes_for_api_() const { return this->supported_color_modes_; } -#endif - - std::set supported_color_modes_{}; float min_mireds_{0}; float max_mireds_{0}; + ColorModeMask supported_color_modes_{}; }; } // namespace light diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 4936434fc2..2f83b0bd79 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -1415,7 +1415,13 @@ class RepeatedTypeInfo(TypeInfo): super().__init__(field) # Check if this is a pointer field by looking for container_pointer option self._container_type = get_field_opt(field, pb.container_pointer, "") - self._use_pointer = bool(self._container_type) + # Check for non-template container pointer + self._container_no_template = get_field_opt( + field, pb.container_pointer_no_template, "" + ) + self._use_pointer = bool(self._container_type) or bool( + self._container_no_template + ) # Check if this should use FixedVector instead of std::vector self._use_fixed_vector = get_field_opt(field, pb.fixed_vector, False) @@ -1434,12 +1440,18 @@ class RepeatedTypeInfo(TypeInfo): @property def cpp_type(self) -> str: + if self._container_no_template: + # Non-template container: use type as-is without appending template parameters + return f"const {self._container_no_template}*" if self._use_pointer and self._container_type: # For pointer fields, use the specified container type - # If the container type already includes the element type (e.g., std::set) - # use it as-is, otherwise append the element type + # Two cases: + # 1. "std::set" - Full type with template params, use as-is + # 2. "std::set" - No <>, append the element type if "<" in self._container_type and ">" in self._container_type: + # Has template parameters specified, use as-is return f"const {self._container_type}*" + # No <> at all, append element type return f"const {self._container_type}<{self._ti.cpp_type}>*" if self._use_fixed_vector: return f"FixedVector<{self._ti.cpp_type}>" diff --git a/tests/integration/test_light_calls.py b/tests/integration/test_light_calls.py index af90ddbe86..0eaf5af91b 100644 --- a/tests/integration/test_light_calls.py +++ b/tests/integration/test_light_calls.py @@ -8,6 +8,7 @@ import asyncio from typing import Any from aioesphomeapi import LightState +from aioesphomeapi.model import ColorMode import pytest from .types import APIClientConnectedFactory, RunCompiledFunction @@ -35,10 +36,51 @@ async def test_light_calls( # Get the light entities entities = await client.list_entities_services() lights = [e for e in entities[0] if e.object_id.startswith("test_")] - assert len(lights) >= 2 # Should have RGBCW and RGB lights + assert len(lights) >= 3 # Should have RGBCW, RGB, and Binary lights rgbcw_light = next(light for light in lights if "RGBCW" in light.name) rgb_light = next(light for light in lights if "RGB Light" in light.name) + binary_light = next(light for light in lights if "Binary" in light.name) + + # Test color mode encoding: Verify supported_color_modes contains actual ColorMode enum values + # not bit positions. This is critical - the iterator must convert bit positions to actual + # ColorMode enum values for API encoding. + + # RGBCW light (rgbww platform) should support RGB_COLD_WARM_WHITE mode + assert ColorMode.RGB_COLD_WARM_WHITE in rgbcw_light.supported_color_modes, ( + f"RGBCW light missing RGB_COLD_WARM_WHITE mode. Got: {rgbcw_light.supported_color_modes}" + ) + # Verify it's the actual enum value, not bit position + assert ColorMode.RGB_COLD_WARM_WHITE.value in [ + mode.value for mode in rgbcw_light.supported_color_modes + ], ( + f"RGBCW light has wrong color mode values. Expected {ColorMode.RGB_COLD_WARM_WHITE.value} " + f"(RGB_COLD_WARM_WHITE), got: {[mode.value for mode in rgbcw_light.supported_color_modes]}" + ) + + # RGB light should support RGB mode + assert ColorMode.RGB in rgb_light.supported_color_modes, ( + f"RGB light missing RGB color mode. Got: {rgb_light.supported_color_modes}" + ) + # Verify it's the actual enum value, not bit position + assert ColorMode.RGB.value in [ + mode.value for mode in rgb_light.supported_color_modes + ], ( + f"RGB light has wrong color mode values. Expected {ColorMode.RGB.value} (RGB), got: " + f"{[mode.value for mode in rgb_light.supported_color_modes]}" + ) + + # Binary light (on/off only) should support ON_OFF mode + assert ColorMode.ON_OFF in binary_light.supported_color_modes, ( + f"Binary light missing ON_OFF color mode. Got: {binary_light.supported_color_modes}" + ) + # Verify it's the actual enum value, not bit position + assert ColorMode.ON_OFF.value in [ + mode.value for mode in binary_light.supported_color_modes + ], ( + f"Binary light has wrong color mode values. Expected {ColorMode.ON_OFF.value} (ON_OFF), got: " + f"{[mode.value for mode in binary_light.supported_color_modes]}" + ) async def wait_for_state_change(key: int, timeout: float = 1.0) -> Any: """Wait for a state change for the given entity key.""" From 09b2ad071bdadad82d20080d2352c577b131eebd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 08:49:13 -1000 Subject: [PATCH 142/526] [esp32_ble_client] Remove duplicate MAC address extraction in set_address() (#11358) --- esphome/components/esp32_ble_client/ble_client_base.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index f2edd6c2b3..7f0ae3b83e 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -61,12 +61,7 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { this->address_str_ = ""; } else { char buf[18]; - uint8_t mac[6] = { - (uint8_t) ((this->address_ >> 40) & 0xff), (uint8_t) ((this->address_ >> 32) & 0xff), - (uint8_t) ((this->address_ >> 24) & 0xff), (uint8_t) ((this->address_ >> 16) & 0xff), - (uint8_t) ((this->address_ >> 8) & 0xff), (uint8_t) ((this->address_ >> 0) & 0xff), - }; - format_mac_addr_upper(mac, buf); + format_mac_addr_upper(this->remote_bda_, buf); this->address_str_ = buf; } } From 57e98ec3fc95104e37984d1a6f25c10a80298c23 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 08:49:58 -1000 Subject: [PATCH 143/526] [wifi] Replace std::vector with std::unique_ptr for WiFi scan buffer (#11364) --- esphome/components/wifi/wifi_component_esp_idf.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 951f5803a6..ce1cc961d0 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -776,13 +776,12 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { } uint16_t number = it.number; - std::vector records(number); - err = esp_wifi_scan_get_ap_records(&number, records.data()); + auto records = std::make_unique(number); + err = esp_wifi_scan_get_ap_records(&number, records.get()); if (err != ESP_OK) { ESP_LOGW(TAG, "esp_wifi_scan_get_ap_records failed: %s", esp_err_to_name(err)); return; } - records.resize(number); scan_result_.init(number); for (int i = 0; i < number; i++) { From bda7676e3a52ef2426177f8bb4422cc47c5d6354 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 08:51:41 -1000 Subject: [PATCH 144/526] [bluetooth_proxy] Merge duplicate loops in get_connection_() (#11359) --- .../components/bluetooth_proxy/bluetooth_proxy.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index cd7261d5e5..34e0aa93a3 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -155,16 +155,12 @@ esp32_ble_tracker::AdvertisementParserType BluetoothProxy::get_advertisement_par BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) { for (uint8_t i = 0; i < this->connection_count_; i++) { auto *connection = this->connections_[i]; - if (connection->get_address() == address) + uint64_t conn_addr = connection->get_address(); + + if (conn_addr == address) return connection; - } - if (!reserve) - return nullptr; - - for (uint8_t i = 0; i < this->connection_count_; i++) { - auto *connection = this->connections_[i]; - if (connection->get_address() == 0) { + if (reserve && conn_addr == 0) { connection->send_service_ = INIT_SENDING_SERVICES; connection->set_address(address); // All connections must start at INIT @@ -175,7 +171,6 @@ BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool rese return connection; } } - return nullptr; } From 0266c897c9d33f62be4888ec5def9b71a96f5bd8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 08:53:00 -1000 Subject: [PATCH 145/526] [mdns] Use std::unique_ptr for TXT records to reduce ESP32 flash usage (#11362) --- esphome/components/mdns/mdns_esp32.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/esphome/components/mdns/mdns_esp32.cpp b/esphome/components/mdns/mdns_esp32.cpp index f2cb2d3ef5..c02bfcbadb 100644 --- a/esphome/components/mdns/mdns_esp32.cpp +++ b/esphome/components/mdns/mdns_esp32.cpp @@ -31,18 +31,17 @@ void MDNSComponent::setup() { mdns_instance_name_set(this->hostname_.c_str()); for (const auto &service : services) { - std::vector txt_records; - for (const auto &record : service.txt_records) { - mdns_txt_item_t it{}; + auto txt_records = std::make_unique(service.txt_records.size()); + for (size_t i = 0; i < service.txt_records.size(); i++) { + const auto &record = service.txt_records[i]; // key and value are either compile-time string literals in flash or pointers to dynamic_txt_values_ // Both remain valid for the lifetime of this function, and ESP-IDF makes internal copies - it.key = MDNS_STR_ARG(record.key); - it.value = MDNS_STR_ARG(record.value); - txt_records.push_back(it); + txt_records[i].key = MDNS_STR_ARG(record.key); + txt_records[i].value = MDNS_STR_ARG(record.value); } uint16_t port = const_cast &>(service.port).value(); err = mdns_service_add(nullptr, MDNS_STR_ARG(service.service_type), MDNS_STR_ARG(service.proto), port, - txt_records.data(), txt_records.size()); + txt_records.get(), service.txt_records.size()); if (err != ESP_OK) { ESP_LOGW(TAG, "Failed to register service %s: %s", MDNS_STR_ARG(service.service_type), esp_err_to_name(err)); From 85babe85e4e97dfe3abd5bb4656ea86102118698 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 08:59:47 -1000 Subject: [PATCH 146/526] [sensor] Optimize sliding window filters to eliminate heap fragmentation (#11282) --- esphome/components/sensor/__init__.py | 60 ++- esphome/components/sensor/filter.cpp | 312 +++++++------- esphome/components/sensor/filter.h | 217 +++++++--- tests/components/sensor/common.yaml | 101 +++++ tests/components/sensor/test.esp8266-ard.yaml | 1 + tests/integration/README.md | 75 ++++ .../fixtures/sensor_filters_batch_window.yaml | 58 +++ .../fixtures/sensor_filters_nan_handling.yaml | 84 ++++ .../fixtures/sensor_filters_ring_buffer.yaml | 115 +++++ ...sensor_filters_ring_buffer_wraparound.yaml | 72 ++++ .../sensor_filters_sliding_window.yaml | 123 ++++++ tests/integration/state_utils.py | 167 ++++++++ .../test_sensor_filters_ring_buffer.py | 151 +++++++ .../test_sensor_filters_sliding_window.py | 395 ++++++++++++++++++ 14 files changed, 1697 insertions(+), 234 deletions(-) create mode 100644 tests/components/sensor/common.yaml create mode 100644 tests/components/sensor/test.esp8266-ard.yaml create mode 100644 tests/integration/fixtures/sensor_filters_batch_window.yaml create mode 100644 tests/integration/fixtures/sensor_filters_nan_handling.yaml create mode 100644 tests/integration/fixtures/sensor_filters_ring_buffer.yaml create mode 100644 tests/integration/fixtures/sensor_filters_ring_buffer_wraparound.yaml create mode 100644 tests/integration/fixtures/sensor_filters_sliding_window.yaml create mode 100644 tests/integration/state_utils.py create mode 100644 tests/integration/test_sensor_filters_ring_buffer.py create mode 100644 tests/integration/test_sensor_filters_sliding_window.py diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index bf13217787..d9724a741d 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -251,6 +251,9 @@ MaxFilter = sensor_ns.class_("MaxFilter", Filter) SlidingWindowMovingAverageFilter = sensor_ns.class_( "SlidingWindowMovingAverageFilter", Filter ) +StreamingMinFilter = sensor_ns.class_("StreamingMinFilter", Filter) +StreamingMaxFilter = sensor_ns.class_("StreamingMaxFilter", Filter) +StreamingMovingAverageFilter = sensor_ns.class_("StreamingMovingAverageFilter", Filter) ExponentialMovingAverageFilter = sensor_ns.class_( "ExponentialMovingAverageFilter", Filter ) @@ -452,14 +455,21 @@ async def skip_initial_filter_to_code(config, filter_id): return cg.new_Pvariable(filter_id, config) -@FILTER_REGISTRY.register("min", MinFilter, MIN_SCHEMA) +@FILTER_REGISTRY.register("min", Filter, MIN_SCHEMA) async def min_filter_to_code(config, filter_id): - return cg.new_Pvariable( - filter_id, - config[CONF_WINDOW_SIZE], - config[CONF_SEND_EVERY], - config[CONF_SEND_FIRST_AT], - ) + window_size: int = config[CONF_WINDOW_SIZE] + send_every: int = config[CONF_SEND_EVERY] + send_first_at: int = config[CONF_SEND_FIRST_AT] + + # Optimization: Use streaming filter for batch windows (window_size == send_every) + # Saves 99.98% memory for large windows (e.g., 20KB → 4 bytes for window_size=5000) + if window_size == send_every: + # Use streaming filter - O(1) memory instead of O(n) + rhs = StreamingMinFilter.new(window_size, send_first_at) + return cg.Pvariable(filter_id, rhs, StreamingMinFilter) + # Use sliding window filter - maintains ring buffer + rhs = MinFilter.new(window_size, send_every, send_first_at) + return cg.Pvariable(filter_id, rhs, MinFilter) MAX_SCHEMA = cv.All( @@ -474,14 +484,18 @@ MAX_SCHEMA = cv.All( ) -@FILTER_REGISTRY.register("max", MaxFilter, MAX_SCHEMA) +@FILTER_REGISTRY.register("max", Filter, MAX_SCHEMA) async def max_filter_to_code(config, filter_id): - return cg.new_Pvariable( - filter_id, - config[CONF_WINDOW_SIZE], - config[CONF_SEND_EVERY], - config[CONF_SEND_FIRST_AT], - ) + window_size: int = config[CONF_WINDOW_SIZE] + send_every: int = config[CONF_SEND_EVERY] + send_first_at: int = config[CONF_SEND_FIRST_AT] + + # Optimization: Use streaming filter for batch windows (window_size == send_every) + if window_size == send_every: + rhs = StreamingMaxFilter.new(window_size, send_first_at) + return cg.Pvariable(filter_id, rhs, StreamingMaxFilter) + rhs = MaxFilter.new(window_size, send_every, send_first_at) + return cg.Pvariable(filter_id, rhs, MaxFilter) SLIDING_AVERAGE_SCHEMA = cv.All( @@ -498,16 +512,20 @@ SLIDING_AVERAGE_SCHEMA = cv.All( @FILTER_REGISTRY.register( "sliding_window_moving_average", - SlidingWindowMovingAverageFilter, + Filter, SLIDING_AVERAGE_SCHEMA, ) async def sliding_window_moving_average_filter_to_code(config, filter_id): - return cg.new_Pvariable( - filter_id, - config[CONF_WINDOW_SIZE], - config[CONF_SEND_EVERY], - config[CONF_SEND_FIRST_AT], - ) + window_size: int = config[CONF_WINDOW_SIZE] + send_every: int = config[CONF_SEND_EVERY] + send_first_at: int = config[CONF_SEND_FIRST_AT] + + # Optimization: Use streaming filter for batch windows (window_size == send_every) + if window_size == send_every: + rhs = StreamingMovingAverageFilter.new(window_size, send_first_at) + return cg.Pvariable(filter_id, rhs, StreamingMovingAverageFilter) + rhs = SlidingWindowMovingAverageFilter.new(window_size, send_every, send_first_at) + return cg.Pvariable(filter_id, rhs, SlidingWindowMovingAverageFilter) EXPONENTIAL_AVERAGE_SCHEMA = cv.All( diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index 3241ae28af..1eb0b84964 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -32,50 +32,76 @@ void Filter::initialize(Sensor *parent, Filter *next) { this->next_ = next; } -// MedianFilter -MedianFilter::MedianFilter(size_t window_size, size_t send_every, size_t send_first_at) - : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {} -void MedianFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } -void MedianFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } -optional MedianFilter::new_value(float value) { - while (this->queue_.size() >= this->window_size_) { - this->queue_.pop_front(); - } - this->queue_.push_back(value); - ESP_LOGVV(TAG, "MedianFilter(%p)::new_value(%f)", this, value); +// SlidingWindowFilter +SlidingWindowFilter::SlidingWindowFilter(size_t window_size, size_t send_every, size_t send_first_at) + : window_size_(window_size), send_every_(send_every), send_at_(send_every - send_first_at) { + // Allocate ring buffer once at initialization + this->window_.init(window_size); +} +optional SlidingWindowFilter::new_value(float value) { + // Add value to ring buffer + if (this->window_count_ < this->window_size_) { + // Buffer not yet full - just append + this->window_.push_back(value); + this->window_count_++; + } else { + // Buffer full - overwrite oldest value (ring buffer) + this->window_[this->window_head_] = value; + this->window_head_++; + if (this->window_head_ >= this->window_size_) { + this->window_head_ = 0; + } + } + + // Check if we should send a result if (++this->send_at_ >= this->send_every_) { this->send_at_ = 0; - - float median = NAN; - if (!this->queue_.empty()) { - // Copy queue without NaN values - std::vector median_queue; - median_queue.reserve(this->queue_.size()); - for (auto v : this->queue_) { - if (!std::isnan(v)) { - median_queue.push_back(v); - } - } - - sort(median_queue.begin(), median_queue.end()); - - size_t queue_size = median_queue.size(); - if (queue_size) { - if (queue_size % 2) { - median = median_queue[queue_size / 2]; - } else { - median = (median_queue[queue_size / 2] + median_queue[(queue_size / 2) - 1]) / 2.0f; - } - } - } - - ESP_LOGVV(TAG, "MedianFilter(%p)::new_value(%f) SENDING %f", this, value, median); - return median; + float result = this->compute_result(); + ESP_LOGVV(TAG, "SlidingWindowFilter(%p)::new_value(%f) SENDING %f", this, value, result); + return result; } return {}; } +// SortedWindowFilter +FixedVector SortedWindowFilter::get_window_values_() { + // Copy window without NaN values using FixedVector (no heap allocation) + // Returns unsorted values - caller will use std::nth_element for partial sorting as needed + FixedVector values; + values.init(this->window_count_); + for (size_t i = 0; i < this->window_count_; i++) { + float v = this->window_[i]; + if (!std::isnan(v)) { + values.push_back(v); + } + } + return values; +} + +// MedianFilter +float MedianFilter::compute_result() { + FixedVector values = this->get_window_values_(); + if (values.empty()) + return NAN; + + size_t size = values.size(); + size_t mid = size / 2; + + if (size % 2) { + // Odd number of elements - use nth_element to find middle element + std::nth_element(values.begin(), values.begin() + mid, values.end()); + return values[mid]; + } + // Even number of elements - need both middle elements + // Use nth_element to find upper middle element + std::nth_element(values.begin(), values.begin() + mid, values.end()); + float upper = values[mid]; + // Find the maximum of the lower half (which is now everything before mid) + float lower = *std::max_element(values.begin(), values.begin() + mid); + return (lower + upper) / 2.0f; +} + // SkipInitialFilter SkipInitialFilter::SkipInitialFilter(size_t num_to_ignore) : num_to_ignore_(num_to_ignore) {} optional SkipInitialFilter::new_value(float value) { @@ -91,136 +117,39 @@ optional SkipInitialFilter::new_value(float value) { // QuantileFilter QuantileFilter::QuantileFilter(size_t window_size, size_t send_every, size_t send_first_at, float quantile) - : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size), quantile_(quantile) {} -void QuantileFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } -void QuantileFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } -void QuantileFilter::set_quantile(float quantile) { this->quantile_ = quantile; } -optional QuantileFilter::new_value(float value) { - while (this->queue_.size() >= this->window_size_) { - this->queue_.pop_front(); - } - this->queue_.push_back(value); - ESP_LOGVV(TAG, "QuantileFilter(%p)::new_value(%f), quantile:%f", this, value, this->quantile_); + : SortedWindowFilter(window_size, send_every, send_first_at), quantile_(quantile) {} - if (++this->send_at_ >= this->send_every_) { - this->send_at_ = 0; +float QuantileFilter::compute_result() { + FixedVector values = this->get_window_values_(); + if (values.empty()) + return NAN; - float result = NAN; - if (!this->queue_.empty()) { - // Copy queue without NaN values - std::vector quantile_queue; - for (auto v : this->queue_) { - if (!std::isnan(v)) { - quantile_queue.push_back(v); - } - } + size_t position = ceilf(values.size() * this->quantile_) - 1; + ESP_LOGVV(TAG, "QuantileFilter(%p)::position: %zu/%zu", this, position + 1, values.size()); - sort(quantile_queue.begin(), quantile_queue.end()); - - size_t queue_size = quantile_queue.size(); - if (queue_size) { - size_t position = ceilf(queue_size * this->quantile_) - 1; - ESP_LOGVV(TAG, "QuantileFilter(%p)::position: %zu/%zu", this, position + 1, queue_size); - result = quantile_queue[position]; - } - } - - ESP_LOGVV(TAG, "QuantileFilter(%p)::new_value(%f) SENDING %f", this, value, result); - return result; - } - return {}; + // Use nth_element to find the quantile element (O(n) instead of O(n log n)) + std::nth_element(values.begin(), values.begin() + position, values.end()); + return values[position]; } // MinFilter -MinFilter::MinFilter(size_t window_size, size_t send_every, size_t send_first_at) - : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {} -void MinFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } -void MinFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } -optional MinFilter::new_value(float value) { - while (this->queue_.size() >= this->window_size_) { - this->queue_.pop_front(); - } - this->queue_.push_back(value); - ESP_LOGVV(TAG, "MinFilter(%p)::new_value(%f)", this, value); - - if (++this->send_at_ >= this->send_every_) { - this->send_at_ = 0; - - float min = NAN; - for (auto v : this->queue_) { - if (!std::isnan(v)) { - min = std::isnan(min) ? v : std::min(min, v); - } - } - - ESP_LOGVV(TAG, "MinFilter(%p)::new_value(%f) SENDING %f", this, value, min); - return min; - } - return {}; -} +float MinFilter::compute_result() { return this->find_extremum_>(); } // MaxFilter -MaxFilter::MaxFilter(size_t window_size, size_t send_every, size_t send_first_at) - : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {} -void MaxFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } -void MaxFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } -optional MaxFilter::new_value(float value) { - while (this->queue_.size() >= this->window_size_) { - this->queue_.pop_front(); - } - this->queue_.push_back(value); - ESP_LOGVV(TAG, "MaxFilter(%p)::new_value(%f)", this, value); - - if (++this->send_at_ >= this->send_every_) { - this->send_at_ = 0; - - float max = NAN; - for (auto v : this->queue_) { - if (!std::isnan(v)) { - max = std::isnan(max) ? v : std::max(max, v); - } - } - - ESP_LOGVV(TAG, "MaxFilter(%p)::new_value(%f) SENDING %f", this, value, max); - return max; - } - return {}; -} +float MaxFilter::compute_result() { return this->find_extremum_>(); } // SlidingWindowMovingAverageFilter -SlidingWindowMovingAverageFilter::SlidingWindowMovingAverageFilter(size_t window_size, size_t send_every, - size_t send_first_at) - : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {} -void SlidingWindowMovingAverageFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } -void SlidingWindowMovingAverageFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } -optional SlidingWindowMovingAverageFilter::new_value(float value) { - while (this->queue_.size() >= this->window_size_) { - this->queue_.pop_front(); - } - this->queue_.push_back(value); - ESP_LOGVV(TAG, "SlidingWindowMovingAverageFilter(%p)::new_value(%f)", this, value); - - if (++this->send_at_ >= this->send_every_) { - this->send_at_ = 0; - - float sum = 0; - size_t valid_count = 0; - for (auto v : this->queue_) { - if (!std::isnan(v)) { - sum += v; - valid_count++; - } +float SlidingWindowMovingAverageFilter::compute_result() { + float sum = 0; + size_t valid_count = 0; + for (size_t i = 0; i < this->window_count_; i++) { + float v = this->window_[i]; + if (!std::isnan(v)) { + sum += v; + valid_count++; } - - float average = NAN; - if (valid_count) { - average = sum / valid_count; - } - - ESP_LOGVV(TAG, "SlidingWindowMovingAverageFilter(%p)::new_value(%f) SENDING %f", this, value, average); - return average; } - return {}; + return valid_count ? sum / valid_count : NAN; } // ExponentialMovingAverageFilter @@ -543,5 +472,78 @@ optional ToNTCTemperatureFilter::new_value(float value) { return temp; } +// StreamingFilter (base class) +StreamingFilter::StreamingFilter(size_t window_size, size_t send_first_at) + : window_size_(window_size), send_first_at_(send_first_at) {} + +optional StreamingFilter::new_value(float value) { + // Process the value (child class tracks min/max/sum/etc) + this->process_value(value); + + this->count_++; + + // Check if we should send (handle send_first_at for first value) + bool should_send = false; + if (this->first_send_ && this->count_ >= this->send_first_at_) { + should_send = true; + this->first_send_ = false; + } else if (!this->first_send_ && this->count_ >= this->window_size_) { + should_send = true; + } + + if (should_send) { + float result = this->compute_batch_result(); + // Reset for next batch + this->count_ = 0; + this->reset_batch(); + ESP_LOGVV(TAG, "StreamingFilter(%p)::new_value(%f) SENDING %f", this, value, result); + return result; + } + + return {}; +} + +// StreamingMinFilter +void StreamingMinFilter::process_value(float value) { + // Update running minimum (ignore NaN values) + if (!std::isnan(value)) { + this->current_min_ = std::isnan(this->current_min_) ? value : std::min(this->current_min_, value); + } +} + +float StreamingMinFilter::compute_batch_result() { return this->current_min_; } + +void StreamingMinFilter::reset_batch() { this->current_min_ = NAN; } + +// StreamingMaxFilter +void StreamingMaxFilter::process_value(float value) { + // Update running maximum (ignore NaN values) + if (!std::isnan(value)) { + this->current_max_ = std::isnan(this->current_max_) ? value : std::max(this->current_max_, value); + } +} + +float StreamingMaxFilter::compute_batch_result() { return this->current_max_; } + +void StreamingMaxFilter::reset_batch() { this->current_max_ = NAN; } + +// StreamingMovingAverageFilter +void StreamingMovingAverageFilter::process_value(float value) { + // Accumulate sum (ignore NaN values) + if (!std::isnan(value)) { + this->sum_ += value; + this->valid_count_++; + } +} + +float StreamingMovingAverageFilter::compute_batch_result() { + return this->valid_count_ > 0 ? this->sum_ / this->valid_count_ : NAN; +} + +void StreamingMovingAverageFilter::reset_batch() { + this->sum_ = 0.0f; + this->valid_count_ = 0; +} + } // namespace sensor } // namespace esphome diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 49d83e5b4b..57bb06b517 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -44,11 +44,75 @@ class Filter { Sensor *parent_{nullptr}; }; +/** Base class for filters that use a sliding window of values. + * + * Uses a ring buffer to efficiently maintain a fixed-size sliding window without + * reallocations or pop_front() overhead. Eliminates deque fragmentation issues. + */ +class SlidingWindowFilter : public Filter { + public: + SlidingWindowFilter(size_t window_size, size_t send_every, size_t send_first_at); + + optional new_value(float value) final; + + protected: + /// Called by new_value() to compute the filtered result from the current window + virtual float compute_result() = 0; + + /// Access the sliding window values (ring buffer implementation) + /// Use: for (size_t i = 0; i < window_count_; i++) { float val = window_[i]; } + FixedVector window_; + size_t window_head_{0}; ///< Index where next value will be written + size_t window_count_{0}; ///< Number of valid values in window (0 to window_size_) + size_t window_size_; ///< Maximum window size + size_t send_every_; ///< Send result every N values + size_t send_at_; ///< Counter for send_every +}; + +/** Base class for Min/Max filters. + * + * Provides a templated helper to find extremum values efficiently. + */ +class MinMaxFilter : public SlidingWindowFilter { + public: + using SlidingWindowFilter::SlidingWindowFilter; + + protected: + /// Helper to find min or max value in window, skipping NaN values + /// Usage: find_extremum_>() for min, find_extremum_>() for max + template float find_extremum_() { + float result = NAN; + Compare comp; + for (size_t i = 0; i < this->window_count_; i++) { + float v = this->window_[i]; + if (!std::isnan(v)) { + result = std::isnan(result) ? v : (comp(v, result) ? v : result); + } + } + return result; + } +}; + +/** Base class for filters that need a sorted window (Median, Quantile). + * + * Extends SlidingWindowFilter to provide a helper that filters out NaN values. + * Derived classes use std::nth_element for efficient partial sorting. + */ +class SortedWindowFilter : public SlidingWindowFilter { + public: + using SlidingWindowFilter::SlidingWindowFilter; + + protected: + /// Helper to get non-NaN values from the window (not sorted - caller will use nth_element) + /// Returns empty FixedVector if all values are NaN + FixedVector get_window_values_(); +}; + /** Simple quantile filter. * - * Takes the quantile of the last values and pushes it out every . + * Takes the quantile of the last values and pushes it out every . */ -class QuantileFilter : public Filter { +class QuantileFilter : public SortedWindowFilter { public: /** Construct a QuantileFilter. * @@ -61,25 +125,18 @@ class QuantileFilter : public Filter { */ explicit QuantileFilter(size_t window_size, size_t send_every, size_t send_first_at, float quantile); - optional new_value(float value) override; - - void set_send_every(size_t send_every); - void set_window_size(size_t window_size); - void set_quantile(float quantile); + void set_quantile(float quantile) { this->quantile_ = quantile; } protected: - std::deque queue_; - size_t send_every_; - size_t send_at_; - size_t window_size_; + float compute_result() override; float quantile_; }; /** Simple median filter. * - * Takes the median of the last values and pushes it out every . + * Takes the median of the last values and pushes it out every . */ -class MedianFilter : public Filter { +class MedianFilter : public SortedWindowFilter { public: /** Construct a MedianFilter. * @@ -89,18 +146,10 @@ class MedianFilter : public Filter { * on startup being published on the first *raw* value, so with no filter applied. Must be less than or equal to * send_every. */ - explicit MedianFilter(size_t window_size, size_t send_every, size_t send_first_at); - - optional new_value(float value) override; - - void set_send_every(size_t send_every); - void set_window_size(size_t window_size); + using SortedWindowFilter::SortedWindowFilter; protected: - std::deque queue_; - size_t send_every_; - size_t send_at_; - size_t window_size_; + float compute_result() override; }; /** Simple skip filter. @@ -123,9 +172,9 @@ class SkipInitialFilter : public Filter { /** Simple min filter. * - * Takes the min of the last values and pushes it out every . + * Takes the min of the last values and pushes it out every . */ -class MinFilter : public Filter { +class MinFilter : public MinMaxFilter { public: /** Construct a MinFilter. * @@ -135,25 +184,17 @@ class MinFilter : public Filter { * on startup being published on the first *raw* value, so with no filter applied. Must be less than or equal to * send_every. */ - explicit MinFilter(size_t window_size, size_t send_every, size_t send_first_at); - - optional new_value(float value) override; - - void set_send_every(size_t send_every); - void set_window_size(size_t window_size); + using MinMaxFilter::MinMaxFilter; protected: - std::deque queue_; - size_t send_every_; - size_t send_at_; - size_t window_size_; + float compute_result() override; }; /** Simple max filter. * - * Takes the max of the last values and pushes it out every . + * Takes the max of the last values and pushes it out every . */ -class MaxFilter : public Filter { +class MaxFilter : public MinMaxFilter { public: /** Construct a MaxFilter. * @@ -163,18 +204,10 @@ class MaxFilter : public Filter { * on startup being published on the first *raw* value, so with no filter applied. Must be less than or equal to * send_every. */ - explicit MaxFilter(size_t window_size, size_t send_every, size_t send_first_at); - - optional new_value(float value) override; - - void set_send_every(size_t send_every); - void set_window_size(size_t window_size); + using MinMaxFilter::MinMaxFilter; protected: - std::deque queue_; - size_t send_every_; - size_t send_at_; - size_t window_size_; + float compute_result() override; }; /** Simple sliding window moving average filter. @@ -182,7 +215,7 @@ class MaxFilter : public Filter { * Essentially just takes takes the average of the last window_size values and pushes them out * every send_every. */ -class SlidingWindowMovingAverageFilter : public Filter { +class SlidingWindowMovingAverageFilter : public SlidingWindowFilter { public: /** Construct a SlidingWindowMovingAverageFilter. * @@ -192,18 +225,10 @@ class SlidingWindowMovingAverageFilter : public Filter { * on startup being published on the first *raw* value, so with no filter applied. Must be less than or equal to * send_every. */ - explicit SlidingWindowMovingAverageFilter(size_t window_size, size_t send_every, size_t send_first_at); - - optional new_value(float value) override; - - void set_send_every(size_t send_every); - void set_window_size(size_t window_size); + using SlidingWindowFilter::SlidingWindowFilter; protected: - std::deque queue_; - size_t send_every_; - size_t send_at_; - size_t window_size_; + float compute_result() override; }; /** Simple exponential moving average filter. @@ -476,5 +501,81 @@ class ToNTCTemperatureFilter : public Filter { double c_; }; +/** Base class for streaming filters (batch windows where window_size == send_every). + * + * When window_size equals send_every, we don't need a sliding window. + * This base class handles the common batching logic. + */ +class StreamingFilter : public Filter { + public: + StreamingFilter(size_t window_size, size_t send_first_at); + + optional new_value(float value) final; + + protected: + /// Called by new_value() to process each value in the batch + virtual void process_value(float value) = 0; + + /// Called by new_value() to compute the result after collecting window_size values + virtual float compute_batch_result() = 0; + + /// Called by new_value() to reset internal state after sending a result + virtual void reset_batch() = 0; + + size_t window_size_; + size_t count_{0}; + size_t send_first_at_; + bool first_send_{true}; +}; + +/** Streaming min filter for batch windows (window_size == send_every). + * + * Uses O(1) memory instead of O(n) by tracking only the minimum value. + */ +class StreamingMinFilter : public StreamingFilter { + public: + using StreamingFilter::StreamingFilter; + + protected: + void process_value(float value) override; + float compute_batch_result() override; + void reset_batch() override; + + float current_min_{NAN}; +}; + +/** Streaming max filter for batch windows (window_size == send_every). + * + * Uses O(1) memory instead of O(n) by tracking only the maximum value. + */ +class StreamingMaxFilter : public StreamingFilter { + public: + using StreamingFilter::StreamingFilter; + + protected: + void process_value(float value) override; + float compute_batch_result() override; + void reset_batch() override; + + float current_max_{NAN}; +}; + +/** Streaming moving average filter for batch windows (window_size == send_every). + * + * Uses O(1) memory instead of O(n) by tracking only sum and count. + */ +class StreamingMovingAverageFilter : public StreamingFilter { + public: + using StreamingFilter::StreamingFilter; + + protected: + void process_value(float value) override; + float compute_batch_result() override; + void reset_batch() override; + + float sum_{0.0f}; + size_t valid_count_{0}; +}; + } // namespace sensor } // namespace esphome diff --git a/tests/components/sensor/common.yaml b/tests/components/sensor/common.yaml new file mode 100644 index 0000000000..ace7d0a38a --- /dev/null +++ b/tests/components/sensor/common.yaml @@ -0,0 +1,101 @@ +sensor: + # Source sensor for testing filters + - platform: template + name: "Source Sensor" + id: source_sensor + lambda: return 42.0; + update_interval: 1s + + # Streaming filters (window_size == send_every) - uses StreamingFilter base class + - platform: copy + source_id: source_sensor + name: "Streaming Min Filter" + filters: + - min: + window_size: 10 + send_every: 10 # Batch window → StreamingMinFilter + + - platform: copy + source_id: source_sensor + name: "Streaming Max Filter" + filters: + - max: + window_size: 10 + send_every: 10 # Batch window → StreamingMaxFilter + + - platform: copy + source_id: source_sensor + name: "Streaming Moving Average Filter" + filters: + - sliding_window_moving_average: + window_size: 10 + send_every: 10 # Batch window → StreamingMovingAverageFilter + + # Sliding window filters (window_size != send_every) - uses SlidingWindowFilter base class with ring buffer + - platform: copy + source_id: source_sensor + name: "Sliding Min Filter" + filters: + - min: + window_size: 10 + send_every: 5 # Sliding window → MinFilter with ring buffer + + - platform: copy + source_id: source_sensor + name: "Sliding Max Filter" + filters: + - max: + window_size: 10 + send_every: 5 # Sliding window → MaxFilter with ring buffer + + - platform: copy + source_id: source_sensor + name: "Sliding Median Filter" + filters: + - median: + window_size: 10 + send_every: 5 # Sliding window → MedianFilter with ring buffer + + - platform: copy + source_id: source_sensor + name: "Sliding Quantile Filter" + filters: + - quantile: + window_size: 10 + send_every: 5 + quantile: 0.9 # Sliding window → QuantileFilter with ring buffer + + - platform: copy + source_id: source_sensor + name: "Sliding Moving Average Filter" + filters: + - sliding_window_moving_average: + window_size: 10 + send_every: 5 # Sliding window → SlidingWindowMovingAverageFilter with ring buffer + + # Edge cases + - platform: copy + source_id: source_sensor + name: "Large Batch Window Min" + filters: + - min: + window_size: 1000 + send_every: 1000 # Large batch → StreamingMinFilter (4 bytes, not 4KB) + + - platform: copy + source_id: source_sensor + name: "Small Sliding Window" + filters: + - median: + window_size: 3 + send_every: 1 # Frequent output → MedianFilter with 3-element ring buffer + + # send_first_at parameter test + - platform: copy + source_id: source_sensor + name: "Early Send Filter" + filters: + - max: + window_size: 10 + send_every: 10 + send_first_at: 1 # Send after first value diff --git a/tests/components/sensor/test.esp8266-ard.yaml b/tests/components/sensor/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/sensor/test.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/integration/README.md b/tests/integration/README.md index 8fce81bb80..2a6b6fe564 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -7,6 +7,7 @@ This directory contains end-to-end integration tests for ESPHome, focusing on te - `conftest.py` - Common fixtures and utilities - `const.py` - Constants used throughout the integration tests - `types.py` - Type definitions for fixtures and functions +- `state_utils.py` - State handling utilities (e.g., `InitialStateHelper`, `build_key_to_entity_mapping`) - `fixtures/` - YAML configuration files for tests - `test_*.py` - Individual test files @@ -26,6 +27,32 @@ The `yaml_config` fixture automatically loads YAML configurations based on the t - `reserved_tcp_port` - Reserves a TCP port by holding the socket open until ESPHome needs it - `unused_tcp_port` - Provides the reserved port number for each test +### Helper Utilities + +#### InitialStateHelper (`state_utils.py`) + +The `InitialStateHelper` class solves a common problem in integration tests: when an API client connects, ESPHome automatically broadcasts the current state of all entities. This can interfere with tests that want to track only new state changes triggered by test actions. + +**What it does:** +- Tracks all entities (except stateless ones like buttons) +- Swallows the first state broadcast for each entity +- Forwards all subsequent state changes to your test callback +- Provides `wait_for_initial_states()` to synchronize before test actions + +**When to use it:** +- Any test that triggers entity state changes and needs to verify them +- Tests that would otherwise see duplicate or unexpected states +- Tests that need clean separation between initial state and test-triggered changes + +**Implementation details:** +- Uses `(device_id, key)` tuples to uniquely identify entities across devices +- Automatically excludes `ButtonInfo` entities (stateless) +- Provides debug logging to track state reception (use `--log-cli-level=DEBUG`) +- Safe for concurrent use with multiple entity types + +**Future work:** +Consider converting existing integration tests to use `InitialStateHelper` for more reliable state tracking and to eliminate race conditions related to initial state broadcasts. + ### Writing Tests The simplest way to write a test is to use the `run_compiled` and `api_client_connected` fixtures: @@ -125,6 +152,54 @@ async def test_my_sensor( ``` ##### State Subscription Pattern + +**Recommended: Using InitialStateHelper** + +When an API client connects, ESPHome automatically sends the current state of all entities. The `InitialStateHelper` (from `state_utils.py`) handles this by swallowing these initial states and only forwarding subsequent state changes to your test callback: + +```python +from .state_utils import InitialStateHelper + +# Track state changes with futures +loop = asyncio.get_running_loop() +states: dict[int, EntityState] = {} +state_future: asyncio.Future[EntityState] = loop.create_future() + +def on_state(state: EntityState) -> None: + """This callback only receives NEW state changes, not initial states.""" + states[state.key] = state + # Check for specific condition using isinstance + if isinstance(state, SensorState) and state.state == expected_value: + if not state_future.done(): + state_future.set_result(state) + +# Get entities and set up state synchronization +entities, services = await client.list_entities_services() +initial_state_helper = InitialStateHelper(entities) + +# Subscribe with the wrapper that filters initial states +client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + +# Wait for all initial states to be broadcast +try: + await initial_state_helper.wait_for_initial_states() +except TimeoutError: + pytest.fail("Timeout waiting for initial states") + +# Now perform your test actions - on_state will only receive new changes +# ... trigger state changes ... + +# Wait for expected state +try: + result = await asyncio.wait_for(state_future, timeout=5.0) +except asyncio.TimeoutError: + pytest.fail(f"Expected state not received. Got: {list(states.values())}") +``` + +**Legacy: Manual State Tracking** + +If you need to handle initial states manually (not recommended for new tests): + ```python # Track state changes with futures loop = asyncio.get_running_loop() diff --git a/tests/integration/fixtures/sensor_filters_batch_window.yaml b/tests/integration/fixtures/sensor_filters_batch_window.yaml new file mode 100644 index 0000000000..58a254c215 --- /dev/null +++ b/tests/integration/fixtures/sensor_filters_batch_window.yaml @@ -0,0 +1,58 @@ +esphome: + name: test-batch-window-filters + +host: +api: + batch_delay: 0ms # Disable batching to receive all state updates +logger: + level: DEBUG + +# Template sensor that we'll use to publish values +sensor: + - platform: template + name: "Source Sensor" + id: source_sensor + accuracy_decimals: 2 + + # Batch window filters (window_size == send_every) - use streaming filters + - platform: copy + source_id: source_sensor + name: "Min Sensor" + id: min_sensor + filters: + - min: + window_size: 5 + send_every: 5 + send_first_at: 1 + + - platform: copy + source_id: source_sensor + name: "Max Sensor" + id: max_sensor + filters: + - max: + window_size: 5 + send_every: 5 + send_first_at: 1 + + - platform: copy + source_id: source_sensor + name: "Moving Avg Sensor" + id: moving_avg_sensor + filters: + - sliding_window_moving_average: + window_size: 5 + send_every: 5 + send_first_at: 1 + +# Button to trigger publishing test values +button: + - platform: template + name: "Publish Values Button" + id: publish_button + on_press: + - lambda: |- + // Publish 10 values: 1.0, 2.0, ..., 10.0 + for (int i = 1; i <= 10; i++) { + id(source_sensor).publish_state(float(i)); + } diff --git a/tests/integration/fixtures/sensor_filters_nan_handling.yaml b/tests/integration/fixtures/sensor_filters_nan_handling.yaml new file mode 100644 index 0000000000..fcb12cfde5 --- /dev/null +++ b/tests/integration/fixtures/sensor_filters_nan_handling.yaml @@ -0,0 +1,84 @@ +esphome: + name: test-nan-handling + +host: +api: + batch_delay: 0ms # Disable batching to receive all state updates +logger: + level: DEBUG + +sensor: + - platform: template + name: "Source NaN Sensor" + id: source_nan_sensor + accuracy_decimals: 2 + + - platform: copy + source_id: source_nan_sensor + name: "Min NaN Sensor" + id: min_nan_sensor + filters: + - min: + window_size: 5 + send_every: 5 + send_first_at: 1 + + - platform: copy + source_id: source_nan_sensor + name: "Max NaN Sensor" + id: max_nan_sensor + filters: + - max: + window_size: 5 + send_every: 5 + send_first_at: 1 + +script: + - id: publish_nan_values_script + then: + - sensor.template.publish: + id: source_nan_sensor + state: 10.0 + - delay: 20ms + - sensor.template.publish: + id: source_nan_sensor + state: !lambda 'return NAN;' + - delay: 20ms + - sensor.template.publish: + id: source_nan_sensor + state: 5.0 + - delay: 20ms + - sensor.template.publish: + id: source_nan_sensor + state: !lambda 'return NAN;' + - delay: 20ms + - sensor.template.publish: + id: source_nan_sensor + state: 15.0 + - delay: 20ms + - sensor.template.publish: + id: source_nan_sensor + state: 8.0 + - delay: 20ms + - sensor.template.publish: + id: source_nan_sensor + state: !lambda 'return NAN;' + - delay: 20ms + - sensor.template.publish: + id: source_nan_sensor + state: 12.0 + - delay: 20ms + - sensor.template.publish: + id: source_nan_sensor + state: 3.0 + - delay: 20ms + - sensor.template.publish: + id: source_nan_sensor + state: !lambda 'return NAN;' + +button: + - platform: template + name: "Publish NaN Values Button" + id: publish_nan_button + on_press: + - script.execute: publish_nan_values_script diff --git a/tests/integration/fixtures/sensor_filters_ring_buffer.yaml b/tests/integration/fixtures/sensor_filters_ring_buffer.yaml new file mode 100644 index 0000000000..ea7a326b8d --- /dev/null +++ b/tests/integration/fixtures/sensor_filters_ring_buffer.yaml @@ -0,0 +1,115 @@ +esphome: + name: test-sliding-window-filters + +host: +api: + batch_delay: 0ms # Disable batching to receive all state updates +logger: + level: DEBUG + +# Template sensor that we'll use to publish values +sensor: + - platform: template + name: "Source Sensor" + id: source_sensor + accuracy_decimals: 2 + + # ACTUAL sliding window filters (window_size != send_every) - use ring buffers + # Window of 5, send every 2 values + - platform: copy + source_id: source_sensor + name: "Sliding Min Sensor" + id: sliding_min_sensor + filters: + - min: + window_size: 5 + send_every: 2 + send_first_at: 1 + + - platform: copy + source_id: source_sensor + name: "Sliding Max Sensor" + id: sliding_max_sensor + filters: + - max: + window_size: 5 + send_every: 2 + send_first_at: 1 + + - platform: copy + source_id: source_sensor + name: "Sliding Median Sensor" + id: sliding_median_sensor + filters: + - median: + window_size: 5 + send_every: 2 + send_first_at: 1 + + - platform: copy + source_id: source_sensor + name: "Sliding Moving Avg Sensor" + id: sliding_moving_avg_sensor + filters: + - sliding_window_moving_average: + window_size: 5 + send_every: 2 + send_first_at: 1 + +# Button to trigger publishing test values +script: + - id: publish_values_script + then: + # Publish 10 values: 1.0, 2.0, ..., 10.0 + # With window_size=5, send_every=2, send_first_at=1: + # - Output at position 1: window=[1], min=1, max=1, median=1, avg=1 + # - Output at position 3: window=[1,2,3], min=1, max=3, median=2, avg=2 + # - Output at position 5: window=[1,2,3,4,5], min=1, max=5, median=3, avg=3 + # - Output at position 7: window=[3,4,5,6,7], min=3, max=7, median=5, avg=5 + # - Output at position 9: window=[5,6,7,8,9], min=5, max=9, median=7, avg=7 + - sensor.template.publish: + id: source_sensor + state: 1.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 2.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 3.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 4.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 5.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 6.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 7.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 8.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 9.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 10.0 + +button: + - platform: template + name: "Publish Values Button" + id: publish_button + on_press: + - script.execute: publish_values_script diff --git a/tests/integration/fixtures/sensor_filters_ring_buffer_wraparound.yaml b/tests/integration/fixtures/sensor_filters_ring_buffer_wraparound.yaml new file mode 100644 index 0000000000..bd5980160b --- /dev/null +++ b/tests/integration/fixtures/sensor_filters_ring_buffer_wraparound.yaml @@ -0,0 +1,72 @@ +esphome: + name: test-ring-buffer-wraparound + +host: +api: + batch_delay: 0ms # Disable batching to receive all state updates +logger: + level: DEBUG + +sensor: + - platform: template + name: "Source Wraparound Sensor" + id: source_wraparound + accuracy_decimals: 2 + + - platform: copy + source_id: source_wraparound + name: "Wraparound Min Sensor" + id: wraparound_min_sensor + filters: + - min: + window_size: 3 + send_every: 3 + send_first_at: 1 + +script: + - id: publish_wraparound_script + then: + # Publish 9 values to test ring buffer wraparound + # Values: 10, 20, 30, 5, 25, 15, 40, 35, 20 + - sensor.template.publish: + id: source_wraparound + state: 10.0 + - delay: 20ms + - sensor.template.publish: + id: source_wraparound + state: 20.0 + - delay: 20ms + - sensor.template.publish: + id: source_wraparound + state: 30.0 + - delay: 20ms + - sensor.template.publish: + id: source_wraparound + state: 5.0 + - delay: 20ms + - sensor.template.publish: + id: source_wraparound + state: 25.0 + - delay: 20ms + - sensor.template.publish: + id: source_wraparound + state: 15.0 + - delay: 20ms + - sensor.template.publish: + id: source_wraparound + state: 40.0 + - delay: 20ms + - sensor.template.publish: + id: source_wraparound + state: 35.0 + - delay: 20ms + - sensor.template.publish: + id: source_wraparound + state: 20.0 + +button: + - platform: template + name: "Publish Wraparound Button" + id: publish_wraparound_button + on_press: + - script.execute: publish_wraparound_script diff --git a/tests/integration/fixtures/sensor_filters_sliding_window.yaml b/tests/integration/fixtures/sensor_filters_sliding_window.yaml new file mode 100644 index 0000000000..2055118811 --- /dev/null +++ b/tests/integration/fixtures/sensor_filters_sliding_window.yaml @@ -0,0 +1,123 @@ +esphome: + name: test-sliding-window-filters + +host: +api: + batch_delay: 0ms # Disable batching to receive all state updates +logger: + level: DEBUG + +# Template sensor that we'll use to publish values +sensor: + - platform: template + name: "Source Sensor" + id: source_sensor + accuracy_decimals: 2 + + # Min filter sensor + - platform: copy + source_id: source_sensor + name: "Min Sensor" + id: min_sensor + filters: + - min: + window_size: 5 + send_every: 5 + send_first_at: 1 + + # Max filter sensor + - platform: copy + source_id: source_sensor + name: "Max Sensor" + id: max_sensor + filters: + - max: + window_size: 5 + send_every: 5 + send_first_at: 1 + + # Median filter sensor + - platform: copy + source_id: source_sensor + name: "Median Sensor" + id: median_sensor + filters: + - median: + window_size: 5 + send_every: 5 + send_first_at: 1 + + # Quantile filter sensor (90th percentile) + - platform: copy + source_id: source_sensor + name: "Quantile Sensor" + id: quantile_sensor + filters: + - quantile: + window_size: 5 + send_every: 5 + send_first_at: 1 + quantile: 0.9 + + # Moving average filter sensor + - platform: copy + source_id: source_sensor + name: "Moving Avg Sensor" + id: moving_avg_sensor + filters: + - sliding_window_moving_average: + window_size: 5 + send_every: 5 + send_first_at: 1 + +# Script to publish values with delays +script: + - id: publish_values_script + then: + - sensor.template.publish: + id: source_sensor + state: 1.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 2.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 3.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 4.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 5.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 6.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 7.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 8.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 9.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor + state: 10.0 + +# Button to trigger publishing test values +button: + - platform: template + name: "Publish Values Button" + id: publish_button + on_press: + - script.execute: publish_values_script diff --git a/tests/integration/state_utils.py b/tests/integration/state_utils.py new file mode 100644 index 0000000000..58d6d2790f --- /dev/null +++ b/tests/integration/state_utils.py @@ -0,0 +1,167 @@ +"""Shared utilities for ESPHome integration tests - state handling.""" + +from __future__ import annotations + +import asyncio +import logging + +from aioesphomeapi import ButtonInfo, EntityInfo, EntityState + +_LOGGER = logging.getLogger(__name__) + + +def build_key_to_entity_mapping( + entities: list[EntityInfo], entity_names: list[str] +) -> dict[int, str]: + """Build a mapping from entity keys to entity names. + + Args: + entities: List of entity info objects from the API + entity_names: List of entity names to search for in object_ids + + Returns: + Dictionary mapping entity keys to entity names + """ + key_to_entity: dict[int, str] = {} + for entity in entities: + obj_id = entity.object_id.lower() + for entity_name in entity_names: + if entity_name in obj_id: + key_to_entity[entity.key] = entity_name + break + return key_to_entity + + +class InitialStateHelper: + """Helper to wait for initial states before processing test states. + + When an API client connects, ESPHome sends the current state of all entities. + This helper wraps the user's state callback and swallows the first state for + each entity, then forwards all subsequent states to the user callback. + + Usage: + entities, services = await client.list_entities_services() + helper = InitialStateHelper(entities) + client.subscribe_states(helper.on_state_wrapper(user_callback)) + await helper.wait_for_initial_states() + """ + + def __init__(self, entities: list[EntityInfo]) -> None: + """Initialize the helper. + + Args: + entities: All entities from list_entities_services() + """ + # Set of (device_id, key) tuples waiting for initial state + # Buttons are stateless, so exclude them + self._wait_initial_states = { + (entity.device_id, entity.key) + for entity in entities + if not isinstance(entity, ButtonInfo) + } + # Keep entity info for debugging - use (device_id, key) tuple + self._entities_by_id = { + (entity.device_id, entity.key): entity for entity in entities + } + + # Log all entities + _LOGGER.debug( + "InitialStateHelper: Found %d total entities: %s", + len(entities), + [(type(e).__name__, e.object_id) for e in entities], + ) + + # Log which ones we're waiting for + _LOGGER.debug( + "InitialStateHelper: Waiting for %d entities (excluding ButtonInfo): %s", + len(self._wait_initial_states), + [self._entities_by_id[k].object_id for k in self._wait_initial_states], + ) + + # Log which ones we're NOT waiting for + not_waiting = { + (e.device_id, e.key) for e in entities + } - self._wait_initial_states + if not_waiting: + not_waiting_info = [ + f"{type(self._entities_by_id[k]).__name__}:{self._entities_by_id[k].object_id}" + for k in not_waiting + ] + _LOGGER.debug( + "InitialStateHelper: NOT waiting for %d entities: %s", + len(not_waiting), + not_waiting_info, + ) + + # Create future in the running event loop + self._initial_states_received = asyncio.get_running_loop().create_future() + # If no entities to wait for, mark complete immediately + if not self._wait_initial_states: + self._initial_states_received.set_result(True) + + def on_state_wrapper(self, user_callback): + """Wrap a user callback to track initial states. + + Args: + user_callback: The user's state callback function + + Returns: + Wrapped callback that swallows first state per entity, forwards rest + """ + + def wrapper(state: EntityState) -> None: + """Swallow initial state per entity, forward subsequent states.""" + # Create entity identifier tuple + entity_id = (state.device_id, state.key) + + # Log which entity is sending state + if entity_id in self._entities_by_id: + entity = self._entities_by_id[entity_id] + _LOGGER.debug( + "Received state for %s (type: %s, device_id: %s, key: %d)", + entity.object_id, + type(entity).__name__, + state.device_id, + state.key, + ) + + # If this entity is waiting for initial state + if entity_id in self._wait_initial_states: + # Remove from waiting set + self._wait_initial_states.discard(entity_id) + + _LOGGER.debug( + "Swallowed initial state for %s, %d entities remaining", + self._entities_by_id[entity_id].object_id + if entity_id in self._entities_by_id + else entity_id, + len(self._wait_initial_states), + ) + + # Check if we've now seen all entities + if ( + not self._wait_initial_states + and not self._initial_states_received.done() + ): + _LOGGER.debug("All initial states received") + self._initial_states_received.set_result(True) + + # Don't forward initial state to user + return + + # Forward subsequent states to user callback + _LOGGER.debug("Forwarding state to user callback") + user_callback(state) + + return wrapper + + async def wait_for_initial_states(self, timeout: float = 5.0) -> None: + """Wait for all initial states to be received. + + Args: + timeout: Maximum time to wait in seconds + + Raises: + asyncio.TimeoutError: If initial states aren't received within timeout + """ + await asyncio.wait_for(self._initial_states_received, timeout=timeout) diff --git a/tests/integration/test_sensor_filters_ring_buffer.py b/tests/integration/test_sensor_filters_ring_buffer.py new file mode 100644 index 0000000000..c8be8edce0 --- /dev/null +++ b/tests/integration/test_sensor_filters_ring_buffer.py @@ -0,0 +1,151 @@ +"""Test sensor ring buffer filter functionality (window_size != send_every).""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState, SensorState +import pytest + +from .state_utils import InitialStateHelper, build_key_to_entity_mapping +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_sensor_filters_ring_buffer( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that ring buffer filters (window_size != send_every) work correctly.""" + loop = asyncio.get_running_loop() + + # Track state changes for each sensor + sensor_states: dict[str, list[float]] = { + "sliding_min": [], + "sliding_max": [], + "sliding_median": [], + "sliding_moving_avg": [], + } + + # Futures to track when we receive expected values + all_updates_received = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track sensor state updates.""" + if not isinstance(state, SensorState): + return + + # Skip NaN values + if state.missing_state: + return + + # Get the sensor name from the key mapping + sensor_name = key_to_sensor.get(state.key) + if not sensor_name or sensor_name not in sensor_states: + return + + sensor_states[sensor_name].append(state.state) + + # Check if we've received enough updates from all sensors + # With send_every=2, send_first_at=1, we expect 5 outputs per sensor + if ( + len(sensor_states["sliding_min"]) >= 5 + and len(sensor_states["sliding_max"]) >= 5 + and len(sensor_states["sliding_median"]) >= 5 + and len(sensor_states["sliding_moving_avg"]) >= 5 + and not all_updates_received.done() + ): + all_updates_received.set_result(True) + + async with ( + run_compiled(yaml_config), + api_client_connected() as client, + ): + # Get entities first to build key mapping + entities, services = await client.list_entities_services() + + # Build key-to-sensor mapping + key_to_sensor = build_key_to_entity_mapping( + entities, + [ + "sliding_min", + "sliding_max", + "sliding_median", + "sliding_moving_avg", + ], + ) + + # Set up initial state helper with all entities + initial_state_helper = InitialStateHelper(entities) + + # Subscribe to state changes with wrapper + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + + # Wait for initial states to be sent before pressing button + try: + await initial_state_helper.wait_for_initial_states() + except TimeoutError: + pytest.fail("Timeout waiting for initial states") + + # Find the publish button + publish_button = next( + (e for e in entities if "publish_values_button" in e.object_id.lower()), + None, + ) + assert publish_button is not None, "Publish Values Button not found" + + # Press the button to publish test values + client.button_command(publish_button.key) + + # Wait for all sensors to receive their values + try: + await asyncio.wait_for(all_updates_received, timeout=10.0) + except TimeoutError: + # Provide detailed failure info + pytest.fail( + f"Timeout waiting for updates. Received states:\n" + f" min: {sensor_states['sliding_min']}\n" + f" max: {sensor_states['sliding_max']}\n" + f" median: {sensor_states['sliding_median']}\n" + f" moving_avg: {sensor_states['sliding_moving_avg']}" + ) + + # Verify we got 5 outputs per sensor (positions 1, 3, 5, 7, 9) + assert len(sensor_states["sliding_min"]) == 5, ( + f"Min sensor should have 5 values, got {len(sensor_states['sliding_min'])}: {sensor_states['sliding_min']}" + ) + assert len(sensor_states["sliding_max"]) == 5 + assert len(sensor_states["sliding_median"]) == 5 + assert len(sensor_states["sliding_moving_avg"]) == 5 + + # Verify the values at each output position + # Position 1: window=[1] + assert sensor_states["sliding_min"][0] == pytest.approx(1.0) + assert sensor_states["sliding_max"][0] == pytest.approx(1.0) + assert sensor_states["sliding_median"][0] == pytest.approx(1.0) + assert sensor_states["sliding_moving_avg"][0] == pytest.approx(1.0) + + # Position 3: window=[1,2,3] + assert sensor_states["sliding_min"][1] == pytest.approx(1.0) + assert sensor_states["sliding_max"][1] == pytest.approx(3.0) + assert sensor_states["sliding_median"][1] == pytest.approx(2.0) + assert sensor_states["sliding_moving_avg"][1] == pytest.approx(2.0) + + # Position 5: window=[1,2,3,4,5] + assert sensor_states["sliding_min"][2] == pytest.approx(1.0) + assert sensor_states["sliding_max"][2] == pytest.approx(5.0) + assert sensor_states["sliding_median"][2] == pytest.approx(3.0) + assert sensor_states["sliding_moving_avg"][2] == pytest.approx(3.0) + + # Position 7: window=[3,4,5,6,7] (ring buffer wrapped) + assert sensor_states["sliding_min"][3] == pytest.approx(3.0) + assert sensor_states["sliding_max"][3] == pytest.approx(7.0) + assert sensor_states["sliding_median"][3] == pytest.approx(5.0) + assert sensor_states["sliding_moving_avg"][3] == pytest.approx(5.0) + + # Position 9: window=[5,6,7,8,9] (ring buffer wrapped) + assert sensor_states["sliding_min"][4] == pytest.approx(5.0) + assert sensor_states["sliding_max"][4] == pytest.approx(9.0) + assert sensor_states["sliding_median"][4] == pytest.approx(7.0) + assert sensor_states["sliding_moving_avg"][4] == pytest.approx(7.0) diff --git a/tests/integration/test_sensor_filters_sliding_window.py b/tests/integration/test_sensor_filters_sliding_window.py new file mode 100644 index 0000000000..b0688a6536 --- /dev/null +++ b/tests/integration/test_sensor_filters_sliding_window.py @@ -0,0 +1,395 @@ +"""Test sensor sliding window filter functionality.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState, SensorState +import pytest + +from .state_utils import InitialStateHelper, build_key_to_entity_mapping +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_sensor_filters_sliding_window( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that sliding window filters (min, max, median, quantile, moving_average) work correctly.""" + loop = asyncio.get_running_loop() + + # Track state changes for each sensor + sensor_states: dict[str, list[float]] = { + "min_sensor": [], + "max_sensor": [], + "median_sensor": [], + "quantile_sensor": [], + "moving_avg_sensor": [], + } + + # Futures to track when we receive expected values + min_received = loop.create_future() + max_received = loop.create_future() + median_received = loop.create_future() + quantile_received = loop.create_future() + moving_avg_received = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track sensor state updates.""" + if not isinstance(state, SensorState): + return + + # Skip NaN values + if state.missing_state: + return + + # Get the sensor name from the key mapping + sensor_name = key_to_sensor.get(state.key) + if not sensor_name or sensor_name not in sensor_states: + return + + sensor_states[sensor_name].append(state.state) + + # Check if we received the expected final value + # After publishing 10 values [1.0, 2.0, ..., 10.0], the window has the last 5: [2, 3, 4, 5, 6] + # Filters send at position 1 and position 6 (send_every=5 means every 5th value after first) + if ( + sensor_name == "min_sensor" + and state.state == pytest.approx(2.0) + and not min_received.done() + ): + min_received.set_result(True) + elif ( + sensor_name == "max_sensor" + and state.state == pytest.approx(6.0) + and not max_received.done() + ): + max_received.set_result(True) + elif ( + sensor_name == "median_sensor" + and state.state == pytest.approx(4.0) + and not median_received.done() + ): + # Median of [2, 3, 4, 5, 6] = 4 + median_received.set_result(True) + elif ( + sensor_name == "quantile_sensor" + and state.state == pytest.approx(6.0) + and not quantile_received.done() + ): + # 90th percentile of [2, 3, 4, 5, 6] = 6 + quantile_received.set_result(True) + elif ( + sensor_name == "moving_avg_sensor" + and state.state == pytest.approx(4.0) + and not moving_avg_received.done() + ): + # Average of [2, 3, 4, 5, 6] = 4 + moving_avg_received.set_result(True) + + async with ( + run_compiled(yaml_config), + api_client_connected() as client, + ): + # Get entities first to build key mapping + entities, services = await client.list_entities_services() + + # Build key-to-sensor mapping + key_to_sensor = build_key_to_entity_mapping( + entities, + [ + "min_sensor", + "max_sensor", + "median_sensor", + "quantile_sensor", + "moving_avg_sensor", + ], + ) + + # Set up initial state helper with all entities + initial_state_helper = InitialStateHelper(entities) + + # Subscribe to state changes with wrapper + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + + # Wait for initial states to be sent before pressing button + try: + await initial_state_helper.wait_for_initial_states() + except TimeoutError: + pytest.fail("Timeout waiting for initial states") + + # Find the publish button + publish_button = next( + (e for e in entities if "publish_values_button" in e.object_id.lower()), + None, + ) + assert publish_button is not None, "Publish Values Button not found" + + # Press the button to publish test values + client.button_command(publish_button.key) + + # Wait for all sensors to receive their final values + try: + await asyncio.wait_for( + asyncio.gather( + min_received, + max_received, + median_received, + quantile_received, + moving_avg_received, + ), + timeout=10.0, + ) + except TimeoutError: + # Provide detailed failure info + pytest.fail( + f"Timeout waiting for expected values. Received states:\n" + f" min: {sensor_states['min_sensor']}\n" + f" max: {sensor_states['max_sensor']}\n" + f" median: {sensor_states['median_sensor']}\n" + f" quantile: {sensor_states['quantile_sensor']}\n" + f" moving_avg: {sensor_states['moving_avg_sensor']}" + ) + + # Verify we got the expected values + # With batch_delay: 0ms, we should receive all outputs + # Filters output at positions 1 and 6 (send_every: 5) + assert len(sensor_states["min_sensor"]) == 2, ( + f"Min sensor should have 2 values, got {len(sensor_states['min_sensor'])}: {sensor_states['min_sensor']}" + ) + assert len(sensor_states["max_sensor"]) == 2, ( + f"Max sensor should have 2 values, got {len(sensor_states['max_sensor'])}: {sensor_states['max_sensor']}" + ) + assert len(sensor_states["median_sensor"]) == 2 + assert len(sensor_states["quantile_sensor"]) == 2 + assert len(sensor_states["moving_avg_sensor"]) == 2 + + # Verify the first output (after 1 value: [1]) + assert sensor_states["min_sensor"][0] == pytest.approx(1.0), ( + f"First min should be 1.0, got {sensor_states['min_sensor'][0]}" + ) + assert sensor_states["max_sensor"][0] == pytest.approx(1.0), ( + f"First max should be 1.0, got {sensor_states['max_sensor'][0]}" + ) + assert sensor_states["median_sensor"][0] == pytest.approx(1.0), ( + f"First median should be 1.0, got {sensor_states['median_sensor'][0]}" + ) + assert sensor_states["moving_avg_sensor"][0] == pytest.approx(1.0), ( + f"First moving avg should be 1.0, got {sensor_states['moving_avg_sensor'][0]}" + ) + + # Verify the second output (after 6 values, window has [2, 3, 4, 5, 6]) + assert sensor_states["min_sensor"][1] == pytest.approx(2.0), ( + f"Second min should be 2.0, got {sensor_states['min_sensor'][1]}" + ) + assert sensor_states["max_sensor"][1] == pytest.approx(6.0), ( + f"Second max should be 6.0, got {sensor_states['max_sensor'][1]}" + ) + assert sensor_states["median_sensor"][1] == pytest.approx(4.0), ( + f"Second median should be 4.0, got {sensor_states['median_sensor'][1]}" + ) + assert sensor_states["moving_avg_sensor"][1] == pytest.approx(4.0), ( + f"Second moving avg should be 4.0, got {sensor_states['moving_avg_sensor'][1]}" + ) + + +@pytest.mark.asyncio +async def test_sensor_filters_nan_handling( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that sliding window filters handle NaN values correctly.""" + loop = asyncio.get_running_loop() + + # Track states + min_states: list[float] = [] + max_states: list[float] = [] + + # Future to track completion + filters_completed = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track sensor state updates.""" + if not isinstance(state, SensorState): + return + + # Skip NaN values + if state.missing_state: + return + + sensor_name = key_to_sensor.get(state.key) + + if sensor_name == "min_nan": + min_states.append(state.state) + elif sensor_name == "max_nan": + max_states.append(state.state) + + # Check if both have received their final values + # With batch_delay: 0ms, we should receive 2 outputs each + if ( + len(min_states) >= 2 + and len(max_states) >= 2 + and not filters_completed.done() + ): + filters_completed.set_result(True) + + async with ( + run_compiled(yaml_config), + api_client_connected() as client, + ): + # Get entities first to build key mapping + entities, services = await client.list_entities_services() + + # Build key-to-sensor mapping + key_to_sensor = build_key_to_entity_mapping(entities, ["min_nan", "max_nan"]) + + # Set up initial state helper with all entities + initial_state_helper = InitialStateHelper(entities) + + # Subscribe to state changes with wrapper + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + + # Wait for initial states + try: + await initial_state_helper.wait_for_initial_states() + except TimeoutError: + pytest.fail("Timeout waiting for initial states") + + # Find the publish button + publish_button = next( + (e for e in entities if "publish_nan_values_button" in e.object_id.lower()), + None, + ) + assert publish_button is not None, "Publish NaN Values Button not found" + + # Press the button + client.button_command(publish_button.key) + + # Wait for filters to process + try: + await asyncio.wait_for(filters_completed, timeout=10.0) + except TimeoutError: + pytest.fail( + f"Timeout waiting for NaN handling. Received:\n" + f" min_states: {min_states}\n" + f" max_states: {max_states}" + ) + + # Verify NaN values were ignored + # With batch_delay: 0ms, we should receive both outputs (at positions 1 and 6) + # Position 1: window=[10], min=10, max=10 + # Position 6: window=[NaN, 5, NaN, 15, 8], ignoring NaN -> [5, 15, 8], min=5, max=15 + assert len(min_states) == 2, ( + f"Should have 2 min states, got {len(min_states)}: {min_states}" + ) + assert len(max_states) == 2, ( + f"Should have 2 max states, got {len(max_states)}: {max_states}" + ) + + # First output + assert min_states[0] == pytest.approx(10.0), ( + f"First min should be 10.0, got {min_states[0]}" + ) + assert max_states[0] == pytest.approx(10.0), ( + f"First max should be 10.0, got {max_states[0]}" + ) + + # Second output - verify NaN values were ignored + assert min_states[1] == pytest.approx(5.0), ( + f"Second min should ignore NaN and return 5.0, got {min_states[1]}" + ) + assert max_states[1] == pytest.approx(15.0), ( + f"Second max should ignore NaN and return 15.0, got {max_states[1]}" + ) + + +@pytest.mark.asyncio +async def test_sensor_filters_ring_buffer_wraparound( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that ring buffer correctly wraps around when window fills up.""" + loop = asyncio.get_running_loop() + + min_states: list[float] = [] + + test_completed = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track min sensor states.""" + if not isinstance(state, SensorState): + return + + # Skip NaN values + if state.missing_state: + return + + sensor_name = key_to_sensor.get(state.key) + + if sensor_name == "wraparound_min": + min_states.append(state.state) + # With batch_delay: 0ms, we should receive all 3 outputs + if len(min_states) >= 3 and not test_completed.done(): + test_completed.set_result(True) + + async with ( + run_compiled(yaml_config), + api_client_connected() as client, + ): + # Get entities first to build key mapping + entities, services = await client.list_entities_services() + + # Build key-to-sensor mapping + key_to_sensor = build_key_to_entity_mapping(entities, ["wraparound_min"]) + + # Set up initial state helper with all entities + initial_state_helper = InitialStateHelper(entities) + + # Subscribe to state changes with wrapper + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + + # Wait for initial state + try: + await initial_state_helper.wait_for_initial_states() + except TimeoutError: + pytest.fail("Timeout waiting for initial state") + + # Find the publish button + publish_button = next( + (e for e in entities if "publish_wraparound_button" in e.object_id.lower()), + None, + ) + assert publish_button is not None, "Publish Wraparound Button not found" + + # Press the button + # Will publish: 10, 20, 30, 5, 25, 15, 40, 35, 20 + client.button_command(publish_button.key) + + # Wait for completion + try: + await asyncio.wait_for(test_completed, timeout=10.0) + except TimeoutError: + pytest.fail(f"Timeout waiting for wraparound test. Received: {min_states}") + + # Verify outputs + # With window_size=3, send_every=3, we get outputs at positions 1, 4, 7 + # Position 1: window=[10], min=10 + # Position 4: window=[20, 30, 5], min=5 + # Position 7: window=[15, 40, 35], min=15 + # With batch_delay: 0ms, we should receive all 3 outputs + assert len(min_states) == 3, ( + f"Should have 3 states, got {len(min_states)}: {min_states}" + ) + assert min_states[0] == pytest.approx(10.0), ( + f"First min should be 10.0, got {min_states[0]}" + ) + assert min_states[1] == pytest.approx(5.0), ( + f"Second min should be 5.0, got {min_states[1]}" + ) + assert min_states[2] == pytest.approx(15.0), ( + f"Third min should be 15.0, got {min_states[2]}" + ) From e9933126400193e9e3cb9ea950044c7e3058b9d3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 09:15:47 -1000 Subject: [PATCH 147/526] [core] Fix IndexError when OTA devices cannot be resolved (#11311) --- esphome/__main__.py | 4 +- tests/unit_tests/test_main.py | 78 +++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index a0b7d16ae9..982e00f5e1 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -185,7 +185,9 @@ def choose_upload_log_host( else: resolved.append(device) if not resolved: - _LOGGER.error("All specified devices: %s could not be resolved.", defaults) + raise EsphomeError( + f"All specified devices {defaults} could not be resolved. Is the device connected to the network?" + ) return resolved # No devices specified, show interactive chooser diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index 59d0433aa4..73dfe359f0 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -321,12 +321,14 @@ def test_choose_upload_log_host_with_serial_device_no_ports( ) -> None: """Test SERIAL device when no serial ports are found.""" setup_core() - result = choose_upload_log_host( - default="SERIAL", - check_default=None, - purpose=Purpose.UPLOADING, - ) - assert result == [] + with pytest.raises( + EsphomeError, match="All specified devices .* could not be resolved" + ): + choose_upload_log_host( + default="SERIAL", + check_default=None, + purpose=Purpose.UPLOADING, + ) assert "No serial ports found, skipping SERIAL device" in caplog.text @@ -367,12 +369,14 @@ def test_choose_upload_log_host_with_ota_device_with_api_config() -> None: """Test OTA device when API is configured (no upload without OTA in config).""" setup_core(config={CONF_API: {}}, address="192.168.1.100") - result = choose_upload_log_host( - default="OTA", - check_default=None, - purpose=Purpose.UPLOADING, - ) - assert result == [] + with pytest.raises( + EsphomeError, match="All specified devices .* could not be resolved" + ): + choose_upload_log_host( + default="OTA", + check_default=None, + purpose=Purpose.UPLOADING, + ) def test_choose_upload_log_host_with_ota_device_with_api_config_logging() -> None: @@ -405,12 +409,14 @@ def test_choose_upload_log_host_with_ota_device_no_fallback() -> None: """Test OTA device with no valid fallback options.""" setup_core() - result = choose_upload_log_host( - default="OTA", - check_default=None, - purpose=Purpose.UPLOADING, - ) - assert result == [] + with pytest.raises( + EsphomeError, match="All specified devices .* could not be resolved" + ): + choose_upload_log_host( + default="OTA", + check_default=None, + purpose=Purpose.UPLOADING, + ) @pytest.mark.usefixtures("mock_choose_prompt") @@ -615,21 +621,19 @@ def test_choose_upload_log_host_empty_defaults_list() -> None: @pytest.mark.usefixtures("mock_no_serial_ports", "mock_no_mqtt_logging") -def test_choose_upload_log_host_all_devices_unresolved( - caplog: pytest.LogCaptureFixture, -) -> None: +def test_choose_upload_log_host_all_devices_unresolved() -> None: """Test when all specified devices cannot be resolved.""" setup_core() - result = choose_upload_log_host( - default=["SERIAL", "OTA"], - check_default=None, - purpose=Purpose.UPLOADING, - ) - assert result == [] - assert ( - "All specified devices: ['SERIAL', 'OTA'] could not be resolved." in caplog.text - ) + with pytest.raises( + EsphomeError, + match=r"All specified devices \['SERIAL', 'OTA'\] could not be resolved", + ): + choose_upload_log_host( + default=["SERIAL", "OTA"], + check_default=None, + purpose=Purpose.UPLOADING, + ) @pytest.mark.usefixtures("mock_no_serial_ports", "mock_no_mqtt_logging") @@ -762,12 +766,14 @@ def test_choose_upload_log_host_no_address_with_ota_config() -> None: """Test OTA device when OTA is configured but no address is set.""" setup_core(config={CONF_OTA: {}}) - result = choose_upload_log_host( - default="OTA", - check_default=None, - purpose=Purpose.UPLOADING, - ) - assert result == [] + with pytest.raises( + EsphomeError, match="All specified devices .* could not be resolved" + ): + choose_upload_log_host( + default="OTA", + check_default=None, + purpose=Purpose.UPLOADING, + ) @dataclass From 25f3b6a959992c4664bde1603100d455359da88c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 09:17:33 -1000 Subject: [PATCH 148/526] [mqtt] Reduce flash usage by optimizing ArduinoJson assignments (#11340) --- esphome/components/mqtt/mqtt_client.cpp | 7 +-- esphome/components/mqtt/mqtt_component.cpp | 52 ++++++++-------------- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 16f54ab8a0..9055b4421e 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -140,11 +140,8 @@ void MQTTClientComponent::send_device_info_() { #endif #ifdef USE_API_NOISE - if (api::global_api_server->get_noise_ctx()->has_psk()) { - root["api_encryption"] = "Noise_NNpsk0_25519_ChaChaPoly_SHA256"; - } else { - root["api_encryption_supported"] = "Noise_NNpsk0_25519_ChaChaPoly_SHA256"; - } + root[api::global_api_server->get_noise_ctx()->has_psk() ? "api_encryption" : "api_encryption_supported"] = + "Noise_NNpsk0_25519_ChaChaPoly_SHA256"; #endif }, 2, this->discovery_info_.retain); diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 6ceaf219ff..eb6114008a 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -85,24 +85,20 @@ bool MQTTComponent::send_discovery_() { } // Fields from EntityBase - if (this->get_entity()->has_own_name()) { - root[MQTT_NAME] = this->friendly_name(); - } else { - root[MQTT_NAME] = ""; - } + root[MQTT_NAME] = this->get_entity()->has_own_name() ? this->friendly_name() : ""; + if (this->is_disabled_by_default()) root[MQTT_ENABLED_BY_DEFAULT] = false; if (!this->get_icon().empty()) root[MQTT_ICON] = this->get_icon(); - switch (this->get_entity()->get_entity_category()) { + const auto entity_category = this->get_entity()->get_entity_category(); + switch (entity_category) { case ENTITY_CATEGORY_NONE: break; case ENTITY_CATEGORY_CONFIG: - root[MQTT_ENTITY_CATEGORY] = "config"; - break; case ENTITY_CATEGORY_DIAGNOSTIC: - root[MQTT_ENTITY_CATEGORY] = "diagnostic"; + root[MQTT_ENTITY_CATEGORY] = entity_category == ENTITY_CATEGORY_CONFIG ? "config" : "diagnostic"; break; } @@ -113,20 +109,14 @@ bool MQTTComponent::send_discovery_() { if (this->command_retain_) root[MQTT_COMMAND_RETAIN] = true; - if (this->availability_ == nullptr) { - if (!global_mqtt_client->get_availability().topic.empty()) { - root[MQTT_AVAILABILITY_TOPIC] = global_mqtt_client->get_availability().topic; - if (global_mqtt_client->get_availability().payload_available != "online") - root[MQTT_PAYLOAD_AVAILABLE] = global_mqtt_client->get_availability().payload_available; - if (global_mqtt_client->get_availability().payload_not_available != "offline") - root[MQTT_PAYLOAD_NOT_AVAILABLE] = global_mqtt_client->get_availability().payload_not_available; - } - } else if (!this->availability_->topic.empty()) { - root[MQTT_AVAILABILITY_TOPIC] = this->availability_->topic; - if (this->availability_->payload_available != "online") - root[MQTT_PAYLOAD_AVAILABLE] = this->availability_->payload_available; - if (this->availability_->payload_not_available != "offline") - root[MQTT_PAYLOAD_NOT_AVAILABLE] = this->availability_->payload_not_available; + const Availability &avail = + this->availability_ == nullptr ? global_mqtt_client->get_availability() : *this->availability_; + if (!avail.topic.empty()) { + root[MQTT_AVAILABILITY_TOPIC] = avail.topic; + if (avail.payload_available != "online") + root[MQTT_PAYLOAD_AVAILABLE] = avail.payload_available; + if (avail.payload_not_available != "offline") + root[MQTT_PAYLOAD_NOT_AVAILABLE] = avail.payload_not_available; } const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info(); @@ -145,10 +135,8 @@ bool MQTTComponent::send_discovery_() { if (discovery_info.object_id_generator == MQTT_DEVICE_NAME_OBJECT_ID_GENERATOR) root[MQTT_OBJECT_ID] = node_name + "_" + this->get_default_object_id_(); - std::string node_friendly_name = App.get_friendly_name(); - if (node_friendly_name.empty()) { - node_friendly_name = node_name; - } + const std::string &friendly_name_ref = App.get_friendly_name(); + const std::string &node_friendly_name = friendly_name_ref.empty() ? node_name : friendly_name_ref; std::string node_area = App.get_area(); JsonObject device_info = root[MQTT_DEVICE].to(); @@ -158,13 +146,9 @@ bool MQTTComponent::send_discovery_() { #ifdef ESPHOME_PROJECT_NAME device_info[MQTT_DEVICE_SW_VERSION] = ESPHOME_PROJECT_VERSION " (ESPHome " ESPHOME_VERSION ")"; const char *model = std::strchr(ESPHOME_PROJECT_NAME, '.'); - if (model == nullptr) { // must never happen but check anyway - device_info[MQTT_DEVICE_MODEL] = ESPHOME_BOARD; - device_info[MQTT_DEVICE_MANUFACTURER] = ESPHOME_PROJECT_NAME; - } else { - device_info[MQTT_DEVICE_MODEL] = model + 1; - device_info[MQTT_DEVICE_MANUFACTURER] = std::string(ESPHOME_PROJECT_NAME, model - ESPHOME_PROJECT_NAME); - } + device_info[MQTT_DEVICE_MODEL] = model == nullptr ? ESPHOME_BOARD : model + 1; + device_info[MQTT_DEVICE_MANUFACTURER] = + model == nullptr ? ESPHOME_PROJECT_NAME : std::string(ESPHOME_PROJECT_NAME, model - ESPHOME_PROJECT_NAME); #else device_info[MQTT_DEVICE_SW_VERSION] = ESPHOME_VERSION " (" + App.get_compilation_time() + ")"; device_info[MQTT_DEVICE_MODEL] = ESPHOME_BOARD; From 33fea90c19fa3e274e4253fa23bcd24ceb8c4cd3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 09:26:18 -1000 Subject: [PATCH 149/526] [wifi] Optimize WiFi scanning to reduce copies and heap allocations (#11323) --- esphome/components/wifi/wifi_component.cpp | 40 ++++++++++++---------- esphome/components/wifi/wifi_component.h | 2 +- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 5aa2a03a14..612b11a50f 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -607,10 +607,12 @@ void WiFiComponent::check_scanning_finished() { for (auto &ap : this->sta_) { if (res.matches(ap)) { res.set_matches(true); - if (!this->has_sta_priority(res.get_bssid())) { - this->set_sta_priority(res.get_bssid(), ap.get_priority()); + // Cache priority lookup - do single search instead of 2 separate searches + const bssid_t &bssid = res.get_bssid(); + if (!this->has_sta_priority(bssid)) { + this->set_sta_priority(bssid, ap.get_priority()); } - res.set_priority(this->get_sta_priority(res.get_bssid())); + res.set_priority(this->get_sta_priority(bssid)); break; } } @@ -629,8 +631,9 @@ void WiFiComponent::check_scanning_finished() { return; } - WiFiAP connect_params; - WiFiScanResult scan_res = this->scan_result_[0]; + // Build connection params directly into selected_ap_ to avoid extra copy + const WiFiScanResult &scan_res = this->scan_result_[0]; + WiFiAP &selected = this->selected_ap_; for (auto &config : this->sta_) { // search for matching STA config, at least one will match (from checks before) if (!scan_res.matches(config)) { @@ -639,37 +642,38 @@ void WiFiComponent::check_scanning_finished() { if (config.get_hidden()) { // selected network is hidden, we use the data from the config - connect_params.set_hidden(true); - connect_params.set_ssid(config.get_ssid()); - // don't set BSSID and channel, there might be multiple hidden networks + selected.set_hidden(true); + selected.set_ssid(config.get_ssid()); + // Clear channel and BSSID for hidden networks - there might be multiple hidden networks // but we can't know which one is the correct one. Rely on probe-req with just SSID. + selected.set_channel(0); + selected.set_bssid(optional{}); } else { // selected network is visible, we use the data from the scan // limit the connect params to only connect to exactly this network // (network selection is done during scan phase). - connect_params.set_hidden(false); - connect_params.set_ssid(scan_res.get_ssid()); - connect_params.set_channel(scan_res.get_channel()); - connect_params.set_bssid(scan_res.get_bssid()); + selected.set_hidden(false); + selected.set_ssid(scan_res.get_ssid()); + selected.set_channel(scan_res.get_channel()); + selected.set_bssid(scan_res.get_bssid()); } // copy manual IP (if set) - connect_params.set_manual_ip(config.get_manual_ip()); + selected.set_manual_ip(config.get_manual_ip()); #ifdef USE_WIFI_WPA2_EAP // copy EAP parameters (if set) - connect_params.set_eap(config.get_eap()); + selected.set_eap(config.get_eap()); #endif // copy password (if set) - connect_params.set_password(config.get_password()); + selected.set_password(config.get_password()); break; } yield(); - this->selected_ap_ = connect_params; - this->start_connecting(connect_params, false); + this->start_connecting(this->selected_ap_, false); } void WiFiComponent::dump_config() { @@ -902,7 +906,7 @@ WiFiScanResult::WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t c rssi_(rssi), with_auth_(with_auth), is_hidden_(is_hidden) {} -bool WiFiScanResult::matches(const WiFiAP &config) { +bool WiFiScanResult::matches(const WiFiAP &config) const { if (config.get_hidden()) { // User configured a hidden network, only match actually hidden networks // don't match SSID diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 9d32071b2b..508024a235 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -170,7 +170,7 @@ class WiFiScanResult { public: WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth, bool is_hidden); - bool matches(const WiFiAP &config); + bool matches(const WiFiAP &config) const; bool get_matches() const; void set_matches(bool matches); From c9312d5c2799a213d338981527907d475bb7844a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 09:42:17 -1000 Subject: [PATCH 150/526] [script] Fix unbounded queue growth, optimize queued mode (default max_runs=5) (#11308) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- esphome/components/script/__init__.py | 17 +- esphome/components/script/script.h | 72 +++++-- tests/integration/fixtures/script_queued.yaml | 170 +++++++++++++++ tests/integration/test_script_queued.py | 203 ++++++++++++++++++ 4 files changed, 442 insertions(+), 20 deletions(-) create mode 100644 tests/integration/fixtures/script_queued.yaml create mode 100644 tests/integration/test_script_queued.py diff --git a/esphome/components/script/__init__.py b/esphome/components/script/__init__.py index e8a8aa5671..8d69981db0 100644 --- a/esphome/components/script/__init__.py +++ b/esphome/components/script/__init__.py @@ -45,13 +45,26 @@ def get_script(script_id): def check_max_runs(value): + # Set default for queued mode to prevent unbounded queue growth + if CONF_MAX_RUNS not in value and value[CONF_MODE] == CONF_QUEUED: + value[CONF_MAX_RUNS] = 5 + if CONF_MAX_RUNS not in value: return value + if value[CONF_MODE] not in [CONF_QUEUED, CONF_PARALLEL]: raise cv.Invalid( - "The option 'max_runs' is only valid in 'queue' and 'parallel' mode.", + "The option 'max_runs' is only valid in 'queued' and 'parallel' mode.", path=[CONF_MAX_RUNS], ) + + # Queued mode must have bounded queue (min 1), parallel mode can be unlimited (0) + if value[CONF_MODE] == CONF_QUEUED and value[CONF_MAX_RUNS] < 1: + raise cv.Invalid( + "The option 'max_runs' must be at least 1 for queued mode.", + path=[CONF_MAX_RUNS], + ) + return value @@ -106,7 +119,7 @@ CONFIG_SCHEMA = automation.validate_automation( cv.Optional(CONF_MODE, default=CONF_SINGLE): cv.one_of( *SCRIPT_MODES, lower=True ), - cv.Optional(CONF_MAX_RUNS): cv.positive_int, + cv.Optional(CONF_MAX_RUNS): cv.int_range(min=0, max=100), cv.Optional(CONF_PARAMETERS, default={}): cv.Schema( { validate_parameter_name: validate_parameter_type, diff --git a/esphome/components/script/script.h b/esphome/components/script/script.h index b87402f52e..58fb67a3ea 100644 --- a/esphome/components/script/script.h +++ b/esphome/components/script/script.h @@ -1,10 +1,11 @@ #pragma once +#include +#include #include "esphome/core/automation.h" #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" - -#include namespace esphome { namespace script { @@ -96,23 +97,41 @@ template class RestartScript : public Script { /** A script type that queues new instances that are created. * * Only one instance of the script can be active at a time. + * + * Ring buffer implementation: + * - num_queued_ tracks the number of queued (waiting) instances, NOT including the currently running one + * - queue_front_ points to the next item to execute (read position) + * - Buffer size is max_runs_ - 1 (max total instances minus the running one) + * - Write position is calculated as: (queue_front_ + num_queued_) % (max_runs_ - 1) + * - When an item finishes, queue_front_ advances: (queue_front_ + 1) % (max_runs_ - 1) + * - First execute() runs immediately without queuing (num_queued_ stays 0) + * - Subsequent executes while running are queued starting at position 0 + * - Maximum total instances = max_runs_ (includes 1 running + (max_runs_ - 1) queued) */ template class QueueingScript : public Script, public Component { public: void execute(Ts... x) override { - if (this->is_action_running() || this->num_runs_ > 0) { - // num_runs_ is the number of *queued* instances, so total number of instances is - // num_runs_ + 1 - if (this->max_runs_ != 0 && this->num_runs_ + 1 >= this->max_runs_) { - this->esp_logw_(__LINE__, ESPHOME_LOG_FORMAT("Script '%s' maximum number of queued runs exceeded!"), + if (this->is_action_running() || this->num_queued_ > 0) { + // num_queued_ is the number of *queued* instances (waiting, not including currently running) + // max_runs_ is the maximum *total* instances (running + queued) + // So we reject when num_queued_ + 1 >= max_runs_ (queued + running >= max) + if (this->num_queued_ + 1 >= this->max_runs_) { + this->esp_logw_(__LINE__, ESPHOME_LOG_FORMAT("Script '%s' max instances (running + queued) reached!"), LOG_STR_ARG(this->name_)); return; } + // Initialize queue on first queued item (after capacity check) + this->lazy_init_queue_(); + this->esp_logd_(__LINE__, ESPHOME_LOG_FORMAT("Script '%s' queueing new instance (mode: queued)"), LOG_STR_ARG(this->name_)); - this->num_runs_++; - this->var_queue_.push(std::make_tuple(x...)); + // Ring buffer: write to (queue_front_ + num_queued_) % queue_capacity + const size_t queue_capacity = static_cast(this->max_runs_ - 1); + size_t write_pos = (this->queue_front_ + this->num_queued_) % queue_capacity; + // Use std::make_unique to replace the unique_ptr + this->var_queue_[write_pos] = std::make_unique>(x...); + this->num_queued_++; return; } @@ -122,29 +141,46 @@ template class QueueingScript : public Script, public Com } void stop() override { - this->num_runs_ = 0; + // Clear all queued items to free memory immediately + // Resetting the array automatically destroys all unique_ptrs and their contents + this->var_queue_.reset(); + this->num_queued_ = 0; + this->queue_front_ = 0; Script::stop(); } void loop() override { - if (this->num_runs_ != 0 && !this->is_action_running()) { - this->num_runs_--; - auto &vars = this->var_queue_.front(); - this->var_queue_.pop(); - this->trigger_tuple_(vars, typename gens::type()); + if (this->num_queued_ != 0 && !this->is_action_running()) { + // Dequeue: decrement count, move tuple out (frees slot), advance read position + this->num_queued_--; + const size_t queue_capacity = static_cast(this->max_runs_ - 1); + auto tuple_ptr = std::move(this->var_queue_[this->queue_front_]); + this->queue_front_ = (this->queue_front_ + 1) % queue_capacity; + this->trigger_tuple_(*tuple_ptr, typename gens::type()); } } void set_max_runs(int max_runs) { max_runs_ = max_runs; } protected: + // Lazy init queue on first use - avoids setup() ordering issues and saves memory + // if script is never executed during this boot cycle + inline void lazy_init_queue_() { + if (!this->var_queue_) { + // Allocate array of max_runs_ - 1 slots for queued items (running item is separate) + // unique_ptr array is zero-initialized, so all slots start as nullptr + this->var_queue_ = std::make_unique>[]>(this->max_runs_ - 1); + } + } + template void trigger_tuple_(const std::tuple &tuple, seq /*unused*/) { this->trigger(std::get(tuple)...); } - int num_runs_ = 0; - int max_runs_ = 0; - std::queue> var_queue_; + int num_queued_ = 0; // Number of queued instances (not including currently running) + int max_runs_ = 0; // Maximum total instances (running + queued) + size_t queue_front_ = 0; // Ring buffer read position (next item to execute) + std::unique_ptr>[]> var_queue_; // Ring buffer of queued parameters }; /** A script type that executes new instances in parallel. diff --git a/tests/integration/fixtures/script_queued.yaml b/tests/integration/fixtures/script_queued.yaml new file mode 100644 index 0000000000..996dd6436f --- /dev/null +++ b/tests/integration/fixtures/script_queued.yaml @@ -0,0 +1,170 @@ +esphome: + name: test-script-queued + +host: +api: + actions: + # Test 1: Queue depth with default max_runs=5 + - action: test_queue_depth + then: + - logger.log: "=== TEST 1: Queue depth (max_runs=5 means 5 total, reject 6-7) ===" + - script.execute: + id: queue_depth_script + value: 1 + - script.execute: + id: queue_depth_script + value: 2 + - script.execute: + id: queue_depth_script + value: 3 + - script.execute: + id: queue_depth_script + value: 4 + - script.execute: + id: queue_depth_script + value: 5 + - script.execute: + id: queue_depth_script + value: 6 + - script.execute: + id: queue_depth_script + value: 7 + + # Test 2: Ring buffer wrap test + - action: test_ring_buffer + then: + - logger.log: "=== TEST 2: Ring buffer wrap (should process A, B, C in order) ===" + - script.execute: + id: wrap_script + msg: "A" + - script.execute: + id: wrap_script + msg: "B" + - script.execute: + id: wrap_script + msg: "C" + + # Test 3: Stop clears queue + - action: test_stop_clears + then: + - logger.log: "=== TEST 3: Stop clears queue (should only see 1, then 'STOPPED') ===" + - script.execute: + id: stop_script + num: 1 + - script.execute: + id: stop_script + num: 2 + - script.execute: + id: stop_script + num: 3 + - delay: 50ms + - logger.log: "STOPPING script now" + - script.stop: stop_script + + # Test 4: Verify rejection (max_runs=3) + - action: test_rejection + then: + - logger.log: "=== TEST 4: Verify rejection (max_runs=3 means 3 total, reject 4-8) ===" + - script.execute: + id: rejection_script + val: 1 + - script.execute: + id: rejection_script + val: 2 + - script.execute: + id: rejection_script + val: 3 + - script.execute: + id: rejection_script + val: 4 + - script.execute: + id: rejection_script + val: 5 + - script.execute: + id: rejection_script + val: 6 + - script.execute: + id: rejection_script + val: 7 + - script.execute: + id: rejection_script + val: 8 + + # Test 5: No parameters test + - action: test_no_params + then: + - logger.log: "=== TEST 5: No params (should process 3 times) ===" + - script.execute: no_params_script + - script.execute: no_params_script + - script.execute: no_params_script + +logger: + level: DEBUG + +script: + # Test script 1: Queue depth test (default max_runs=5) + - id: queue_depth_script + mode: queued + parameters: + value: int + then: + - logger.log: + format: "Queue test: START item %d" + args: ['value'] + - delay: 100ms + - logger.log: + format: "Queue test: END item %d" + args: ['value'] + + # Test script 2: Ring buffer wrap test (max_runs=3) + - id: wrap_script + mode: queued + max_runs: 3 + parameters: + msg: string + then: + - logger.log: + format: "Ring buffer: START '%s'" + args: ['msg.c_str()'] + - delay: 50ms + - logger.log: + format: "Ring buffer: END '%s'" + args: ['msg.c_str()'] + + # Test script 3: Stop test + - id: stop_script + mode: queued + max_runs: 5 + parameters: + num: int + then: + - logger.log: + format: "Stop test: START %d" + args: ['num'] + - delay: 100ms + - logger.log: + format: "Stop test: END %d" + args: ['num'] + + # Test script 4: Rejection test (max_runs=3) + - id: rejection_script + mode: queued + max_runs: 3 + parameters: + val: int + then: + - logger.log: + format: "Rejection test: START %d" + args: ['val'] + - delay: 200ms + - logger.log: + format: "Rejection test: END %d" + args: ['val'] + + # Test script 5: No parameters + - id: no_params_script + mode: queued + then: + - logger.log: "No params: START" + - delay: 50ms + - logger.log: "No params: END" diff --git a/tests/integration/test_script_queued.py b/tests/integration/test_script_queued.py new file mode 100644 index 0000000000..ce1c25b649 --- /dev/null +++ b/tests/integration/test_script_queued.py @@ -0,0 +1,203 @@ +"""Test ESPHome queued script functionality.""" + +from __future__ import annotations + +import asyncio +import re + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_script_queued( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test comprehensive queued script functionality.""" + loop = asyncio.get_running_loop() + + # Track all test results + test_results = { + "queue_depth": {"processed": [], "rejections": 0}, + "ring_buffer": {"start_order": [], "end_order": []}, + "stop": {"processed": [], "stop_logged": False}, + "rejection": {"processed": [], "rejections": 0}, + "no_params": {"executions": 0}, + } + + # Patterns for Test 1: Queue depth + queue_start = re.compile(r"Queue test: START item (\d+)") + queue_end = re.compile(r"Queue test: END item (\d+)") + queue_reject = re.compile(r"Script 'queue_depth_script' max instances") + + # Patterns for Test 2: Ring buffer + ring_start = re.compile(r"Ring buffer: START '([A-Z])'") + ring_end = re.compile(r"Ring buffer: END '([A-Z])'") + + # Patterns for Test 3: Stop + stop_start = re.compile(r"Stop test: START (\d+)") + stop_log = re.compile(r"STOPPING script now") + + # Patterns for Test 4: Rejection + reject_start = re.compile(r"Rejection test: START (\d+)") + reject_end = re.compile(r"Rejection test: END (\d+)") + reject_reject = re.compile(r"Script 'rejection_script' max instances") + + # Patterns for Test 5: No params + no_params_end = re.compile(r"No params: END") + + # Test completion futures + test1_complete = loop.create_future() + test2_complete = loop.create_future() + test3_complete = loop.create_future() + test4_complete = loop.create_future() + test5_complete = loop.create_future() + + def check_output(line: str) -> None: + """Check log output for all test messages.""" + # Test 1: Queue depth + if match := queue_start.search(line): + item = int(match.group(1)) + if item not in test_results["queue_depth"]["processed"]: + test_results["queue_depth"]["processed"].append(item) + + if match := queue_end.search(line): + item = int(match.group(1)) + if item == 5 and not test1_complete.done(): + test1_complete.set_result(True) + + if queue_reject.search(line): + test_results["queue_depth"]["rejections"] += 1 + + # Test 2: Ring buffer + if match := ring_start.search(line): + msg = match.group(1) + test_results["ring_buffer"]["start_order"].append(msg) + + if match := ring_end.search(line): + msg = match.group(1) + test_results["ring_buffer"]["end_order"].append(msg) + if ( + len(test_results["ring_buffer"]["end_order"]) == 3 + and not test2_complete.done() + ): + test2_complete.set_result(True) + + # Test 3: Stop + if match := stop_start.search(line): + item = int(match.group(1)) + if item not in test_results["stop"]["processed"]: + test_results["stop"]["processed"].append(item) + + if stop_log.search(line): + test_results["stop"]["stop_logged"] = True + # Give time for any queued items to be cleared + if not test3_complete.done(): + loop.call_later( + 0.3, + lambda: test3_complete.set_result(True) + if not test3_complete.done() + else None, + ) + + # Test 4: Rejection + if match := reject_start.search(line): + item = int(match.group(1)) + if item not in test_results["rejection"]["processed"]: + test_results["rejection"]["processed"].append(item) + + if match := reject_end.search(line): + item = int(match.group(1)) + if item == 3 and not test4_complete.done(): + test4_complete.set_result(True) + + if reject_reject.search(line): + test_results["rejection"]["rejections"] += 1 + + # Test 5: No params + if no_params_end.search(line): + test_results["no_params"]["executions"] += 1 + if ( + test_results["no_params"]["executions"] == 3 + and not test5_complete.done() + ): + test5_complete.set_result(True) + + async with ( + run_compiled(yaml_config, line_callback=check_output), + api_client_connected() as client, + ): + # Get services + _, services = await client.list_entities_services() + + # Test 1: Queue depth limit + test_service = next((s for s in services if s.name == "test_queue_depth"), None) + assert test_service is not None, "test_queue_depth service not found" + client.execute_service(test_service, {}) + await asyncio.wait_for(test1_complete, timeout=2.0) + await asyncio.sleep(0.1) # Give time for rejections + + # Verify Test 1 + assert sorted(test_results["queue_depth"]["processed"]) == [1, 2, 3, 4, 5], ( + f"Test 1: Expected to process items 1-5 (max_runs=5 means 5 total), got {sorted(test_results['queue_depth']['processed'])}" + ) + assert test_results["queue_depth"]["rejections"] >= 2, ( + "Test 1: Expected at least 2 rejection warnings (items 6-7 should be rejected)" + ) + + # Test 2: Ring buffer order + test_service = next((s for s in services if s.name == "test_ring_buffer"), None) + assert test_service is not None, "test_ring_buffer service not found" + client.execute_service(test_service, {}) + await asyncio.wait_for(test2_complete, timeout=2.0) + + # Verify Test 2 + assert test_results["ring_buffer"]["start_order"] == ["A", "B", "C"], ( + f"Test 2: Expected start order [A, B, C], got {test_results['ring_buffer']['start_order']}" + ) + assert test_results["ring_buffer"]["end_order"] == ["A", "B", "C"], ( + f"Test 2: Expected end order [A, B, C], got {test_results['ring_buffer']['end_order']}" + ) + + # Test 3: Stop clears queue + test_service = next((s for s in services if s.name == "test_stop_clears"), None) + assert test_service is not None, "test_stop_clears service not found" + client.execute_service(test_service, {}) + await asyncio.wait_for(test3_complete, timeout=2.0) + + # Verify Test 3 + assert test_results["stop"]["stop_logged"], ( + "Test 3: Stop command was not logged" + ) + assert test_results["stop"]["processed"] == [1], ( + f"Test 3: Expected only item 1 to process, got {test_results['stop']['processed']}" + ) + + # Test 4: Rejection enforcement (max_runs=3) + test_service = next((s for s in services if s.name == "test_rejection"), None) + assert test_service is not None, "test_rejection service not found" + client.execute_service(test_service, {}) + await asyncio.wait_for(test4_complete, timeout=2.0) + await asyncio.sleep(0.1) # Give time for rejections + + # Verify Test 4 + assert sorted(test_results["rejection"]["processed"]) == [1, 2, 3], ( + f"Test 4: Expected to process items 1-3 (max_runs=3 means 3 total), got {sorted(test_results['rejection']['processed'])}" + ) + assert test_results["rejection"]["rejections"] == 5, ( + f"Test 4: Expected 5 rejections (items 4-8), got {test_results['rejection']['rejections']}" + ) + + # Test 5: No parameters + test_service = next((s for s in services if s.name == "test_no_params"), None) + assert test_service is not None, "test_no_params service not found" + client.execute_service(test_service, {}) + await asyncio.wait_for(test5_complete, timeout=2.0) + + # Verify Test 5 + assert test_results["no_params"]["executions"] == 3, ( + f"Test 5: Expected 3 executions, got {test_results['no_params']['executions']}" + ) From b0ea3f57de61f7761f1bdca274b631be4d369500 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Sun, 19 Oct 2025 15:49:05 -0400 Subject: [PATCH 151/526] [esp32] Fix OTA rollback (#11300) Co-authored-by: J. Nick Koston --- esphome/components/esp32/core.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp index f3bdfea2a0..3427c96e70 100644 --- a/esphome/components/esp32/core.cpp +++ b/esphome/components/esp32/core.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,16 @@ void arch_init() { disableCore1WDT(); #endif #endif + + // If the bootloader was compiled with CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE the current + // partition will get rolled back unless it is marked as valid. + esp_ota_img_states_t state; + const esp_partition_t *running = esp_ota_get_running_partition(); + if (esp_ota_get_state_partition(running, &state) == ESP_OK) { + if (state == ESP_OTA_IMG_PENDING_VERIFY) { + esp_ota_mark_app_valid_cancel_rollback(); + } + } } void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); } From 3bdd351d49b399712f701790632968f2931aecb4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 09:52:33 -1000 Subject: [PATCH 152/526] [wifi] Convert fast_connect to compile-time define, save 156-1024 bytes flash (#11328) --- esphome/components/wifi/__init__.py | 3 +- esphome/components/wifi/wifi_component.cpp | 88 ++++++++++++---------- esphome/components/wifi/wifi_component.h | 10 ++- esphome/core/defines.h | 1 + tests/components/wifi/common-eap.yaml | 1 + 5 files changed, 60 insertions(+), 43 deletions(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 1f742dc1a8..494470cb48 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -407,7 +407,8 @@ async def to_code(config): cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE])) - cg.add(var.set_fast_connect(config[CONF_FAST_CONNECT])) + if config[CONF_FAST_CONNECT]: + cg.add_define("USE_WIFI_FAST_CONNECT") cg.add(var.set_passive_scan(config[CONF_PASSIVE_SCAN])) if CONF_OUTPUT_POWER in config: cg.add(var.set_output_power(config[CONF_OUTPUT_POWER])) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 612b11a50f..c89384d742 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -84,9 +84,9 @@ void WiFiComponent::start() { uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL; this->pref_ = global_preferences->make_preference(hash, true); - if (this->fast_connect_) { - this->fast_connect_pref_ = global_preferences->make_preference(hash + 1, false); - } +#ifdef USE_WIFI_FAST_CONNECT + this->fast_connect_pref_ = global_preferences->make_preference(hash + 1, false); +#endif SavedWifiSettings save{}; if (this->pref_.load(&save)) { @@ -108,16 +108,16 @@ void WiFiComponent::start() { ESP_LOGV(TAG, "Setting Power Save Option failed"); } - if (this->fast_connect_) { - this->trying_loaded_ap_ = this->load_fast_connect_settings_(); - if (!this->trying_loaded_ap_) { - this->ap_index_ = 0; - this->selected_ap_ = this->sta_[this->ap_index_]; - } - this->start_connecting(this->selected_ap_, false); - } else { - this->start_scanning(); +#ifdef USE_WIFI_FAST_CONNECT + this->trying_loaded_ap_ = this->load_fast_connect_settings_(); + if (!this->trying_loaded_ap_) { + this->ap_index_ = 0; + this->selected_ap_ = this->sta_[this->ap_index_]; } + this->start_connecting(this->selected_ap_, false); +#else + this->start_scanning(); +#endif #ifdef USE_WIFI_AP } else if (this->has_ap()) { this->setup_ap_config_(); @@ -168,13 +168,20 @@ void WiFiComponent::loop() { case WIFI_COMPONENT_STATE_COOLDOWN: { this->status_set_warning(LOG_STR("waiting to reconnect")); if (millis() - this->action_started_ > 5000) { - if (this->fast_connect_ || this->retry_hidden_) { +#ifdef USE_WIFI_FAST_CONNECT + // NOTE: This check may not make sense here as it could interfere with AP cycling + if (!this->selected_ap_.get_bssid().has_value()) + this->selected_ap_ = this->sta_[0]; + this->start_connecting(this->selected_ap_, false); +#else + if (this->retry_hidden_) { if (!this->selected_ap_.get_bssid().has_value()) this->selected_ap_ = this->sta_[0]; this->start_connecting(this->selected_ap_, false); } else { this->start_scanning(); } +#endif } break; } @@ -244,7 +251,6 @@ WiFiComponent::WiFiComponent() { global_wifi_component = this; } bool WiFiComponent::has_ap() const { return this->has_ap_; } bool WiFiComponent::has_sta() const { return !this->sta_.empty(); } -void WiFiComponent::set_fast_connect(bool fast_connect) { this->fast_connect_ = fast_connect; } #ifdef USE_WIFI_11KV_SUPPORT void WiFiComponent::set_btm(bool btm) { this->btm_ = btm; } void WiFiComponent::set_rrm(bool rrm) { this->rrm_ = rrm; } @@ -723,9 +729,9 @@ void WiFiComponent::check_connecting_finished() { this->scan_result_.shrink_to_fit(); } - if (this->fast_connect_) { - this->save_fast_connect_settings_(); - } +#ifdef USE_WIFI_FAST_CONNECT + this->save_fast_connect_settings_(); +#endif return; } @@ -773,31 +779,31 @@ void WiFiComponent::retry_connect() { delay(10); if (!this->is_captive_portal_active_() && !this->is_esp32_improv_active_() && (this->num_retried_ > 3 || this->error_from_callback_)) { - if (this->fast_connect_) { - if (this->trying_loaded_ap_) { - this->trying_loaded_ap_ = false; - this->ap_index_ = 0; // Retry from the first configured AP - } else if (this->ap_index_ >= this->sta_.size() - 1) { - ESP_LOGW(TAG, "No more APs to try"); - this->ap_index_ = 0; - this->restart_adapter(); - } else { - // Try next AP - this->ap_index_++; - } - this->num_retried_ = 0; - this->selected_ap_ = this->sta_[this->ap_index_]; +#ifdef USE_WIFI_FAST_CONNECT + if (this->trying_loaded_ap_) { + this->trying_loaded_ap_ = false; + this->ap_index_ = 0; // Retry from the first configured AP + } else if (this->ap_index_ >= this->sta_.size() - 1) { + ESP_LOGW(TAG, "No more APs to try"); + this->ap_index_ = 0; + this->restart_adapter(); } else { - if (this->num_retried_ > 5) { - // If retry failed for more than 5 times, let's restart STA - this->restart_adapter(); - } else { - // Try hidden networks after 3 failed retries - ESP_LOGD(TAG, "Retrying with hidden networks"); - this->retry_hidden_ = true; - this->num_retried_++; - } + // Try next AP + this->ap_index_++; } + this->num_retried_ = 0; + this->selected_ap_ = this->sta_[this->ap_index_]; +#else + if (this->num_retried_ > 5) { + // If retry failed for more than 5 times, let's restart STA + this->restart_adapter(); + } else { + // Try hidden networks after 3 failed retries + ESP_LOGD(TAG, "Retrying with hidden networks"); + this->retry_hidden_ = true; + this->num_retried_++; + } +#endif } else { this->num_retried_++; } @@ -843,6 +849,7 @@ bool WiFiComponent::is_esp32_improv_active_() { #endif } +#ifdef USE_WIFI_FAST_CONNECT bool WiFiComponent::load_fast_connect_settings_() { SavedWifiFastConnectSettings fast_connect_save{}; @@ -877,6 +884,7 @@ void WiFiComponent::save_fast_connect_settings_() { ESP_LOGD(TAG, "Saved fast_connect settings"); } } +#endif void WiFiAP::set_ssid(const std::string &ssid) { this->ssid_ = ssid; } void WiFiAP::set_bssid(bssid_t bssid) { this->bssid_ = bssid; } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 508024a235..10aa82a065 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -240,7 +240,6 @@ class WiFiComponent : public Component { void start_scanning(); void check_scanning_finished(); void start_connecting(const WiFiAP &ap, bool two); - void set_fast_connect(bool fast_connect); void set_ap_timeout(uint32_t ap_timeout) { ap_timeout_ = ap_timeout; } void check_connecting_finished(); @@ -364,8 +363,10 @@ class WiFiComponent : public Component { bool is_captive_portal_active_(); bool is_esp32_improv_active_(); +#ifdef USE_WIFI_FAST_CONNECT bool load_fast_connect_settings_(); void save_fast_connect_settings_(); +#endif #ifdef USE_ESP8266 static void wifi_event_callback(System_Event_t *event); @@ -399,7 +400,9 @@ class WiFiComponent : public Component { WiFiAP ap_; optional output_power_; ESPPreferenceObject pref_; +#ifdef USE_WIFI_FAST_CONNECT ESPPreferenceObject fast_connect_pref_; +#endif // Group all 32-bit integers together uint32_t action_started_; @@ -411,14 +414,17 @@ class WiFiComponent : public Component { WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF}; WiFiPowerSaveMode power_save_{WIFI_POWER_SAVE_NONE}; uint8_t num_retried_{0}; +#ifdef USE_WIFI_FAST_CONNECT uint8_t ap_index_{0}; +#endif #if USE_NETWORK_IPV6 uint8_t num_ipv6_addresses_{0}; #endif /* USE_NETWORK_IPV6 */ // Group all boolean values together - bool fast_connect_{false}; +#ifdef USE_WIFI_FAST_CONNECT bool trying_loaded_ap_{false}; +#endif bool retry_hidden_{false}; bool has_ap_{false}; bool handled_connected_state_{false}; diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 1afb296fc0..b1bd7f92d7 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -199,6 +199,7 @@ #define USE_WEBSERVER_PORT 80 // NOLINT #define USE_WEBSERVER_SORTING #define USE_WIFI_11KV_SUPPORT +#define USE_WIFI_FAST_CONNECT #define USB_HOST_MAX_REQUESTS 16 #ifdef USE_ARDUINO diff --git a/tests/components/wifi/common-eap.yaml b/tests/components/wifi/common-eap.yaml index 779cd6b49a..52319fa5a1 100644 --- a/tests/components/wifi/common-eap.yaml +++ b/tests/components/wifi/common-eap.yaml @@ -1,4 +1,5 @@ wifi: + fast_connect: true networks: - ssid: MySSID eap: From 70cb1793f38111e3ce81c492c9708f5928f37409 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 09:53:05 -1000 Subject: [PATCH 153/526] [wifi] Optimize WiFi scan results with in-place construction (#11330) --- esphome/components/wifi/wifi_component_esp8266.cpp | 8 ++++---- esphome/components/wifi/wifi_component_esp_idf.cpp | 4 ++-- esphome/components/wifi/wifi_component_libretiny.cpp | 6 +++--- esphome/core/helpers.h | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 59909b2cb5..4e17c42f41 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -706,10 +706,10 @@ void WiFiComponent::wifi_scan_done_callback_(void *arg, STATUS status) { this->scan_result_.init(count); for (bss_info *it = head; it != nullptr; it = STAILQ_NEXT(it, next)) { - WiFiScanResult res({it->bssid[0], it->bssid[1], it->bssid[2], it->bssid[3], it->bssid[4], it->bssid[5]}, - std::string(reinterpret_cast(it->ssid), it->ssid_len), it->channel, it->rssi, - it->authmode != AUTH_OPEN, it->is_hidden != 0); - this->scan_result_.push_back(res); + this->scan_result_.emplace_back( + bssid_t{it->bssid[0], it->bssid[1], it->bssid[2], it->bssid[3], it->bssid[4], it->bssid[5]}, + std::string(reinterpret_cast(it->ssid), it->ssid_len), it->channel, it->rssi, it->authmode != AUTH_OPEN, + it->is_hidden != 0); } this->scan_done_ = true; } diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index ce1cc961d0..08ecba3598 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -789,8 +789,8 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { bssid_t bssid; std::copy(record.bssid, record.bssid + 6, bssid.begin()); std::string ssid(reinterpret_cast(record.ssid)); - WiFiScanResult result(bssid, ssid, record.primary, record.rssi, record.authmode != WIFI_AUTH_OPEN, ssid.empty()); - scan_result_.push_back(result); + scan_result_.emplace_back(bssid, ssid, record.primary, record.rssi, record.authmode != WIFI_AUTH_OPEN, + ssid.empty()); } } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_START) { diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index cb179d9022..45e2fba82a 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -419,9 +419,9 @@ void WiFiComponent::wifi_scan_done_callback_() { uint8_t *bssid = WiFi.BSSID(i); int32_t channel = WiFi.channel(i); - WiFiScanResult scan({bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]}, std::string(ssid.c_str()), - channel, rssi, authmode != WIFI_AUTH_OPEN, ssid.length() == 0); - this->scan_result_.push_back(scan); + this->scan_result_.emplace_back(bssid_t{bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]}, + std::string(ssid.c_str()), channel, rssi, authmode != WIFI_AUTH_OPEN, + ssid.length() == 0); } WiFi.scanDelete(); this->scan_done_ = true; diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 326718e974..37a64d46b2 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -281,13 +281,13 @@ template class FixedVector { } } - /// Emplace element without bounds checking - constructs in-place + /// Emplace element without bounds checking - constructs in-place with arguments /// Caller must ensure sufficient capacity was allocated via init() /// Returns reference to the newly constructed element /// NOTE: Caller MUST ensure size_ < capacity_ before calling - T &emplace_back() { - // Use placement new to default-construct the object in pre-allocated memory - new (&data_[size_]) T(); + template T &emplace_back(Args &&...args) { + // Use placement new to construct the object in pre-allocated memory + new (&data_[size_]) T(std::forward(args)...); size_++; return data_[size_ - 1]; } From 1a2057df3011a6e5de32ffb36b7efc62f451a9cd Mon Sep 17 00:00:00 2001 From: Juan Antonio Aldea Date: Sun, 19 Oct 2025 23:15:17 +0200 Subject: [PATCH 154/526] Migrate from hexencode() to format_hex_pretty() in Kuntze component (#11372) --- esphome/components/kuntze/kuntze.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/kuntze/kuntze.cpp b/esphome/components/kuntze/kuntze.cpp index 42545d9d54..30f98aaa99 100644 --- a/esphome/components/kuntze/kuntze.cpp +++ b/esphome/components/kuntze/kuntze.cpp @@ -14,7 +14,7 @@ void Kuntze::on_modbus_data(const std::vector &data) { auto get_16bit = [&](int i) -> uint16_t { return (uint16_t(data[i * 2]) << 8) | uint16_t(data[i * 2 + 1]); }; this->waiting_ = false; - ESP_LOGV(TAG, "Data: %s", hexencode(data).c_str()); + ESP_LOGV(TAG, "Data: %s", format_hex_pretty(data).c_str()); float value = (float) get_16bit(0); for (int i = 0; i < data[3]; i++) From 1e1fefbd0a865fe2858441ab9d35d73b71347a89 Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Sun, 19 Oct 2025 23:31:25 +0200 Subject: [PATCH 155/526] [substitutions] !extend and !remove now support substitutions and jinja (#11203) --- esphome/config.py | 109 ++++++++++++++---- esphome/config_helpers.py | 86 ++++---------- esphome/config_validation.py | 7 -- .../component_tests/packages/test_packages.py | 71 +++++------- .../05-extend-remove.approved.yaml | 9 ++ .../substitutions/05-extend-remove.input.yaml | 22 ++++ tests/unit_tests/test_substitutions.py | 3 + 7 files changed, 171 insertions(+), 136 deletions(-) create mode 100644 tests/unit_tests/fixtures/substitutions/05-extend-remove.approved.yaml create mode 100644 tests/unit_tests/fixtures/substitutions/05-extend-remove.input.yaml diff --git a/esphome/config.py b/esphome/config.py index 6adecb5c65..634dba8dad 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -12,7 +12,7 @@ from typing import Any import voluptuous as vol from esphome import core, loader, pins, yaml_util -from esphome.config_helpers import Extend, Remove, merge_dicts_ordered +from esphome.config_helpers import Extend, Remove, merge_config, merge_dicts_ordered import esphome.config_validation as cv from esphome.const import ( CONF_ESPHOME, @@ -324,13 +324,7 @@ def iter_ids(config, path=None): yield from iter_ids(value, path + [key]) -def recursive_check_replaceme(value): - if isinstance(value, list): - return cv.Schema([recursive_check_replaceme])(value) - if isinstance(value, dict): - return cv.Schema({cv.valid: recursive_check_replaceme})(value) - if isinstance(value, ESPLiteralValue): - pass +def check_replaceme(value): if isinstance(value, str) and value == "REPLACEME": raise cv.Invalid( "Found 'REPLACEME' in configuration, this is most likely an error. " @@ -339,7 +333,86 @@ def recursive_check_replaceme(value): "If you want to use the literal REPLACEME string, " 'please use "!literal REPLACEME"' ) - return value + + +def _build_list_index(lst): + index = OrderedDict() + extensions, removals = [], set() + for item in lst: + if item is None: + removals.add(None) + continue + item_id = None + if isinstance(item, dict) and (item_id := item.get(CONF_ID)): + if isinstance(item_id, Extend): + extensions.append(item) + continue + if isinstance(item_id, Remove): + removals.add(item_id.value) + continue + if not item_id or item_id in index: + # no id or duplicate -> pass through with identity-based key + item_id = id(item) + index[item_id] = item + return index, extensions, removals + + +def resolve_extend_remove(value, is_key=None): + if isinstance(value, ESPLiteralValue): + return # do not check inside literal blocks + if isinstance(value, list): + index, extensions, removals = _build_list_index(value) + if extensions or removals: + # Rebuild the original list after + # processing all extensions and removals + for item in extensions: + item_id = item[CONF_ID].value + if item_id in removals: + continue + old = index.get(item_id) + if old is None: + # Failed to find source for extension + # Find index of item to show error at correct position + i = next( + ( + i + for i, d in enumerate(value) + if d.get(CONF_ID) == item[CONF_ID] + ) + ) + with cv.prepend_path(i): + raise cv.Invalid( + f"Source for extension of ID '{item_id}' was not found." + ) + item[CONF_ID] = item_id + index[item_id] = merge_config(old, item) + for item_id in removals: + index.pop(item_id, None) + + value[:] = index.values() + + for i, item in enumerate(value): + with cv.prepend_path(i): + resolve_extend_remove(item, False) + return + if isinstance(value, dict): + removals = [] + for k, v in value.items(): + with cv.prepend_path(k): + if isinstance(v, Remove): + removals.append(k) + continue + resolve_extend_remove(k, True) + resolve_extend_remove(v, False) + for k in removals: + value.pop(k, None) + return + if is_key: + return # do not check keys (yet) + + check_replaceme(value) + + return class ConfigValidationStep(abc.ABC): @@ -437,19 +510,6 @@ class LoadValidationStep(ConfigValidationStep): continue p_name = p_config.get("platform") if p_name is None: - p_id = p_config.get(CONF_ID) - if isinstance(p_id, Extend): - result.add_str_error( - f"Source for extension of ID '{p_id.value}' was not found.", - path + [CONF_ID], - ) - continue - if isinstance(p_id, Remove): - result.add_str_error( - f"Source for removal of ID '{p_id.value}' was not found.", - path + [CONF_ID], - ) - continue result.add_str_error( f"'{self.domain}' requires a 'platform' key but it was not specified.", path, @@ -934,9 +994,10 @@ def validate_config( CORE.raw_config = config - # 1.1. Check for REPLACEME special value + # 1.1. Resolve !extend and !remove and check for REPLACEME + # After this step, there will not be any Extend or Remove values in the config anymore try: - recursive_check_replaceme(config) + resolve_extend_remove(config) except vol.Invalid as err: result.add_error(err) diff --git a/esphome/config_helpers.py b/esphome/config_helpers.py index 88cfa49fdc..c0a3b99968 100644 --- a/esphome/config_helpers.py +++ b/esphome/config_helpers.py @@ -1,7 +1,6 @@ from collections.abc import Callable from esphome.const import ( - CONF_ID, CONF_LEVEL, CONF_LOGGER, KEY_CORE, @@ -75,73 +74,28 @@ class Remove: return isinstance(b, Remove) and self.value == b.value -def merge_config(full_old, full_new): - def merge(old, new): - if isinstance(new, dict): - if not isinstance(old, dict): - return new - # Preserve OrderedDict type by copying to OrderedDict if either input is OrderedDict - if isinstance(old, OrderedDict) or isinstance(new, OrderedDict): - res = OrderedDict(old) - else: - res = old.copy() - for k, v in new.items(): - if isinstance(v, Remove) and k in old: - del res[k] - else: - res[k] = merge(old[k], v) if k in old else v - return res - if isinstance(new, list): - if not isinstance(old, list): - return new - res = old.copy() - ids = { - v_id: i - for i, v in enumerate(res) - if isinstance(v, dict) - and (v_id := v.get(CONF_ID)) - and isinstance(v_id, str) - } - extend_ids = { - v_id.value: i - for i, v in enumerate(res) - if isinstance(v, dict) - and (v_id := v.get(CONF_ID)) - and isinstance(v_id, Extend) - } - - ids_to_delete = [] - for v in new: - if isinstance(v, dict) and (new_id := v.get(CONF_ID)): - if isinstance(new_id, Extend): - new_id = new_id.value - if new_id in ids: - v[CONF_ID] = new_id - res[ids[new_id]] = merge(res[ids[new_id]], v) - continue - elif isinstance(new_id, Remove): - new_id = new_id.value - if new_id in ids: - ids_to_delete.append(ids[new_id]) - continue - elif ( - new_id in extend_ids - ): # When a package is extending a non-packaged item - extend_res = res[extend_ids[new_id]] - extend_res[CONF_ID] = new_id - new_v = merge(v, extend_res) - res[extend_ids[new_id]] = new_v - continue - else: - ids[new_id] = len(res) - res.append(v) - return [v for i, v in enumerate(res) if i not in ids_to_delete] - if new is None: - return old - +def merge_config(old, new): + if isinstance(new, Remove): return new + if isinstance(new, dict): + if not isinstance(old, dict): + return new + # Preserve OrderedDict type by copying to OrderedDict if either input is OrderedDict + if isinstance(old, OrderedDict) or isinstance(new, OrderedDict): + res = OrderedDict(old) + else: + res = old.copy() + for k, v in new.items(): + res[k] = merge_config(old.get(k), v) + return res + if isinstance(new, list): + if not isinstance(old, list): + return new + return old + new + if new is None: + return old - return merge(full_old, full_new) + return new def filter_source_files_from_platform( diff --git a/esphome/config_validation.py b/esphome/config_validation.py index e2f0b835c9..c613a984c4 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -24,7 +24,6 @@ import voluptuous as vol from esphome import core import esphome.codegen as cg -from esphome.config_helpers import Extend, Remove from esphome.const import ( ALLOWED_NAME_CHARS, CONF_AVAILABILITY, @@ -624,12 +623,6 @@ def declare_id(type): if value is None: return core.ID(None, is_declaration=True, type=type) - if isinstance(value, Extend): - raise Invalid(f"Source for extension of ID '{value.value}' was not found.") - - if isinstance(value, Remove): - raise Invalid(f"Source for Removal of ID '{value.value}' was not found.") - return core.ID(validate_id_name(value), is_declaration=True, type=type) return validator diff --git a/tests/component_tests/packages/test_packages.py b/tests/component_tests/packages/test_packages.py index 4712daad0d..d66ca58a69 100644 --- a/tests/component_tests/packages/test_packages.py +++ b/tests/component_tests/packages/test_packages.py @@ -6,6 +6,7 @@ from unittest.mock import MagicMock, patch import pytest from esphome.components.packages import do_packages_pass +from esphome.config import resolve_extend_remove from esphome.config_helpers import Extend, Remove import esphome.config_validation as cv from esphome.const import ( @@ -64,13 +65,20 @@ def fixture_basic_esphome(): return {CONF_NAME: TEST_DEVICE_NAME, CONF_PLATFORM: TEST_PLATFORM} +def packages_pass(config): + """Wrapper around packages_pass that also resolves Extend and Remove.""" + config = do_packages_pass(config) + resolve_extend_remove(config) + return config + + def test_package_unused(basic_esphome, basic_wifi): """ Ensures do_package_pass does not change a config if packages aren't used. """ config = {CONF_ESPHOME: basic_esphome, CONF_WIFI: basic_wifi} - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == config @@ -83,7 +91,7 @@ def test_package_invalid_dict(basic_esphome, basic_wifi): config = {CONF_ESPHOME: basic_esphome, CONF_PACKAGES: basic_wifi | {CONF_URL: ""}} with pytest.raises(cv.Invalid): - do_packages_pass(config) + packages_pass(config) def test_package_include(basic_wifi, basic_esphome): @@ -99,7 +107,7 @@ def test_package_include(basic_wifi, basic_esphome): expected = {CONF_ESPHOME: basic_esphome, CONF_WIFI: basic_wifi} - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -124,7 +132,7 @@ def test_package_append(basic_wifi, basic_esphome): }, } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -148,7 +156,7 @@ def test_package_override(basic_wifi, basic_esphome): }, } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -177,7 +185,7 @@ def test_multiple_package_order(): }, } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -233,7 +241,7 @@ def test_package_list_merge(): ] } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -311,7 +319,7 @@ def test_package_list_merge_by_id(): ] } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -350,13 +358,13 @@ def test_package_merge_by_id_with_list(): ] } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected def test_package_merge_by_missing_id(): """ - Ensures that components with missing IDs are not merged. + Ensures that a validation error is thrown when trying to extend a missing ID. """ config = { @@ -379,25 +387,15 @@ def test_package_merge_by_missing_id(): ], } - expected = { - CONF_SENSOR: [ - { - CONF_ID: TEST_SENSOR_ID_1, - CONF_FILTERS: [{CONF_MULTIPLY: 42.0}], - }, - { - CONF_ID: TEST_SENSOR_ID_1, - CONF_FILTERS: [{CONF_MULTIPLY: 10.0}], - }, - { - CONF_ID: Extend(TEST_SENSOR_ID_2), - CONF_FILTERS: [{CONF_OFFSET: 146.0}], - }, - ] - } + error_raised = False + try: + packages_pass(config) + assert False, "Expected validation error for missing ID" + except cv.Invalid as err: + error_raised = True + assert err.path == [CONF_SENSOR, 2] - actual = do_packages_pass(config) - assert actual == expected + assert error_raised def test_package_list_remove_by_id(): @@ -447,7 +445,7 @@ def test_package_list_remove_by_id(): ] } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -493,7 +491,7 @@ def test_multiple_package_list_remove_by_id(): ] } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -514,7 +512,7 @@ def test_package_dict_remove_by_id(basic_wifi, basic_esphome): CONF_ESPHOME: basic_esphome, } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -545,7 +543,6 @@ def test_package_remove_by_missing_id(): } expected = { - "missing_key": Remove(), CONF_SENSOR: [ { CONF_ID: TEST_SENSOR_ID_1, @@ -555,14 +552,10 @@ def test_package_remove_by_missing_id(): CONF_ID: TEST_SENSOR_ID_1, CONF_FILTERS: [{CONF_MULTIPLY: 10.0}], }, - { - CONF_ID: Remove(TEST_SENSOR_ID_2), - CONF_FILTERS: [{CONF_OFFSET: 146.0}], - }, ], } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -634,7 +627,7 @@ def test_remote_packages_with_files_list( ] } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected @@ -730,5 +723,5 @@ def test_remote_packages_with_files_and_vars( ] } - actual = do_packages_pass(config) + actual = packages_pass(config) assert actual == expected diff --git a/tests/unit_tests/fixtures/substitutions/05-extend-remove.approved.yaml b/tests/unit_tests/fixtures/substitutions/05-extend-remove.approved.yaml new file mode 100644 index 0000000000..a479370f4b --- /dev/null +++ b/tests/unit_tests/fixtures/substitutions/05-extend-remove.approved.yaml @@ -0,0 +1,9 @@ +substitutions: + A: component1 + B: component2 + C: component3 +some_component: + - id: component1 + value: 2 + - id: component2 + value: 5 diff --git a/tests/unit_tests/fixtures/substitutions/05-extend-remove.input.yaml b/tests/unit_tests/fixtures/substitutions/05-extend-remove.input.yaml new file mode 100644 index 0000000000..2e0e60798d --- /dev/null +++ b/tests/unit_tests/fixtures/substitutions/05-extend-remove.input.yaml @@ -0,0 +1,22 @@ +substitutions: + A: component1 + B: component2 + C: component3 + +packages: + - some_component: + - id: component1 + value: 1 + - id: !extend ${B} + value: 4 + - id: !extend ${B} + value: 5 + - id: component3 + value: 6 + +some_component: + - id: !extend ${A} + value: 2 + - id: component2 + value: 3 + - id: !remove ${C} diff --git a/tests/unit_tests/test_substitutions.py b/tests/unit_tests/test_substitutions.py index 59396a4a83..beb1ebc73e 100644 --- a/tests/unit_tests/test_substitutions.py +++ b/tests/unit_tests/test_substitutions.py @@ -4,6 +4,7 @@ from pathlib import Path from esphome import config as config_module, yaml_util from esphome.components import substitutions +from esphome.config import resolve_extend_remove from esphome.config_helpers import merge_config from esphome.const import CONF_PACKAGES, CONF_SUBSTITUTIONS from esphome.core import CORE @@ -81,6 +82,8 @@ def test_substitutions_fixtures(fixture_path): substitutions.do_substitution_pass(config, None) + resolve_extend_remove(config) + # Also load expected using ESPHome's loader, or use {} if missing and DEV_MODE if expected_path.is_file(): expected = yaml_util.load_yaml(expected_path) From afbd3f77af222968161f7bc923b392e29fe00879 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:08:30 +1300 Subject: [PATCH 156/526] [light] Clean up deprecated functions from 1.21 (#11389) --- esphome/components/light/addressable_light.h | 8 +++---- esphome/components/light/light_traits.h | 22 +------------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index baa4507d2f..3e94a39745 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -1,11 +1,11 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/core/defines.h" -#include "esphome/core/color.h" #include "esp_color_correction.h" #include "esp_color_view.h" #include "esp_range_view.h" +#include "esphome/core/color.h" +#include "esphome/core/component.h" +#include "esphome/core/defines.h" #include "light_output.h" #include "light_state.h" #include "transformers.h" @@ -17,8 +17,6 @@ namespace esphome { namespace light { -using ESPColor ESPDEPRECATED("esphome::light::ESPColor is deprecated, use esphome::Color instead.", "v1.21") = Color; - /// Convert the color information from a `LightColorValues` object to a `Color` object (does not apply brightness). Color color_from_light_color_values(LightColorValues val); diff --git a/esphome/components/light/light_traits.h b/esphome/components/light/light_traits.h index c83d8ad2a9..4532edca83 100644 --- a/esphome/components/light/light_traits.h +++ b/esphome/components/light/light_traits.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/helpers.h" #include "color_mode.h" +#include "esphome/core/helpers.h" namespace esphome { @@ -31,26 +31,6 @@ class LightTraits { return this->supported_color_modes_.has_capability(color_capability); } - ESPDEPRECATED("get_supports_brightness() is deprecated, use color modes instead.", "v1.21") - bool get_supports_brightness() const { return this->supports_color_capability(ColorCapability::BRIGHTNESS); } - ESPDEPRECATED("get_supports_rgb() is deprecated, use color modes instead.", "v1.21") - bool get_supports_rgb() const { return this->supports_color_capability(ColorCapability::RGB); } - ESPDEPRECATED("get_supports_rgb_white_value() is deprecated, use color modes instead.", "v1.21") - bool get_supports_rgb_white_value() const { - return this->supports_color_mode(ColorMode::RGB_WHITE) || - this->supports_color_mode(ColorMode::RGB_COLOR_TEMPERATURE); - } - ESPDEPRECATED("get_supports_color_temperature() is deprecated, use color modes instead.", "v1.21") - bool get_supports_color_temperature() const { - return this->supports_color_capability(ColorCapability::COLOR_TEMPERATURE); - } - ESPDEPRECATED("get_supports_color_interlock() is deprecated, use color modes instead.", "v1.21") - bool get_supports_color_interlock() const { - return this->supports_color_mode(ColorMode::RGB) && - (this->supports_color_mode(ColorMode::WHITE) || this->supports_color_mode(ColorMode::COLD_WARM_WHITE) || - this->supports_color_mode(ColorMode::COLOR_TEMPERATURE)); - } - float get_min_mireds() const { return this->min_mireds_; } void set_min_mireds(float min_mireds) { this->min_mireds_ = min_mireds; } float get_max_mireds() const { return this->max_mireds_; } From 9c146a70708899fc01522849bf8e018919c8f841 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:11:35 +1300 Subject: [PATCH 157/526] [climate] Clean up deprecated functions from 1.20 (#11388) --- esphome/components/climate/climate_traits.h | 46 +-------------------- 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/esphome/components/climate/climate_traits.h b/esphome/components/climate/climate_traits.h index 50c1e79ad2..2962a147d7 100644 --- a/esphome/components/climate/climate_traits.h +++ b/esphome/components/climate/climate_traits.h @@ -1,8 +1,8 @@ #pragma once -#include "esphome/core/helpers.h" -#include "climate_mode.h" #include +#include "climate_mode.h" +#include "esphome/core/helpers.h" namespace esphome { @@ -109,44 +109,12 @@ class ClimateTraits { void set_supported_modes(std::set modes) { this->supported_modes_ = std::move(modes); } void add_supported_mode(ClimateMode mode) { this->supported_modes_.insert(mode); } - ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") - void set_supports_auto_mode(bool supports_auto_mode) { set_mode_support_(CLIMATE_MODE_AUTO, supports_auto_mode); } - ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") - void set_supports_cool_mode(bool supports_cool_mode) { set_mode_support_(CLIMATE_MODE_COOL, supports_cool_mode); } - ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") - void set_supports_heat_mode(bool supports_heat_mode) { set_mode_support_(CLIMATE_MODE_HEAT, supports_heat_mode); } - ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") - void set_supports_heat_cool_mode(bool supported) { set_mode_support_(CLIMATE_MODE_HEAT_COOL, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") - void set_supports_fan_only_mode(bool supports_fan_only_mode) { - set_mode_support_(CLIMATE_MODE_FAN_ONLY, supports_fan_only_mode); - } - ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") - void set_supports_dry_mode(bool supports_dry_mode) { set_mode_support_(CLIMATE_MODE_DRY, supports_dry_mode); } bool supports_mode(ClimateMode mode) const { return this->supported_modes_.count(mode); } const std::set &get_supported_modes() const { return this->supported_modes_; } void set_supported_fan_modes(std::set modes) { this->supported_fan_modes_ = std::move(modes); } void add_supported_fan_mode(ClimateFanMode mode) { this->supported_fan_modes_.insert(mode); } void add_supported_custom_fan_mode(const std::string &mode) { this->supported_custom_fan_modes_.insert(mode); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20") - void set_supports_fan_mode_on(bool supported) { set_fan_mode_support_(CLIMATE_FAN_ON, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20") - void set_supports_fan_mode_off(bool supported) { set_fan_mode_support_(CLIMATE_FAN_OFF, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20") - void set_supports_fan_mode_auto(bool supported) { set_fan_mode_support_(CLIMATE_FAN_AUTO, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20") - void set_supports_fan_mode_low(bool supported) { set_fan_mode_support_(CLIMATE_FAN_LOW, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20") - void set_supports_fan_mode_medium(bool supported) { set_fan_mode_support_(CLIMATE_FAN_MEDIUM, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20") - void set_supports_fan_mode_high(bool supported) { set_fan_mode_support_(CLIMATE_FAN_HIGH, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20") - void set_supports_fan_mode_middle(bool supported) { set_fan_mode_support_(CLIMATE_FAN_MIDDLE, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20") - void set_supports_fan_mode_focus(bool supported) { set_fan_mode_support_(CLIMATE_FAN_FOCUS, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20") - void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); } bool supports_fan_mode(ClimateFanMode fan_mode) const { return this->supported_fan_modes_.count(fan_mode); } bool get_supports_fan_modes() const { return !this->supported_fan_modes_.empty() || !this->supported_custom_fan_modes_.empty(); @@ -178,16 +146,6 @@ class ClimateTraits { void set_supported_swing_modes(std::set modes) { this->supported_swing_modes_ = std::move(modes); } void add_supported_swing_mode(ClimateSwingMode mode) { this->supported_swing_modes_.insert(mode); } - ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20") - void set_supports_swing_mode_off(bool supported) { set_swing_mode_support_(CLIMATE_SWING_OFF, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20") - void set_supports_swing_mode_both(bool supported) { set_swing_mode_support_(CLIMATE_SWING_BOTH, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20") - void set_supports_swing_mode_vertical(bool supported) { set_swing_mode_support_(CLIMATE_SWING_VERTICAL, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20") - void set_supports_swing_mode_horizontal(bool supported) { - set_swing_mode_support_(CLIMATE_SWING_HORIZONTAL, supported); - } bool supports_swing_mode(ClimateSwingMode swing_mode) const { return this->supported_swing_modes_.count(swing_mode); } bool get_supports_swing_modes() const { return !this->supported_swing_modes_.empty(); } const std::set &get_supported_swing_modes() const { return this->supported_swing_modes_; } From 020cea80b205a6a7104a928f3f5142a09171748d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:16:50 +1300 Subject: [PATCH 158/526] [nextion] Clean up deprecated code from 1.20 (#11393) --- esphome/components/nextion/nextion.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 0ce9d02e97..fc152ece1e 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -1291,9 +1291,6 @@ void Nextion::check_pending_waveform_() { void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; } -ESPDEPRECATED("set_wait_for_ack(bool) deprecated, no effect", "v1.20") -void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "Deprecated"); } - bool Nextion::is_updating() { return this->connection_state_.is_updating_; } } // namespace nextion From 862bbb7fe158ea44d519c8c705b444f928d62ed4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 13:09:09 -1000 Subject: [PATCH 159/526] [ci] Fix memory impact analysis failing on fork PRs (#11380) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../workflows/ci-memory-impact-comment.yml | 108 +++++++++ .github/workflows/ci.yml | 65 ++---- script/ci_add_metadata_to_json.py | 88 ++++++++ script/ci_memory_impact_comment.py | 207 ++++++++++++------ 4 files changed, 358 insertions(+), 110 deletions(-) create mode 100644 .github/workflows/ci-memory-impact-comment.yml create mode 100755 script/ci_add_metadata_to_json.py diff --git a/.github/workflows/ci-memory-impact-comment.yml b/.github/workflows/ci-memory-impact-comment.yml new file mode 100644 index 0000000000..4ce7abfb85 --- /dev/null +++ b/.github/workflows/ci-memory-impact-comment.yml @@ -0,0 +1,108 @@ +--- +name: Memory Impact Comment (Forks) + +on: + workflow_run: + workflows: ["CI"] + types: [completed] + +permissions: + contents: read + pull-requests: write + actions: read + +jobs: + memory-impact-comment: + name: Post memory impact comment (fork PRs only) + runs-on: ubuntu-24.04 + # Only run for PRs from forks that had successful CI runs + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.head_repository.full_name != github.repository + env: + GH_TOKEN: ${{ github.token }} + steps: + - name: Get PR details + id: pr + run: | + # Get PR details by searching for PR with matching head SHA + # The workflow_run.pull_requests field is often empty for forks + head_sha="${{ github.event.workflow_run.head_sha }}" + pr_data=$(gh api "/repos/${{ github.repository }}/commits/$head_sha/pulls" \ + --jq '.[0] | {number: .number, base_ref: .base.ref}') + if [ -z "$pr_data" ] || [ "$pr_data" == "null" ]; then + echo "No PR found for SHA $head_sha, skipping" + echo "skip=true" >> $GITHUB_OUTPUT + exit 0 + fi + + pr_number=$(echo "$pr_data" | jq -r '.number') + base_ref=$(echo "$pr_data" | jq -r '.base_ref') + + echo "pr_number=$pr_number" >> $GITHUB_OUTPUT + echo "base_ref=$base_ref" >> $GITHUB_OUTPUT + echo "Found PR #$pr_number targeting base branch: $base_ref" + + - name: Check out code from base repository + if: steps.pr.outputs.skip != 'true' + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + # Always check out from the base repository (esphome/esphome), never from forks + # Use the PR's target branch to ensure we run trusted code from the main repo + repository: ${{ github.repository }} + ref: ${{ steps.pr.outputs.base_ref }} + + - name: Restore Python + if: steps.pr.outputs.skip != 'true' + uses: ./.github/actions/restore-python + with: + python-version: "3.11" + cache-key: ${{ hashFiles('.cache-key') }} + + - name: Download memory analysis artifacts + if: steps.pr.outputs.skip != 'true' + run: | + run_id="${{ github.event.workflow_run.id }}" + echo "Downloading artifacts from workflow run $run_id" + + mkdir -p memory-analysis + + # Download target analysis artifact + if gh run download --name "memory-analysis-target" --dir memory-analysis --repo "${{ github.repository }}" "$run_id"; then + echo "Downloaded memory-analysis-target artifact." + else + echo "No memory-analysis-target artifact found." + fi + + # Download PR analysis artifact + if gh run download --name "memory-analysis-pr" --dir memory-analysis --repo "${{ github.repository }}" "$run_id"; then + echo "Downloaded memory-analysis-pr artifact." + else + echo "No memory-analysis-pr artifact found." + fi + + - name: Check if artifacts exist + id: check + if: steps.pr.outputs.skip != 'true' + run: | + if [ -f ./memory-analysis/memory-analysis-target.json ] && [ -f ./memory-analysis/memory-analysis-pr.json ]; then + echo "found=true" >> $GITHUB_OUTPUT + else + echo "found=false" >> $GITHUB_OUTPUT + echo "Memory analysis artifacts not found, skipping comment" + fi + + - name: Post or update PR comment + if: steps.pr.outputs.skip != 'true' && steps.check.outputs.found == 'true' + env: + PR_NUMBER: ${{ steps.pr.outputs.pr_number }} + run: | + . venv/bin/activate + # Pass PR number and JSON file paths directly to Python script + # Let Python parse the JSON to avoid shell injection risks + # The script will validate and sanitize all inputs + python script/ci_memory_impact_comment.py \ + --pr-number "$PR_NUMBER" \ + --target-json ./memory-analysis/memory-analysis-target.json \ + --pr-json ./memory-analysis/memory-analysis-pr.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42f934de9d..6f96f2ac14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -641,6 +641,12 @@ jobs: --output-env \ --output-json memory-analysis-target.json + # Add metadata to JSON before caching + python script/ci_add_metadata_to_json.py \ + --json-file memory-analysis-target.json \ + --components "$components" \ + --platform "$platform" + - name: Save memory analysis to cache if: steps.check-script.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' && steps.build.outcome == 'success' uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 @@ -720,6 +726,13 @@ jobs: python script/ci_memory_impact_extract.py \ --output-env \ --output-json memory-analysis-pr.json + + # Add metadata to JSON (components and platform are in shell variables above) + python script/ci_add_metadata_to_json.py \ + --json-file memory-analysis-pr.json \ + --components "$components" \ + --platform "$platform" + - name: Upload memory analysis JSON uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: @@ -736,10 +749,12 @@ jobs: - determine-jobs - memory-impact-target-branch - memory-impact-pr-branch - if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.memory_impact).should_run == 'true' && needs.memory-impact-target-branch.outputs.skip != 'true' + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && fromJSON(needs.determine-jobs.outputs.memory_impact).should_run == 'true' && needs.memory-impact-target-branch.outputs.skip != 'true' permissions: contents: read pull-requests: write + env: + GH_TOKEN: ${{ github.token }} steps: - name: Check out code uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -762,52 +777,16 @@ jobs: continue-on-error: true - name: Post or update PR comment env: - GH_TOKEN: ${{ github.token }} - COMPONENTS: ${{ toJSON(fromJSON(needs.determine-jobs.outputs.memory_impact).components) }} - PLATFORM: ${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }} - TARGET_RAM: ${{ needs.memory-impact-target-branch.outputs.ram_usage }} - TARGET_FLASH: ${{ needs.memory-impact-target-branch.outputs.flash_usage }} - PR_RAM: ${{ needs.memory-impact-pr-branch.outputs.ram_usage }} - PR_FLASH: ${{ needs.memory-impact-pr-branch.outputs.flash_usage }} - TARGET_CACHE_HIT: ${{ needs.memory-impact-target-branch.outputs.cache_hit }} + PR_NUMBER: ${{ github.event.pull_request.number }} run: | . venv/bin/activate - # Check if analysis JSON files exist - target_json_arg="" - pr_json_arg="" - - if [ -f ./memory-analysis/memory-analysis-target.json ]; then - echo "Found target analysis JSON" - target_json_arg="--target-json ./memory-analysis/memory-analysis-target.json" - else - echo "No target analysis JSON found" - fi - - if [ -f ./memory-analysis/memory-analysis-pr.json ]; then - echo "Found PR analysis JSON" - pr_json_arg="--pr-json ./memory-analysis/memory-analysis-pr.json" - else - echo "No PR analysis JSON found" - fi - - # Add cache flag if target was cached - cache_flag="" - if [ "$TARGET_CACHE_HIT" == "true" ]; then - cache_flag="--target-cache-hit" - fi - + # Pass JSON file paths directly to Python script + # All data is extracted from JSON files for security python script/ci_memory_impact_comment.py \ - --pr-number "${{ github.event.pull_request.number }}" \ - --components "$COMPONENTS" \ - --platform "$PLATFORM" \ - --target-ram "$TARGET_RAM" \ - --target-flash "$TARGET_FLASH" \ - --pr-ram "$PR_RAM" \ - --pr-flash "$PR_FLASH" \ - $target_json_arg \ - $pr_json_arg \ - $cache_flag + --pr-number "$PR_NUMBER" \ + --target-json ./memory-analysis/memory-analysis-target.json \ + --pr-json ./memory-analysis/memory-analysis-pr.json ci-status: name: CI Status diff --git a/script/ci_add_metadata_to_json.py b/script/ci_add_metadata_to_json.py new file mode 100755 index 0000000000..687b5131c0 --- /dev/null +++ b/script/ci_add_metadata_to_json.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +"""Add metadata to memory analysis JSON file. + +This script adds components and platform metadata to an existing +memory analysis JSON file. Used by CI to ensure all required fields are present +for the comment script. +""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import sys + + +def main() -> int: + """Main entry point.""" + parser = argparse.ArgumentParser( + description="Add metadata to memory analysis JSON file" + ) + parser.add_argument( + "--json-file", + required=True, + help="Path to JSON file to update", + ) + parser.add_argument( + "--components", + required=True, + help='JSON array of component names (e.g., \'["api", "wifi"]\')', + ) + parser.add_argument( + "--platform", + required=True, + help="Platform name", + ) + + args = parser.parse_args() + + # Load existing JSON + json_path = Path(args.json_file) + if not json_path.exists(): + print(f"Error: JSON file not found: {args.json_file}", file=sys.stderr) + return 1 + + try: + with open(json_path, encoding="utf-8") as f: + data = json.load(f) + except (json.JSONDecodeError, OSError) as e: + print(f"Error loading JSON: {e}", file=sys.stderr) + return 1 + + # Parse components + try: + components = json.loads(args.components) + if not isinstance(components, list): + print("Error: --components must be a JSON array", file=sys.stderr) + return 1 + # Element-level validation: ensure each component is a non-empty string + for idx, comp in enumerate(components): + if not isinstance(comp, str) or not comp.strip(): + print( + f"Error: component at index {idx} is not a non-empty string: {comp!r}", + file=sys.stderr, + ) + return 1 + except json.JSONDecodeError as e: + print(f"Error parsing components: {e}", file=sys.stderr) + return 1 + + # Add metadata + data["components"] = components + data["platform"] = args.platform + + # Write back + try: + with open(json_path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + print(f"Added metadata to {args.json_file}", file=sys.stderr) + except OSError as e: + print(f"Error writing JSON: {e}", file=sys.stderr) + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/script/ci_memory_impact_comment.py b/script/ci_memory_impact_comment.py index 4e3fbb9086..1331a44d03 100755 --- a/script/ci_memory_impact_comment.py +++ b/script/ci_memory_impact_comment.py @@ -24,6 +24,37 @@ sys.path.insert(0, str(Path(__file__).parent.parent)) # Comment marker to identify our memory impact comments COMMENT_MARKER = "" + +def run_gh_command(args: list[str], operation: str) -> subprocess.CompletedProcess: + """Run a gh CLI command with error handling. + + Args: + args: Command arguments (including 'gh') + operation: Description of the operation for error messages + + Returns: + CompletedProcess result + + Raises: + subprocess.CalledProcessError: If command fails (with detailed error output) + """ + try: + return subprocess.run( + args, + check=True, + capture_output=True, + text=True, + ) + except subprocess.CalledProcessError as e: + print( + f"ERROR: {operation} failed with exit code {e.returncode}", file=sys.stderr + ) + print(f"ERROR: Command: {' '.join(args)}", file=sys.stderr) + print(f"ERROR: stdout: {e.stdout}", file=sys.stderr) + print(f"ERROR: stderr: {e.stderr}", file=sys.stderr) + raise + + # Thresholds for emoji significance indicators (percentage) OVERALL_CHANGE_THRESHOLD = 1.0 # Overall RAM/Flash changes COMPONENT_CHANGE_THRESHOLD = 3.0 # Component breakdown changes @@ -238,7 +269,6 @@ def create_comment_body( pr_analysis: dict | None = None, target_symbols: dict | None = None, pr_symbols: dict | None = None, - target_cache_hit: bool = False, ) -> str: """Create the comment body with memory impact analysis using Jinja2 templates. @@ -253,7 +283,6 @@ def create_comment_body( pr_analysis: Optional component breakdown for PR branch target_symbols: Optional symbol map for target branch pr_symbols: Optional symbol map for PR branch - target_cache_hit: Whether target branch analysis was loaded from cache Returns: Formatted comment body @@ -283,7 +312,6 @@ def create_comment_body( "flash_change": format_change( target_flash, pr_flash, threshold=OVERALL_CHANGE_THRESHOLD ), - "target_cache_hit": target_cache_hit, "component_change_threshold": COMPONENT_CHANGE_THRESHOLD, } @@ -356,7 +384,7 @@ def find_existing_comment(pr_number: str) -> str | None: print(f"DEBUG: Looking for existing comment on PR #{pr_number}", file=sys.stderr) # Use gh api to get comments directly - this returns the numeric id field - result = subprocess.run( + result = run_gh_command( [ "gh", "api", @@ -364,9 +392,7 @@ def find_existing_comment(pr_number: str) -> str | None: "--jq", ".[] | {id, body}", ], - capture_output=True, - text=True, - check=True, + operation="Get PR comments", ) print( @@ -420,7 +446,8 @@ def update_existing_comment(comment_id: str, comment_body: str) -> None: subprocess.CalledProcessError: If gh command fails """ print(f"DEBUG: Updating existing comment {comment_id}", file=sys.stderr) - result = subprocess.run( + print(f"DEBUG: Comment body length: {len(comment_body)} bytes", file=sys.stderr) + result = run_gh_command( [ "gh", "api", @@ -430,9 +457,7 @@ def update_existing_comment(comment_id: str, comment_body: str) -> None: "-f", f"body={comment_body}", ], - check=True, - capture_output=True, - text=True, + operation="Update PR comment", ) print(f"DEBUG: Update response: {result.stdout}", file=sys.stderr) @@ -448,11 +473,10 @@ def create_new_comment(pr_number: str, comment_body: str) -> None: subprocess.CalledProcessError: If gh command fails """ print(f"DEBUG: Posting new comment on PR #{pr_number}", file=sys.stderr) - result = subprocess.run( + print(f"DEBUG: Comment body length: {len(comment_body)} bytes", file=sys.stderr) + result = run_gh_command( ["gh", "pr", "comment", pr_number, "--body", comment_body], - check=True, - capture_output=True, - text=True, + operation="Create PR comment", ) print(f"DEBUG: Post response: {result.stdout}", file=sys.stderr) @@ -484,80 +508,129 @@ def main() -> int: description="Post or update PR comment with memory impact analysis" ) parser.add_argument("--pr-number", required=True, help="PR number") - parser.add_argument( - "--components", - required=True, - help='JSON array of component names (e.g., \'["api", "wifi"]\')', - ) - parser.add_argument("--platform", required=True, help="Platform name") - parser.add_argument( - "--target-ram", type=int, required=True, help="Target branch RAM usage" - ) - parser.add_argument( - "--target-flash", type=int, required=True, help="Target branch flash usage" - ) - parser.add_argument("--pr-ram", type=int, required=True, help="PR branch RAM usage") - parser.add_argument( - "--pr-flash", type=int, required=True, help="PR branch flash usage" - ) parser.add_argument( "--target-json", - help="Optional path to target branch analysis JSON (for detailed analysis)", + required=True, + help="Path to target branch analysis JSON file", ) parser.add_argument( "--pr-json", - help="Optional path to PR branch analysis JSON (for detailed analysis)", - ) - parser.add_argument( - "--target-cache-hit", - action="store_true", - help="Indicates that target branch analysis was loaded from cache", + required=True, + help="Path to PR branch analysis JSON file", ) args = parser.parse_args() - # Parse components from JSON - try: - components = json.loads(args.components) - if not isinstance(components, list): - print("Error: --components must be a JSON array", file=sys.stderr) - sys.exit(1) - except json.JSONDecodeError as e: - print(f"Error parsing --components JSON: {e}", file=sys.stderr) + # Load analysis JSON files (all data comes from JSON for security) + target_data: dict | None = load_analysis_json(args.target_json) + if not target_data: + print("Error: Failed to load target analysis JSON", file=sys.stderr) sys.exit(1) - # Load analysis JSON files - target_analysis = None - pr_analysis = None - target_symbols = None - pr_symbols = None + pr_data: dict | None = load_analysis_json(args.pr_json) + if not pr_data: + print("Error: Failed to load PR analysis JSON", file=sys.stderr) + sys.exit(1) - if args.target_json: - target_data = load_analysis_json(args.target_json) - if target_data and target_data.get("detailed_analysis"): - target_analysis = target_data["detailed_analysis"].get("components") - target_symbols = target_data["detailed_analysis"].get("symbols") + # Extract detailed analysis if available + target_analysis: dict | None = None + pr_analysis: dict | None = None + target_symbols: dict | None = None + pr_symbols: dict | None = None - if args.pr_json: - pr_data = load_analysis_json(args.pr_json) - if pr_data and pr_data.get("detailed_analysis"): - pr_analysis = pr_data["detailed_analysis"].get("components") - pr_symbols = pr_data["detailed_analysis"].get("symbols") + if target_data.get("detailed_analysis"): + target_analysis = target_data["detailed_analysis"].get("components") + target_symbols = target_data["detailed_analysis"].get("symbols") + + if pr_data.get("detailed_analysis"): + pr_analysis = pr_data["detailed_analysis"].get("components") + pr_symbols = pr_data["detailed_analysis"].get("symbols") + + # Extract all values from JSON files (prevents shell injection from PR code) + components = target_data.get("components") + platform = target_data.get("platform") + target_ram = target_data.get("ram_bytes") + target_flash = target_data.get("flash_bytes") + pr_ram = pr_data.get("ram_bytes") + pr_flash = pr_data.get("flash_bytes") + + # Validate required fields and types + missing_fields: list[str] = [] + type_errors: list[str] = [] + + if components is None: + missing_fields.append("components") + elif not isinstance(components, list): + type_errors.append( + f"components must be a list, got {type(components).__name__}" + ) + else: + for idx, comp in enumerate(components): + if not isinstance(comp, str): + type_errors.append( + f"components[{idx}] must be a string, got {type(comp).__name__}" + ) + if platform is None: + missing_fields.append("platform") + elif not isinstance(platform, str): + type_errors.append(f"platform must be a string, got {type(platform).__name__}") + + if target_ram is None: + missing_fields.append("target.ram_bytes") + elif not isinstance(target_ram, int): + type_errors.append( + f"target.ram_bytes must be an integer, got {type(target_ram).__name__}" + ) + + if target_flash is None: + missing_fields.append("target.flash_bytes") + elif not isinstance(target_flash, int): + type_errors.append( + f"target.flash_bytes must be an integer, got {type(target_flash).__name__}" + ) + + if pr_ram is None: + missing_fields.append("pr.ram_bytes") + elif not isinstance(pr_ram, int): + type_errors.append( + f"pr.ram_bytes must be an integer, got {type(pr_ram).__name__}" + ) + + if pr_flash is None: + missing_fields.append("pr.flash_bytes") + elif not isinstance(pr_flash, int): + type_errors.append( + f"pr.flash_bytes must be an integer, got {type(pr_flash).__name__}" + ) + + if missing_fields or type_errors: + if missing_fields: + print( + f"Error: JSON files missing required fields: {', '.join(missing_fields)}", + file=sys.stderr, + ) + if type_errors: + print( + f"Error: Type validation failed: {'; '.join(type_errors)}", + file=sys.stderr, + ) + print(f"Target JSON keys: {list(target_data.keys())}", file=sys.stderr) + print(f"PR JSON keys: {list(pr_data.keys())}", file=sys.stderr) + sys.exit(1) # Create comment body # Note: Memory totals (RAM/Flash) are summed across all builds if multiple were run. comment_body = create_comment_body( components=components, - platform=args.platform, - target_ram=args.target_ram, - target_flash=args.target_flash, - pr_ram=args.pr_ram, - pr_flash=args.pr_flash, + platform=platform, + target_ram=target_ram, + target_flash=target_flash, + pr_ram=pr_ram, + pr_flash=pr_flash, target_analysis=target_analysis, pr_analysis=pr_analysis, target_symbols=target_symbols, pr_symbols=pr_symbols, - target_cache_hit=args.target_cache_hit, ) # Post or update comment From 0f87e7508b8e9ef1c2bedc0918afef5674cf3e62 Mon Sep 17 00:00:00 2001 From: Juan Antonio Aldea Date: Mon, 20 Oct 2025 01:09:28 +0200 Subject: [PATCH 160/526] remove hexencode due 2022.1 deprecation (#11383) --- esphome/core/helpers.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 37a64d46b2..234d2a7d7d 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1158,18 +1158,4 @@ template::value, int> = 0> T &id(T ///@} -/// @name Deprecated functions -///@{ - -ESPDEPRECATED("hexencode() is deprecated, use format_hex_pretty() instead.", "2022.1") -inline std::string hexencode(const uint8_t *data, uint32_t len) { return format_hex_pretty(data, len); } - -template -ESPDEPRECATED("hexencode() is deprecated, use format_hex_pretty() instead.", "2022.1") -std::string hexencode(const T &data) { - return hexencode(data.data(), data.size()); -} - -///@} - } // namespace esphome From a59b1494d80955a73cef001fceca889f94b654b1 Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Mon, 20 Oct 2025 03:17:16 +0200 Subject: [PATCH 161/526] [substitutions] Recursive substitutions and better jinja error handling and debug help (#10806) --- esphome/components/substitutions/__init__.py | 17 +-- esphome/components/substitutions/jinja.py | 109 +++++++++++++++--- .../02-expressions.approved.yaml | 2 + .../substitutions/02-expressions.input.yaml | 2 + 4 files changed, 100 insertions(+), 30 deletions(-) diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index e6bcdc063a..098d56bfad 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -6,7 +6,7 @@ import esphome.config_validation as cv from esphome.const import CONF_SUBSTITUTIONS, VALID_SUBSTITUTIONS_CHARACTERS from esphome.yaml_util import ESPHomeDataBase, ESPLiteralValue, make_data_base -from .jinja import Jinja, JinjaStr, TemplateError, TemplateRuntimeError, has_jinja +from .jinja import Jinja, JinjaError, JinjaStr, has_jinja CODEOWNERS = ["@esphome/core"] _LOGGER = logging.getLogger(__name__) @@ -57,17 +57,12 @@ def _expand_jinja(value, orig_value, path, jinja, ignore_missing): "->".join(str(x) for x in path), err.message, ) - except ( - TemplateError, - TemplateRuntimeError, - RuntimeError, - ArithmeticError, - AttributeError, - TypeError, - ) as err: + except JinjaError as err: raise cv.Invalid( - f"{type(err).__name__} Error evaluating jinja expression '{value}': {str(err)}." - f" See {'->'.join(str(x) for x in path)}", + f"{err.error_name()} Error evaluating jinja expression '{value}': {str(err.parent())}." + f"\nEvaluation stack: (most recent evaluation last)\n{err.stack_trace_str()}" + f"\nRelevant context:\n{err.context_trace_str()}" + f"\nSee {'->'.join(str(x) for x in path)}", path, ) return value diff --git a/esphome/components/substitutions/jinja.py b/esphome/components/substitutions/jinja.py index e7164d8fff..dde0162993 100644 --- a/esphome/components/substitutions/jinja.py +++ b/esphome/components/substitutions/jinja.py @@ -6,6 +6,8 @@ import re import jinja2 as jinja from jinja2.sandbox import SandboxedEnvironment +from esphome.yaml_util import ESPLiteralValue + TemplateError = jinja.TemplateError TemplateSyntaxError = jinja.TemplateSyntaxError TemplateRuntimeError = jinja.TemplateRuntimeError @@ -26,18 +28,20 @@ def has_jinja(st): return detect_jinja_re.search(st) is not None -# SAFE_GLOBAL_FUNCTIONS defines a allowlist of built-in functions that are considered safe to expose +# SAFE_GLOBALS defines a allowlist of built-in functions or modules that are considered safe to expose # in Jinja templates or other sandboxed evaluation contexts. Only functions that do not allow # arbitrary code execution, file access, or other security risks are included. # # The following functions are considered safe: +# - math: The entire math module is injected, allowing access to mathematical functions like sin, cos, sqrt, etc. # - ord: Converts a character to its Unicode code point integer. # - chr: Converts an integer to its corresponding Unicode character. # - len: Returns the length of a sequence or collection. # # These functions were chosen because they are pure, have no side effects, and do not provide access # to the file system, environment, or other potentially sensitive resources. -SAFE_GLOBAL_FUNCTIONS = { +SAFE_GLOBALS = { + "math": math, # Inject entire math module "ord": ord, "chr": chr, "len": len, @@ -56,22 +60,62 @@ class JinjaStr(str): later in the main substitutions pass. """ + Undefined = object() + def __new__(cls, value: str, upvalues=None): - obj = super().__new__(cls, value) - obj.upvalues = upvalues or {} + if isinstance(value, JinjaStr): + base = str(value) + merged = {**value.upvalues, **(upvalues or {})} + else: + base = value + merged = dict(upvalues or {}) + obj = super().__new__(cls, base) + obj.upvalues = merged + obj.result = JinjaStr.Undefined return obj - def __init__(self, value: str, upvalues=None): - self.upvalues = upvalues or {} + +class JinjaError(Exception): + def __init__(self, context_trace: dict, expr: str): + self.context_trace = context_trace + self.eval_stack = [expr] + + def parent(self): + return self.__context__ + + def error_name(self): + return type(self.parent()).__name__ + + def context_trace_str(self): + return "\n".join( + f" {k} = {repr(v)} ({type(v).__name__})" + for k, v in self.context_trace.items() + ) + + def stack_trace_str(self): + return "\n".join( + f" {len(self.eval_stack) - i}: {expr}{i == 0 and ' <-- ' + self.error_name() or ''}" + for i, expr in enumerate(self.eval_stack) + ) -class Jinja: +class TrackerContext(jinja.runtime.Context): + def resolve_or_missing(self, key): + val = super().resolve_or_missing(key) + if isinstance(val, JinjaStr): + self.environment.context_trace[key] = val + val, _ = self.environment.expand(val) + self.environment.context_trace[key] = val + return val + + +class Jinja(SandboxedEnvironment): """ Wraps a Jinja environment """ def __init__(self, context_vars): - self.env = SandboxedEnvironment( + super().__init__( trim_blocks=True, lstrip_blocks=True, block_start_string="<%", @@ -82,13 +126,20 @@ class Jinja: variable_end_string="}", undefined=jinja.StrictUndefined, ) - self.env.add_extension("jinja2.ext.do") - self.env.globals["math"] = math # Inject entire math module + self.context_class = TrackerContext + self.add_extension("jinja2.ext.do") + self.context_trace = {} self.context_vars = {**context_vars} - self.env.globals = { - **self.env.globals, + for k, v in self.context_vars.items(): + if isinstance(v, ESPLiteralValue): + continue + if isinstance(v, str) and not isinstance(v, JinjaStr) and has_jinja(v): + self.context_vars[k] = JinjaStr(v, self.context_vars) + + self.globals = { + **self.globals, **self.context_vars, - **SAFE_GLOBAL_FUNCTIONS, + **SAFE_GLOBALS, } def safe_eval(self, expr): @@ -110,23 +161,43 @@ class Jinja: result = None override_vars = {} if isinstance(content_str, JinjaStr): + if content_str.result is not JinjaStr.Undefined: + return content_str.result, None # If `value` is already a JinjaStr, it means we are trying to evaluate it again # in a parent pass. # Hopefully, all required variables are visible now. override_vars = content_str.upvalues + + old_trace = self.context_trace + self.context_trace = {} try: - template = self.env.from_string(content_str) + template = self.from_string(content_str) result = self.safe_eval(template.render(override_vars)) if isinstance(result, Undefined): - # This happens when the expression is simply an undefined variable. Jinja does not - # raise an exception, instead we get "Undefined". - # Trigger an UndefinedError exception so we skip to below. - print("" + result) + print("" + result) # force a UndefinedError exception except (TemplateSyntaxError, UndefinedError) as err: # `content_str` contains a Jinja expression that refers to a variable that is undefined # in this scope. Perhaps it refers to a root substitution that is not visible yet. - # Therefore, return the original `content_str` as a JinjaStr, which contains the variables + # Therefore, return `content_str` as a JinjaStr, which contains the variables # that are actually visible to it at this point to postpone evaluation. return JinjaStr(content_str, {**self.context_vars, **override_vars}), err + except JinjaError as err: + err.context_trace = {**self.context_trace, **err.context_trace} + err.eval_stack.append(content_str) + raise err + except ( + TemplateError, + TemplateRuntimeError, + RuntimeError, + ArithmeticError, + AttributeError, + TypeError, + ) as err: + raise JinjaError(self.context_trace, content_str) from err + finally: + self.context_trace = old_trace + + if isinstance(content_str, JinjaStr): + content_str.result = result return result, None diff --git a/tests/unit_tests/fixtures/substitutions/02-expressions.approved.yaml b/tests/unit_tests/fixtures/substitutions/02-expressions.approved.yaml index 443cba144e..1a51fc44cf 100644 --- a/tests/unit_tests/fixtures/substitutions/02-expressions.approved.yaml +++ b/tests/unit_tests/fixtures/substitutions/02-expressions.approved.yaml @@ -8,6 +8,7 @@ substitutions: area: 25 numberOne: 1 var1: 79 + double_width: 14 test_list: - The area is 56 - 56 @@ -25,3 +26,4 @@ test_list: - ord("a") = 97 - chr(97) = a - len([1,2,3]) = 3 + - width = 7, double_width = 14 diff --git a/tests/unit_tests/fixtures/substitutions/02-expressions.input.yaml b/tests/unit_tests/fixtures/substitutions/02-expressions.input.yaml index 07ad992f1f..4612f581b5 100644 --- a/tests/unit_tests/fixtures/substitutions/02-expressions.input.yaml +++ b/tests/unit_tests/fixtures/substitutions/02-expressions.input.yaml @@ -8,6 +8,7 @@ substitutions: area: 25 numberOne: 1 var1: 79 + double_width: ${width * 2} test_list: - "The area is ${width * height}" @@ -23,3 +24,4 @@ test_list: - ord("a") = ${ ord("a") } - chr(97) = ${ chr(97) } - len([1,2,3]) = ${ len([1,2,3]) } + - width = ${width}, double_width = ${double_width} From 6a183679496fb13e464ec21728e25a200df40238 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 15:26:37 -1000 Subject: [PATCH 162/526] [cli] Add `analyze-memory` command (#11395) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/__main__.py | 91 +++++++++++ tests/unit_tests/test_main.py | 275 +++++++++++++++++++++++++++++++++- 2 files changed, 358 insertions(+), 8 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 982e00f5e1..26e5ae7424 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -62,6 +62,40 @@ from esphome.util import ( _LOGGER = logging.getLogger(__name__) +# Special non-component keys that appear in configs +_NON_COMPONENT_KEYS = frozenset( + { + CONF_ESPHOME, + "substitutions", + "packages", + "globals", + "external_components", + "<<", + } +) + + +def detect_external_components(config: ConfigType) -> set[str]: + """Detect external/custom components in the configuration. + + External components are those that appear in the config but are not + part of ESPHome's built-in components and are not special config keys. + + Args: + config: The ESPHome configuration dictionary + + Returns: + A set of external component names + """ + from esphome.analyze_memory.helpers import get_esphome_components + + builtin_components = get_esphome_components() + return { + key + for key in config + if key not in builtin_components and key not in _NON_COMPONENT_KEYS + } + class ArgsProtocol(Protocol): device: list[str] | None @@ -892,6 +926,54 @@ def command_idedata(args: ArgsProtocol, config: ConfigType) -> int: return 0 +def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int: + """Analyze memory usage by component. + + This command compiles the configuration and performs memory analysis. + Compilation is fast if sources haven't changed (just relinking). + """ + from esphome import platformio_api + from esphome.analyze_memory.cli import MemoryAnalyzerCLI + + # Always compile to ensure fresh data (fast if no changes - just relinks) + exit_code = write_cpp(config) + if exit_code != 0: + return exit_code + exit_code = compile_program(args, config) + if exit_code != 0: + return exit_code + _LOGGER.info("Successfully compiled program.") + + # Get idedata for analysis + idedata = platformio_api.get_idedata(config) + if idedata is None: + _LOGGER.error("Failed to get IDE data for memory analysis") + return 1 + + firmware_elf = Path(idedata.firmware_elf_path) + + # Extract external components from config + external_components = detect_external_components(config) + _LOGGER.debug("Detected external components: %s", external_components) + + # Perform memory analysis + _LOGGER.info("Analyzing memory usage...") + analyzer = MemoryAnalyzerCLI( + str(firmware_elf), + idedata.objdump_path, + idedata.readelf_path, + external_components, + ) + analyzer.analyze() + + # Generate and display report + report = analyzer.generate_report() + print() + print(report) + + return 0 + + def command_rename(args: ArgsProtocol, config: ConfigType) -> int | None: new_name = args.name for c in new_name: @@ -1007,6 +1089,7 @@ POST_CONFIG_ACTIONS = { "idedata": command_idedata, "rename": command_rename, "discover": command_discover, + "analyze-memory": command_analyze_memory, } SIMPLE_CONFIG_ACTIONS = [ @@ -1292,6 +1375,14 @@ def parse_args(argv): ) parser_rename.add_argument("name", help="The new name for the device.", type=str) + parser_analyze_memory = subparsers.add_parser( + "analyze-memory", + help="Analyze memory usage by component.", + ) + parser_analyze_memory.add_argument( + "configuration", help="Your YAML configuration file(s).", nargs="+" + ) + # Keep backward compatibility with the old command line format of # esphome . # diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index 73dfe359f0..9119c88502 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -17,10 +17,12 @@ from esphome import platformio_api from esphome.__main__ import ( Purpose, choose_upload_log_host, + command_analyze_memory, command_clean_all, command_rename, command_update_all, command_wizard, + detect_external_components, get_port_type, has_ip_address, has_mqtt, @@ -226,13 +228,47 @@ def mock_run_external_process() -> Generator[Mock]: @pytest.fixture -def mock_run_external_command() -> Generator[Mock]: - """Mock run_external_command for testing.""" +def mock_run_external_command_main() -> Generator[Mock]: + """Mock run_external_command in __main__ module (different from platformio_api).""" with patch("esphome.__main__.run_external_command") as mock: mock.return_value = 0 # Default to success yield mock +@pytest.fixture +def mock_write_cpp() -> Generator[Mock]: + """Mock write_cpp for testing.""" + with patch("esphome.__main__.write_cpp") as mock: + mock.return_value = 0 # Default to success + yield mock + + +@pytest.fixture +def mock_compile_program() -> Generator[Mock]: + """Mock compile_program for testing.""" + with patch("esphome.__main__.compile_program") as mock: + mock.return_value = 0 # Default to success + yield mock + + +@pytest.fixture +def mock_get_esphome_components() -> Generator[Mock]: + """Mock get_esphome_components for testing.""" + with patch("esphome.analyze_memory.helpers.get_esphome_components") as mock: + mock.return_value = {"logger", "api", "ota"} + yield mock + + +@pytest.fixture +def mock_memory_analyzer_cli() -> Generator[Mock]: + """Mock MemoryAnalyzerCLI for testing.""" + with patch("esphome.analyze_memory.cli.MemoryAnalyzerCLI") as mock_class: + mock_analyzer = MagicMock() + mock_analyzer.generate_report.return_value = "Mock Memory Report" + mock_class.return_value = mock_analyzer + yield mock_class + + def test_choose_upload_log_host_with_string_default() -> None: """Test with a single string default device.""" setup_core() @@ -839,7 +875,7 @@ def test_upload_program_serial_esp8266_with_file( def test_upload_using_esptool_path_conversion( tmp_path: Path, - mock_run_external_command: Mock, + mock_run_external_command_main: Mock, mock_get_idedata: Mock, ) -> None: """Test upload_using_esptool properly converts Path objects to strings for esptool. @@ -875,10 +911,10 @@ def test_upload_using_esptool_path_conversion( assert result == 0 # Verify that run_external_command was called - assert mock_run_external_command.call_count == 1 + assert mock_run_external_command_main.call_count == 1 # Get the actual call arguments - call_args = mock_run_external_command.call_args[0] + call_args = mock_run_external_command_main.call_args[0] # The first argument should be esptool.main function, # followed by the command arguments @@ -917,7 +953,7 @@ def test_upload_using_esptool_path_conversion( def test_upload_using_esptool_with_file_path( tmp_path: Path, - mock_run_external_command: Mock, + mock_run_external_command_main: Mock, ) -> None: """Test upload_using_esptool with a custom file that's a Path object.""" setup_core(platform=PLATFORM_ESP8266, tmp_path=tmp_path, name="test") @@ -934,10 +970,10 @@ def test_upload_using_esptool_with_file_path( assert result == 0 # Verify that run_external_command was called - mock_run_external_command.assert_called_once() + mock_run_external_command_main.assert_called_once() # Get the actual call arguments - call_args = mock_run_external_command.call_args[0] + call_args = mock_run_external_command_main.call_args[0] cmd_list = list(call_args[1:]) # Skip the esptool.main function # Find the firmware path in the command @@ -2273,3 +2309,226 @@ def test_show_logs_api_mqtt_timeout_fallback( # Verify run_logs was called with only the static IP (MQTT failed) mock_run_logs.assert_called_once_with(CORE.config, ["192.168.1.100"]) + + +def test_detect_external_components_no_external( + mock_get_esphome_components: Mock, +) -> None: + """Test detect_external_components with no external components.""" + config = { + CONF_ESPHOME: {CONF_NAME: "test_device"}, + "logger": {}, + "api": {}, + } + + result = detect_external_components(config) + + assert result == set() + mock_get_esphome_components.assert_called_once() + + +def test_detect_external_components_with_external( + mock_get_esphome_components: Mock, +) -> None: + """Test detect_external_components detects external components.""" + config = { + CONF_ESPHOME: {CONF_NAME: "test_device"}, + "logger": {}, # Built-in + "api": {}, # Built-in + "my_custom_sensor": {}, # External + "another_custom": {}, # External + "external_components": [], # Special key, not a component + "substitutions": {}, # Special key, not a component + } + + result = detect_external_components(config) + + assert result == {"my_custom_sensor", "another_custom"} + mock_get_esphome_components.assert_called_once() + + +def test_detect_external_components_filters_special_keys( + mock_get_esphome_components: Mock, +) -> None: + """Test detect_external_components filters out special config keys.""" + config = { + CONF_ESPHOME: {CONF_NAME: "test_device"}, + "substitutions": {"key": "value"}, + "packages": {}, + "globals": [], + "external_components": [], + "<<": {}, # YAML merge key + } + + result = detect_external_components(config) + + assert result == set() + mock_get_esphome_components.assert_called_once() + + +def test_command_analyze_memory_success( + tmp_path: Path, + capfd: CaptureFixture[str], + mock_write_cpp: Mock, + mock_compile_program: Mock, + mock_get_idedata: Mock, + mock_get_esphome_components: Mock, + mock_memory_analyzer_cli: Mock, +) -> None: + """Test command_analyze_memory with successful compilation and analysis.""" + setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path, name="test_device") + + # Create firmware.elf file + firmware_path = ( + tmp_path / ".esphome" / "build" / "test_device" / ".pioenvs" / "test_device" + ) + firmware_path.mkdir(parents=True, exist_ok=True) + firmware_elf = firmware_path / "firmware.elf" + firmware_elf.write_text("mock elf file") + + # Mock idedata + mock_idedata_obj = MagicMock(spec=platformio_api.IDEData) + mock_idedata_obj.firmware_elf_path = str(firmware_elf) + mock_idedata_obj.objdump_path = "/path/to/objdump" + mock_idedata_obj.readelf_path = "/path/to/readelf" + mock_get_idedata.return_value = mock_idedata_obj + + config = { + CONF_ESPHOME: {CONF_NAME: "test_device"}, + "logger": {}, + } + + args = MockArgs() + + result = command_analyze_memory(args, config) + + assert result == 0 + + # Verify compilation was done + mock_write_cpp.assert_called_once_with(config) + mock_compile_program.assert_called_once_with(args, config) + + # Verify analyzer was created with correct parameters + mock_memory_analyzer_cli.assert_called_once_with( + str(firmware_elf), + "/path/to/objdump", + "/path/to/readelf", + set(), # No external components + ) + + # Verify analysis was run + mock_analyzer = mock_memory_analyzer_cli.return_value + mock_analyzer.analyze.assert_called_once() + mock_analyzer.generate_report.assert_called_once() + + # Verify report was printed + captured = capfd.readouterr() + assert "Mock Memory Report" in captured.out + + +def test_command_analyze_memory_with_external_components( + tmp_path: Path, + mock_write_cpp: Mock, + mock_compile_program: Mock, + mock_get_idedata: Mock, + mock_get_esphome_components: Mock, + mock_memory_analyzer_cli: Mock, +) -> None: + """Test command_analyze_memory detects external components.""" + setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path, name="test_device") + + # Create firmware.elf file + firmware_path = ( + tmp_path / ".esphome" / "build" / "test_device" / ".pioenvs" / "test_device" + ) + firmware_path.mkdir(parents=True, exist_ok=True) + firmware_elf = firmware_path / "firmware.elf" + firmware_elf.write_text("mock elf file") + + # Mock idedata + mock_idedata_obj = MagicMock(spec=platformio_api.IDEData) + mock_idedata_obj.firmware_elf_path = str(firmware_elf) + mock_idedata_obj.objdump_path = "/path/to/objdump" + mock_idedata_obj.readelf_path = "/path/to/readelf" + mock_get_idedata.return_value = mock_idedata_obj + + config = { + CONF_ESPHOME: {CONF_NAME: "test_device"}, + "logger": {}, + "my_custom_component": {"param": "value"}, # External component + "external_components": [{"source": "github://user/repo"}], # Not a component + } + + args = MockArgs() + + result = command_analyze_memory(args, config) + + assert result == 0 + + # Verify analyzer was created with external components detected + mock_memory_analyzer_cli.assert_called_once_with( + str(firmware_elf), + "/path/to/objdump", + "/path/to/readelf", + {"my_custom_component"}, # External component detected + ) + + +def test_command_analyze_memory_write_cpp_fails( + tmp_path: Path, + mock_write_cpp: Mock, +) -> None: + """Test command_analyze_memory when write_cpp fails.""" + setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path, name="test_device") + + config = {CONF_ESPHOME: {CONF_NAME: "test_device"}} + args = MockArgs() + + mock_write_cpp.return_value = 1 # Failure + + result = command_analyze_memory(args, config) + + assert result == 1 + mock_write_cpp.assert_called_once_with(config) + + +def test_command_analyze_memory_compile_fails( + tmp_path: Path, + mock_write_cpp: Mock, + mock_compile_program: Mock, +) -> None: + """Test command_analyze_memory when compilation fails.""" + setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path, name="test_device") + + config = {CONF_ESPHOME: {CONF_NAME: "test_device"}} + args = MockArgs() + + mock_compile_program.return_value = 1 # Compilation failed + + result = command_analyze_memory(args, config) + + assert result == 1 + mock_write_cpp.assert_called_once_with(config) + mock_compile_program.assert_called_once_with(args, config) + + +def test_command_analyze_memory_no_idedata( + tmp_path: Path, + caplog: pytest.LogCaptureFixture, + mock_write_cpp: Mock, + mock_compile_program: Mock, + mock_get_idedata: Mock, +) -> None: + """Test command_analyze_memory when idedata cannot be retrieved.""" + setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path, name="test_device") + + config = {CONF_ESPHOME: {CONF_NAME: "test_device"}} + args = MockArgs() + + mock_get_idedata.return_value = None # Failed to get idedata + + with caplog.at_level(logging.ERROR): + result = command_analyze_memory(args, config) + + assert result == 1 + assert "Failed to get IDE data for memory analysis" in caplog.text From 11b53096a6544e8332b6dcc4d894b096d3365b64 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 15:58:05 -1000 Subject: [PATCH 163/526] [ci] Fix fork PR workflow failing to find PRs from forks (#11396) --- .../workflows/ci-memory-impact-comment.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-memory-impact-comment.yml b/.github/workflows/ci-memory-impact-comment.yml index 4ce7abfb85..eea1d2c148 100644 --- a/.github/workflows/ci-memory-impact-comment.yml +++ b/.github/workflows/ci-memory-impact-comment.yml @@ -28,20 +28,23 @@ jobs: run: | # Get PR details by searching for PR with matching head SHA # The workflow_run.pull_requests field is often empty for forks + # Use paginate to handle repos with many open PRs head_sha="${{ github.event.workflow_run.head_sha }}" - pr_data=$(gh api "/repos/${{ github.repository }}/commits/$head_sha/pulls" \ - --jq '.[0] | {number: .number, base_ref: .base.ref}') - if [ -z "$pr_data" ] || [ "$pr_data" == "null" ]; then + pr_data=$(gh api --paginate "/repos/${{ github.repository }}/pulls" \ + --jq ".[] | select(.head.sha == \"$head_sha\") | {number: .number, base_ref: .base.ref}" \ + | head -n 1) + + if [ -z "$pr_data" ]; then echo "No PR found for SHA $head_sha, skipping" - echo "skip=true" >> $GITHUB_OUTPUT + echo "skip=true" >> "$GITHUB_OUTPUT" exit 0 fi pr_number=$(echo "$pr_data" | jq -r '.number') base_ref=$(echo "$pr_data" | jq -r '.base_ref') - echo "pr_number=$pr_number" >> $GITHUB_OUTPUT - echo "base_ref=$base_ref" >> $GITHUB_OUTPUT + echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT" + echo "base_ref=$base_ref" >> "$GITHUB_OUTPUT" echo "Found PR #$pr_number targeting base branch: $base_ref" - name: Check out code from base repository @@ -87,9 +90,9 @@ jobs: if: steps.pr.outputs.skip != 'true' run: | if [ -f ./memory-analysis/memory-analysis-target.json ] && [ -f ./memory-analysis/memory-analysis-pr.json ]; then - echo "found=true" >> $GITHUB_OUTPUT + echo "found=true" >> "$GITHUB_OUTPUT" else - echo "found=false" >> $GITHUB_OUTPUT + echo "found=false" >> "$GITHUB_OUTPUT" echo "Memory analysis artifacts not found, skipping comment" fi From c15f1a9be88be6529b6fb952fd0ec854ceb21502 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 20 Oct 2025 04:11:44 +0200 Subject: [PATCH 164/526] [nrf52] add missing defines for tests (#11384) Co-authored-by: J. Nick Koston --- esphome/core/defines.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index b1bd7f92d7..ff9afb9114 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -273,6 +273,8 @@ #ifdef USE_NRF52 #define USE_NRF52_DFU +#define USE_SOFTDEVICE_ID 7 +#define USE_SOFTDEVICE_VERSION 1 #endif // Disabled feature flags From 8f1c4634ecfd815629b62e66c2ebb192a4c38bf3 Mon Sep 17 00:00:00 2001 From: Stefan Rado <628587+kroimon@users.noreply.github.com> Date: Mon, 20 Oct 2025 04:49:06 +0200 Subject: [PATCH 165/526] [uponor_smatrix] Use combined 32 bit addresses instead of separate 16 bit system and device addresses (#11066) Co-authored-by: J. Nick Koston --- esphome/components/uponor_smatrix/__init__.py | 23 ++++++++----- .../climate/uponor_smatrix_climate.cpp | 2 +- .../sensor/uponor_smatrix_sensor.cpp | 2 +- .../uponor_smatrix/uponor_smatrix.cpp | 34 ++++++------------- .../uponor_smatrix/uponor_smatrix.h | 14 ++++---- tests/components/uponor_smatrix/common.yaml | 7 ++-- 6 files changed, 37 insertions(+), 45 deletions(-) diff --git a/esphome/components/uponor_smatrix/__init__.py b/esphome/components/uponor_smatrix/__init__.py index d4102d1026..9588b0df7f 100644 --- a/esphome/components/uponor_smatrix/__init__.py +++ b/esphome/components/uponor_smatrix/__init__.py @@ -17,6 +17,12 @@ UponorSmatrixDevice = uponor_smatrix_ns.class_( "UponorSmatrixDevice", cg.Parented.template(UponorSmatrixComponent) ) + +device_address = cv.All( + cv.hex_int, + cv.Range(min=0x1000000, max=0xFFFFFFFF, msg="Expected a 32 bit device address"), +) + CONF_UPONOR_SMATRIX_ID = "uponor_smatrix_id" CONF_TIME_DEVICE_ADDRESS = "time_device_address" @@ -24,9 +30,12 @@ CONFIG_SCHEMA = ( cv.Schema( { cv.GenerateID(): cv.declare_id(UponorSmatrixComponent), - cv.Optional(CONF_ADDRESS): cv.hex_uint16_t, + cv.Optional(CONF_ADDRESS): cv.invalid( + f"The '{CONF_ADDRESS}' option has been removed. " + "Use full 32 bit addresses in the device definitions instead." + ), cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock), - cv.Optional(CONF_TIME_DEVICE_ADDRESS): cv.hex_uint16_t, + cv.Optional(CONF_TIME_DEVICE_ADDRESS): device_address, } ) .extend(cv.COMPONENT_SCHEMA) @@ -47,7 +56,7 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( UPONOR_SMATRIX_DEVICE_SCHEMA = cv.Schema( { cv.GenerateID(CONF_UPONOR_SMATRIX_ID): cv.use_id(UponorSmatrixComponent), - cv.Required(CONF_ADDRESS): cv.hex_uint16_t, + cv.Required(CONF_ADDRESS): device_address, } ) @@ -58,17 +67,15 @@ async def to_code(config): await cg.register_component(var, config) await uart.register_uart_device(var, config) - if address := config.get(CONF_ADDRESS): - cg.add(var.set_system_address(address)) if time_id := config.get(CONF_TIME_ID): time_ = await cg.get_variable(time_id) cg.add(var.set_time_id(time_)) - if time_device_address := config.get(CONF_TIME_DEVICE_ADDRESS): - cg.add(var.set_time_device_address(time_device_address)) + if time_device_address := config.get(CONF_TIME_DEVICE_ADDRESS): + cg.add(var.set_time_device_address(time_device_address)) async def register_uponor_smatrix_device(var, config): parent = await cg.get_variable(config[CONF_UPONOR_SMATRIX_ID]) cg.add(var.set_parent(parent)) - cg.add(var.set_device_address(config[CONF_ADDRESS])) + cg.add(var.set_address(config[CONF_ADDRESS])) cg.add(parent.register_device(var)) diff --git a/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp b/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp index 19a9112c73..8af106dfb7 100644 --- a/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +++ b/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp @@ -10,7 +10,7 @@ static const char *const TAG = "uponor_smatrix.climate"; void UponorSmatrixClimate::dump_config() { LOG_CLIMATE("", "Uponor Smatrix Climate", this); - ESP_LOGCONFIG(TAG, " Device address: 0x%04X", this->address_); + ESP_LOGCONFIG(TAG, " Device address: 0x%08X", this->address_); } void UponorSmatrixClimate::loop() { diff --git a/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp b/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp index a1d0db214f..7ee12edcdb 100644 --- a/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +++ b/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp @@ -9,7 +9,7 @@ static const char *const TAG = "uponor_smatrix.sensor"; void UponorSmatrixSensor::dump_config() { ESP_LOGCONFIG(TAG, "Uponor Smatrix Sensor\n" - " Device address: 0x%04X", + " Device address: 0x%08X", this->address_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "External Temperature", this->external_temperature_sensor_); diff --git a/esphome/components/uponor_smatrix/uponor_smatrix.cpp b/esphome/components/uponor_smatrix/uponor_smatrix.cpp index 867305059f..221f07c80e 100644 --- a/esphome/components/uponor_smatrix/uponor_smatrix.cpp +++ b/esphome/components/uponor_smatrix/uponor_smatrix.cpp @@ -18,11 +18,10 @@ void UponorSmatrixComponent::setup() { void UponorSmatrixComponent::dump_config() { ESP_LOGCONFIG(TAG, "Uponor Smatrix"); - ESP_LOGCONFIG(TAG, " System address: 0x%04X", this->address_); #ifdef USE_TIME if (this->time_id_ != nullptr) { ESP_LOGCONFIG(TAG, " Time synchronization: YES"); - ESP_LOGCONFIG(TAG, " Time master device address: 0x%04X", this->time_device_address_); + ESP_LOGCONFIG(TAG, " Time master device address: 0x%08X", this->time_device_address_); } #endif @@ -31,7 +30,7 @@ void UponorSmatrixComponent::dump_config() { if (!this->unknown_devices_.empty()) { ESP_LOGCONFIG(TAG, " Detected unknown device addresses:"); for (auto device_address : this->unknown_devices_) { - ESP_LOGCONFIG(TAG, " 0x%04X", device_address); + ESP_LOGCONFIG(TAG, " 0x%08X", device_address); } } } @@ -89,8 +88,7 @@ bool UponorSmatrixComponent::parse_byte_(uint8_t byte) { return false; } - uint16_t system_address = encode_uint16(packet[0], packet[1]); - uint16_t device_address = encode_uint16(packet[2], packet[3]); + uint32_t device_address = encode_uint32(packet[0], packet[1], packet[2], packet[3]); uint16_t crc = encode_uint16(packet[packet_len - 1], packet[packet_len - 2]); uint16_t computed_crc = crc16(packet, packet_len - 2); @@ -99,24 +97,14 @@ bool UponorSmatrixComponent::parse_byte_(uint8_t byte) { return false; } - ESP_LOGV(TAG, "Received packet: sys=%04X, dev=%04X, data=%s, crc=%04X", system_address, device_address, + ESP_LOGV(TAG, "Received packet: addr=%08X, data=%s, crc=%04X", device_address, format_hex(&packet[4], packet_len - 6).c_str(), crc); - // Detect or check system address - if (this->address_ == 0) { - ESP_LOGI(TAG, "Using detected system address 0x%04X", system_address); - this->address_ = system_address; - } else if (this->address_ != system_address) { - // This should never happen except if the system address was set or detected incorrectly, so warn the user. - ESP_LOGW(TAG, "Received packet from unknown system address 0x%04X", system_address); - return true; - } - // Handle packet size_t data_len = (packet_len - 6) / 3; if (data_len == 0) { if (packet[4] == UPONOR_ID_REQUEST) - ESP_LOGVV(TAG, "Ignoring request packet for device 0x%04X", device_address); + ESP_LOGVV(TAG, "Ignoring request packet for device 0x%08X", device_address); return true; } @@ -141,7 +129,7 @@ bool UponorSmatrixComponent::parse_byte_(uint8_t byte) { if (data[i].id == UPONOR_ID_DATETIME1) found_time = true; if (found_temperature && found_time) { - ESP_LOGI(TAG, "Using detected time device address 0x%04X", device_address); + ESP_LOGI(TAG, "Using detected time device address 0x%08X", device_address); this->time_device_address_ = device_address; break; } @@ -160,7 +148,7 @@ bool UponorSmatrixComponent::parse_byte_(uint8_t byte) { // Log unknown device addresses if (!found && !this->unknown_devices_.count(device_address)) { - ESP_LOGI(TAG, "Received packet for unknown device address 0x%04X ", device_address); + ESP_LOGI(TAG, "Received packet for unknown device address 0x%08X ", device_address); this->unknown_devices_.insert(device_address); } @@ -168,16 +156,16 @@ bool UponorSmatrixComponent::parse_byte_(uint8_t byte) { return true; } -bool UponorSmatrixComponent::send(uint16_t device_address, const UponorSmatrixData *data, size_t data_len) { - if (this->address_ == 0 || device_address == 0 || data == nullptr || data_len == 0) +bool UponorSmatrixComponent::send(uint32_t device_address, const UponorSmatrixData *data, size_t data_len) { + if (device_address == 0 || data == nullptr || data_len == 0) return false; // Assemble packet for send queue. All fields are big-endian except for the little-endian checksum. std::vector packet; packet.reserve(6 + 3 * data_len); - packet.push_back(this->address_ >> 8); - packet.push_back(this->address_ >> 0); + packet.push_back(device_address >> 24); + packet.push_back(device_address >> 16); packet.push_back(device_address >> 8); packet.push_back(device_address >> 0); diff --git a/esphome/components/uponor_smatrix/uponor_smatrix.h b/esphome/components/uponor_smatrix/uponor_smatrix.h index e3e19a12fc..bd760f0d77 100644 --- a/esphome/components/uponor_smatrix/uponor_smatrix.h +++ b/esphome/components/uponor_smatrix/uponor_smatrix.h @@ -71,23 +71,21 @@ class UponorSmatrixComponent : public uart::UARTDevice, public Component { void dump_config() override; void loop() override; - void set_system_address(uint16_t address) { this->address_ = address; } void register_device(UponorSmatrixDevice *device) { this->devices_.push_back(device); } - bool send(uint16_t device_address, const UponorSmatrixData *data, size_t data_len); + bool send(uint32_t device_address, const UponorSmatrixData *data, size_t data_len); #ifdef USE_TIME void set_time_id(time::RealTimeClock *time_id) { this->time_id_ = time_id; } - void set_time_device_address(uint16_t address) { this->time_device_address_ = address; } + void set_time_device_address(uint32_t address) { this->time_device_address_ = address; } void send_time() { this->send_time_requested_ = true; } #endif protected: bool parse_byte_(uint8_t byte); - uint16_t address_; std::vector devices_; - std::set unknown_devices_; + std::set unknown_devices_; std::vector rx_buffer_; std::queue> tx_queue_; @@ -96,7 +94,7 @@ class UponorSmatrixComponent : public uart::UARTDevice, public Component { #ifdef USE_TIME time::RealTimeClock *time_id_{nullptr}; - uint16_t time_device_address_; + uint32_t time_device_address_; bool send_time_requested_; bool do_send_time_(); #endif @@ -104,7 +102,7 @@ class UponorSmatrixComponent : public uart::UARTDevice, public Component { class UponorSmatrixDevice : public Parented { public: - void set_device_address(uint16_t address) { this->address_ = address; } + void set_address(uint32_t address) { this->address_ = address; } virtual void on_device_data(const UponorSmatrixData *data, size_t data_len) = 0; bool send(const UponorSmatrixData *data, size_t data_len) { @@ -113,7 +111,7 @@ class UponorSmatrixDevice : public Parented { protected: friend UponorSmatrixComponent; - uint16_t address_; + uint32_t address_; }; inline float raw_to_celsius(uint16_t raw) { diff --git a/tests/components/uponor_smatrix/common.yaml b/tests/components/uponor_smatrix/common.yaml index 786a604aec..7bb5e952ad 100644 --- a/tests/components/uponor_smatrix/common.yaml +++ b/tests/components/uponor_smatrix/common.yaml @@ -11,18 +11,17 @@ time: - 192.168.178.1 uponor_smatrix: - address: 0x110B time_id: sntp_time - time_device_address: 0xDE13 + time_device_address: 0x110BDE13 climate: - platform: uponor_smatrix - address: 0xDE13 + address: 0x110BDE13 name: Thermostat Living Room sensor: - platform: uponor_smatrix - address: 0xDE13 + address: 0x110BDE13 humidity: name: Thermostat Humidity Living Room temperature: From 22fec4329f30877c69b1e43c984f4692f5b97a2f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:02:03 +1300 Subject: [PATCH 166/526] [fan] Clean up deprecated code from 2022.2 (#11392) --- esphome/components/fan/__init__.py | 1 - esphome/components/fan/automation.h | 4 ++-- esphome/components/fan/fan_state.cpp | 16 ------------- esphome/components/fan/fan_state.h | 34 ---------------------------- esphome/components/mqtt/mqtt_fan.h | 2 +- esphome/core/application.h | 2 +- esphome/core/controller.h | 2 +- 7 files changed, 5 insertions(+), 56 deletions(-) delete mode 100644 esphome/components/fan/fan_state.cpp delete mode 100644 esphome/components/fan/fan_state.h diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index da8bf850c7..245c9f04b4 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -38,7 +38,6 @@ IS_PLATFORM_COMPONENT = True fan_ns = cg.esphome_ns.namespace("fan") Fan = fan_ns.class_("Fan", cg.EntityBase) -FanState = fan_ns.class_("Fan", Fan, cg.Component) FanDirection = fan_ns.enum("FanDirection", is_class=True) FAN_DIRECTION_ENUM = { diff --git a/esphome/components/fan/automation.h b/esphome/components/fan/automation.h index d480a2ef44..90661c307c 100644 --- a/esphome/components/fan/automation.h +++ b/esphome/components/fan/automation.h @@ -1,8 +1,8 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/core/automation.h" -#include "fan_state.h" +#include "esphome/core/component.h" +#include "fan.h" namespace esphome { namespace fan { diff --git a/esphome/components/fan/fan_state.cpp b/esphome/components/fan/fan_state.cpp deleted file mode 100644 index 7c1658fb2e..0000000000 --- a/esphome/components/fan/fan_state.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "fan_state.h" - -namespace esphome { -namespace fan { - -static const char *const TAG = "fan"; - -void FanState::setup() { - auto restore = this->restore_state_(); - if (restore) - restore->to_call(*this).perform(); -} -float FanState::get_setup_priority() const { return setup_priority::DATA - 1.0f; } - -} // namespace fan -} // namespace esphome diff --git a/esphome/components/fan/fan_state.h b/esphome/components/fan/fan_state.h deleted file mode 100644 index 5926e700b0..0000000000 --- a/esphome/components/fan/fan_state.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "fan.h" - -namespace esphome { -namespace fan { - -enum ESPDEPRECATED("LegacyFanDirection members are deprecated, use FanDirection instead.", - "2022.2") LegacyFanDirection { - FAN_DIRECTION_FORWARD = 0, - FAN_DIRECTION_REVERSE = 1 -}; - -class ESPDEPRECATED("FanState is deprecated, use Fan instead.", "2022.2") FanState : public Fan, public Component { - public: - FanState() = default; - - /// Get the traits of this fan. - FanTraits get_traits() override { return this->traits_; } - /// Set the traits of this fan (i.e. what features it supports). - void set_traits(const FanTraits &traits) { this->traits_ = traits; } - - void setup() override; - float get_setup_priority() const override; - - protected: - void control(const FanCall &call) override { this->publish_state(); } - - FanTraits traits_{}; -}; - -} // namespace fan -} // namespace esphome diff --git a/esphome/components/mqtt/mqtt_fan.h b/esphome/components/mqtt/mqtt_fan.h index fdcec0782d..78641d224f 100644 --- a/esphome/components/mqtt/mqtt_fan.h +++ b/esphome/components/mqtt/mqtt_fan.h @@ -5,7 +5,7 @@ #ifdef USE_MQTT #ifdef USE_FAN -#include "esphome/components/fan/fan_state.h" +#include "esphome/components/fan/fan.h" #include "mqtt_component.h" namespace esphome { diff --git a/esphome/core/application.h b/esphome/core/application.h index 6e7f1b49f2..29a734f000 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -39,7 +39,7 @@ #include "esphome/components/text_sensor/text_sensor.h" #endif #ifdef USE_FAN -#include "esphome/components/fan/fan_state.h" +#include "esphome/components/fan/fan.h" #endif #ifdef USE_CLIMATE #include "esphome/components/climate/climate.h" diff --git a/esphome/core/controller.h b/esphome/core/controller.h index 1a5b9ea6b4..b475e326ee 100644 --- a/esphome/core/controller.h +++ b/esphome/core/controller.h @@ -5,7 +5,7 @@ #include "esphome/components/binary_sensor/binary_sensor.h" #endif #ifdef USE_FAN -#include "esphome/components/fan/fan_state.h" +#include "esphome/components/fan/fan.h" #endif #ifdef USE_LIGHT #include "esphome/components/light/light_state.h" From dd732dd1554d7f70dbee4fe9d5c0e2a72a1a94b7 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 20 Oct 2025 13:09:36 +1000 Subject: [PATCH 167/526] [mipi_rgb] Add Waveshare 5" 1024x600 (#11206) --- esphome/components/mipi_rgb/models/waveshare.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/esphome/components/mipi_rgb/models/waveshare.py b/esphome/components/mipi_rgb/models/waveshare.py index a38493e816..0fc765fd52 100644 --- a/esphome/components/mipi_rgb/models/waveshare.py +++ b/esphome/components/mipi_rgb/models/waveshare.py @@ -30,6 +30,19 @@ wave_4_3 = DriverChip( "blue": [14, 38, 18, 17, 10], }, ) + +wave_4_3.extend( + "WAVESHARE-5-1024X600", + width=1024, + height=600, + hsync_back_porch=145, + hsync_front_porch=170, + hsync_pulse_width=30, + vsync_back_porch=23, + vsync_front_porch=12, + vsync_pulse_width=2, +) + wave_4_3.extend( "ESP32-S3-TOUCH-LCD-7-800X480", enable_pin=[{"ch422g": None, "number": 2}, {"ch422g": None, "number": 6}], From 255b5a3abdc435f7442a3e64d13e6ec18f7fb92b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 17:13:08 -1000 Subject: [PATCH 168/526] [ci] Skip memory analysis when only Python/config files change in core (#11397) --- script/determine-jobs.py | 20 ++++++++++------- tests/script/test_determine_jobs.py | 33 +++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 570b1a762c..a0e04a256e 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -273,6 +273,9 @@ def detect_memory_impact_config( building a merged configuration with all changed components (like test_build_components.py does) to get comprehensive memory analysis. + For core C++ file changes without component changes, runs a fallback + analysis using a representative component to measure the impact. + Args: branch: Branch to compare against @@ -289,7 +292,7 @@ def detect_memory_impact_config( # Find all changed components (excluding core and base bus components) changed_component_set: set[str] = set() - has_core_changes = False + has_core_cpp_changes = False for file in files: component = get_component_from_path(file) @@ -297,22 +300,23 @@ def detect_memory_impact_config( # Skip base bus components as they're used across many builds if component not in BASE_BUS_COMPONENTS: changed_component_set.add(component) - elif file.startswith("esphome/"): - # Core ESPHome files changed (not component-specific) - has_core_changes = True + elif file.startswith("esphome/") and file.endswith(CPP_FILE_EXTENSIONS): + # Core ESPHome C++ files changed (not component-specific) + # Only C++ files affect memory usage + has_core_cpp_changes = True - # If no components changed but core changed, test representative component + # If no components changed but core C++ changed, test representative component force_fallback_platform = False - if not changed_component_set and has_core_changes: + if not changed_component_set and has_core_cpp_changes: print( - f"Memory impact: No components changed, but core files changed. " + f"Memory impact: No components changed, but core C++ files changed. " f"Testing {MEMORY_IMPACT_FALLBACK_COMPONENT} component on {MEMORY_IMPACT_FALLBACK_PLATFORM}.", file=sys.stderr, ) changed_component_set.add(MEMORY_IMPACT_FALLBACK_COMPONENT) force_fallback_platform = True # Use fallback platform (most representative) elif not changed_component_set: - # No components and no core changes + # No components and no core C++ changes return {"should_run": "false"} # Find components that have tests and collect their supported platforms diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index b479fc03c5..7587dbee69 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -545,7 +545,7 @@ def test_detect_memory_impact_config_with_common_platform(tmp_path: Path) -> Non def test_detect_memory_impact_config_core_only_changes(tmp_path: Path) -> None: - """Test memory impact detection with core-only changes (no component changes).""" + """Test memory impact detection with core C++ changes (no component changes).""" # Create test directory structure with fallback component tests_dir = tmp_path / "tests" / "components" @@ -554,7 +554,7 @@ def test_detect_memory_impact_config_core_only_changes(tmp_path: Path) -> None: api_dir.mkdir(parents=True) (api_dir / "test.esp32-idf.yaml").write_text("test: api") - # Mock changed_files to return only core files (no component files) + # Mock changed_files to return only core C++ files (no component files) with ( patch.object(determine_jobs, "root_path", str(tmp_path)), patch.object(helpers, "root_path", str(tmp_path)), @@ -574,6 +574,35 @@ def test_detect_memory_impact_config_core_only_changes(tmp_path: Path) -> None: assert result["use_merged_config"] == "true" +def test_detect_memory_impact_config_core_python_only_changes(tmp_path: Path) -> None: + """Test that Python-only core changes don't trigger memory impact analysis.""" + # Create test directory structure with fallback component + tests_dir = tmp_path / "tests" / "components" + + # api component (fallback component) with esp32-idf test + api_dir = tests_dir / "api" + api_dir.mkdir(parents=True) + (api_dir / "test.esp32-idf.yaml").write_text("test: api") + + # Mock changed_files to return only core Python files (no C++ files) + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + ): + mock_changed_files.return_value = [ + "esphome/__main__.py", + "esphome/config.py", + "esphome/core/config.py", + ] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + # Python-only changes should NOT trigger memory impact analysis + assert result["should_run"] == "false" + + def test_detect_memory_impact_config_no_common_platform(tmp_path: Path) -> None: """Test memory impact detection when components have no common platform.""" # Create test directory structure From c00977df54bbb158dd66dd00d4320b55cfbcb24e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 17:27:04 -1000 Subject: [PATCH 169/526] [climate] Add basic compile tests for climate component (#11404) --- tests/components/climate/common.yaml | 31 +++++++++++++++++++ .../components/climate/test.esp8266-ard.yaml | 1 + 2 files changed, 32 insertions(+) create mode 100644 tests/components/climate/common.yaml create mode 100644 tests/components/climate/test.esp8266-ard.yaml diff --git a/tests/components/climate/common.yaml b/tests/components/climate/common.yaml new file mode 100644 index 0000000000..ff405b68e2 --- /dev/null +++ b/tests/components/climate/common.yaml @@ -0,0 +1,31 @@ +switch: + - platform: template + id: climate_heater_switch + optimistic: true + - platform: template + id: climate_cooler_switch + optimistic: true + +sensor: + - platform: template + id: climate_temperature_sensor + lambda: |- + return 21.5; + update_interval: 60s + +climate: + - platform: bang_bang + id: climate_test_climate + name: Test Climate + sensor: climate_temperature_sensor + default_target_temperature_low: 18°C + default_target_temperature_high: 24°C + idle_action: + - switch.turn_off: climate_heater_switch + - switch.turn_off: climate_cooler_switch + cool_action: + - switch.turn_on: climate_cooler_switch + - switch.turn_off: climate_heater_switch + heat_action: + - switch.turn_on: climate_heater_switch + - switch.turn_off: climate_cooler_switch diff --git a/tests/components/climate/test.esp8266-ard.yaml b/tests/components/climate/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/climate/test.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 1b38518c6373a16daae8c4b1081591770dfab51e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 17:45:44 -1000 Subject: [PATCH 170/526] [tests] Fix flaky test_noise_corrupt_encrypted_frame integration test (#11405) --- tests/integration/test_oversized_payloads.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/integration/test_oversized_payloads.py b/tests/integration/test_oversized_payloads.py index ba18e3d348..8bf890261a 100644 --- a/tests/integration/test_oversized_payloads.py +++ b/tests/integration/test_oversized_payloads.py @@ -281,8 +281,12 @@ async def test_noise_corrupt_encrypted_frame( # Check for signs that the process exited/crashed if "Segmentation fault" in line or "core dumped" in line: process_exited = True - # Check for the expected warning about decryption failure + # Check for the expected log about decryption failure + # This can appear as either a VV-level log from noise or a W-level log from connection if ( + "[VV][api.noise" in line + and "noise_cipherstate_decrypt failed: MAC_FAILURE" in line + ) or ( "[W][api.connection" in line and "Reading failed CIPHERSTATE_DECRYPT_FAILED" in line ): @@ -322,9 +326,9 @@ async def test_noise_corrupt_encrypted_frame( assert not process_exited, ( "ESPHome process should not crash on corrupt encrypted frames" ) - # Verify we saw the expected warning message + # Verify we saw the expected log message about decryption failure assert cipherstate_failed, ( - "Expected to see warning about CIPHERSTATE_DECRYPT_FAILED" + "Expected to see log about noise_cipherstate_decrypt failure or CIPHERSTATE_DECRYPT_FAILED" ) # Verify we can still reconnect after handling the corrupt frame From ae8336c26802fe5798a9dadd691d02edfb33564c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Oct 2025 17:58:03 -1000 Subject: [PATCH 171/526] [esp32][ci] Fix IRAM overflow in grouped component tests for ESP32-IDF (#11386) --- esphome/components/esp32/__init__.py | 10 +++ esphome/components/esp32/iram_fix.py.script | 71 +++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 esphome/components/esp32/iram_fix.py.script diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index e5725200a6..af84692615 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -779,6 +779,16 @@ async def to_code(config): Path(__file__).parent / "post_build.py.script", ) + # In testing mode, add IRAM fix script to allow linking grouped component tests + # Similar to ESP8266's approach but for ESP-IDF + if CORE.testing_mode: + cg.add_build_flag("-DESPHOME_TESTING_MODE") + add_extra_script( + "pre", + "iram_fix.py", + Path(__file__).parent / "iram_fix.py.script", + ) + if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF: cg.add_platformio_option("framework", "espidf") cg.add_build_flag("-DUSE_ESP_IDF") diff --git a/esphome/components/esp32/iram_fix.py.script b/esphome/components/esp32/iram_fix.py.script new file mode 100644 index 0000000000..0d23f9a81b --- /dev/null +++ b/esphome/components/esp32/iram_fix.py.script @@ -0,0 +1,71 @@ +import os +import re + +# pylint: disable=E0602 +Import("env") # noqa + +# IRAM size for testing mode (2MB - large enough to accommodate grouped tests) +TESTING_IRAM_SIZE = 0x200000 + + +def patch_idf_linker_script(source, target, env): + """Patch ESP-IDF linker script to increase IRAM size for testing mode.""" + # Check if we're in testing mode by looking for the define + build_flags = env.get("BUILD_FLAGS", []) + testing_mode = any("-DESPHOME_TESTING_MODE" in flag for flag in build_flags) + + if not testing_mode: + return + + # For ESP-IDF, the linker scripts are generated in the build directory + build_dir = env.subst("$BUILD_DIR") + + # The memory.ld file is directly in the build directory + memory_ld = os.path.join(build_dir, "memory.ld") + + if not os.path.exists(memory_ld): + print(f"ESPHome: Warning - could not find linker script at {memory_ld}") + return + + try: + with open(memory_ld, "r") as f: + content = f.read() + except OSError as e: + print(f"ESPHome: Error reading linker script: {e}") + return + + # Check if this file contains iram0_0_seg + if 'iram0_0_seg' not in content: + print(f"ESPHome: Warning - iram0_0_seg not found in {memory_ld}") + return + + # Look for iram0_0_seg definition and increase its length + # ESP-IDF format can be: + # iram0_0_seg (RX) : org = 0x40080000, len = 0x20000 + 0x0 + # or more complex with nested parentheses: + # iram0_0_seg (RX) : org = (0x40370000 + 0x4000), len = (((0x403CB700 - (0x40378000 - 0x3FC88000)) - 0x3FC88000) + 0x8000 - 0x4000) + # We want to change len to TESTING_IRAM_SIZE for testing + + # Use a more robust approach: find the line and manually parse it + lines = content.split('\n') + for i, line in enumerate(lines): + if 'iram0_0_seg' in line and 'len' in line: + # Find the position of "len = " and replace everything after it until the end of the statement + match = re.search(r'(iram0_0_seg\s*\([^)]*\)\s*:\s*org\s*=\s*(?:\([^)]+\)|0x[0-9a-fA-F]+)\s*,\s*len\s*=\s*)(.+?)(\s*)$', line) + if match: + lines[i] = f"{match.group(1)}{TESTING_IRAM_SIZE:#x}{match.group(3)}" + break + + updated = '\n'.join(lines) + + if updated != content: + with open(memory_ld, "w") as f: + f.write(updated) + print(f"ESPHome: Patched IRAM size to {TESTING_IRAM_SIZE:#x} in {memory_ld} for testing mode") + else: + print(f"ESPHome: Warning - could not patch iram0_0_seg in {memory_ld}") + + +# Hook into the build process before linking +# For ESP-IDF, we need to run this after the linker scripts are generated +env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", patch_idf_linker_script) From 319ba4a504001bd0db0708cf3628b6c971c255c3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:03:09 +1300 Subject: [PATCH 172/526] [cover] Clean up deprecated functions from 2021.9 (#11391) --- esphome/components/cover/cover.cpp | 18 ++---------------- esphome/components/cover/cover.h | 20 +------------------- 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/esphome/components/cover/cover.cpp b/esphome/components/cover/cover.cpp index 3378279371..654bb956a5 100644 --- a/esphome/components/cover/cover.cpp +++ b/esphome/components/cover/cover.cpp @@ -1,6 +1,6 @@ #include "cover.h" -#include "esphome/core/log.h" #include +#include "esphome/core/log.h" namespace esphome { namespace cover { @@ -144,21 +144,7 @@ CoverCall &CoverCall::set_stop(bool stop) { bool CoverCall::get_stop() const { return this->stop_; } CoverCall Cover::make_call() { return {this}; } -void Cover::open() { - auto call = this->make_call(); - call.set_command_open(); - call.perform(); -} -void Cover::close() { - auto call = this->make_call(); - call.set_command_close(); - call.perform(); -} -void Cover::stop() { - auto call = this->make_call(); - call.set_command_stop(); - call.perform(); -} + void Cover::add_on_state_callback(std::function &&f) { this->state_callback_.add(std::move(f)); } void Cover::publish_state(bool save) { this->position = clamp(this->position, 0.0f, 1.0f); diff --git a/esphome/components/cover/cover.h b/esphome/components/cover/cover.h index ada5953d57..d5db6cfb4f 100644 --- a/esphome/components/cover/cover.h +++ b/esphome/components/cover/cover.h @@ -4,6 +4,7 @@ #include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" + #include "cover_traits.h" namespace esphome { @@ -125,25 +126,6 @@ class Cover : public EntityBase, public EntityBase_DeviceClass { /// Construct a new cover call used to control the cover. CoverCall make_call(); - /** Open the cover. - * - * This is a legacy method and may be removed later, please use `.make_call()` instead. - */ - ESPDEPRECATED("open() is deprecated, use make_call().set_command_open().perform() instead.", "2021.9") - void open(); - /** Close the cover. - * - * This is a legacy method and may be removed later, please use `.make_call()` instead. - */ - ESPDEPRECATED("close() is deprecated, use make_call().set_command_close().perform() instead.", "2021.9") - void close(); - /** Stop the cover. - * - * This is a legacy method and may be removed later, please use `.make_call()` instead. - * As per solution from issue #2885 the call should include perform() - */ - ESPDEPRECATED("stop() is deprecated, use make_call().set_command_stop().perform() instead.", "2021.9") - void stop(); void add_on_state_callback(std::function &&f); From 118b1d8593d7ca59ace87e7838eb0b8f014edd68 Mon Sep 17 00:00:00 2001 From: Grant Le Roux Date: Mon, 20 Oct 2025 12:05:05 +0800 Subject: [PATCH 173/526] MQTT Light - Min/Max Color Temperature (#11103) Co-authored-by: Cram42 <5396871+cram42@users.noreply.github.com> Co-authored-by: J. Nick Koston --- esphome/components/mqtt/mqtt_light.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/components/mqtt/mqtt_light.cpp b/esphome/components/mqtt/mqtt_light.cpp index 4f5ff408a4..883b67ffc6 100644 --- a/esphome/components/mqtt/mqtt_light.cpp +++ b/esphome/components/mqtt/mqtt_light.cpp @@ -69,6 +69,12 @@ void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscovery if (traits.supports_color_capability(ColorCapability::BRIGHTNESS)) root["brightness"] = true; + if (traits.supports_color_mode(ColorMode::COLOR_TEMPERATURE) || + traits.supports_color_mode(ColorMode::COLD_WARM_WHITE)) { + root[MQTT_MIN_MIREDS] = traits.get_min_mireds(); + root[MQTT_MAX_MIREDS] = traits.get_max_mireds(); + } + if (this->state_->supports_effects()) { root["effect"] = true; JsonArray effect_list = root[MQTT_EFFECT_LIST].to(); From 6f5e36ffc331425ca36323692fe4739c16d71625 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 19 Oct 2025 23:42:54 -0500 Subject: [PATCH 174/526] [climate] First pass at some optimization (#11366) Co-authored-by: J. Nick Koston --- esphome/components/climate/climate.cpp | 256 ++++++++++++++----------- esphome/components/climate/climate.h | 91 ++++----- 2 files changed, 185 insertions(+), 162 deletions(-) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index f3c93ed44e..24a3fe6d5a 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -6,6 +6,42 @@ namespace climate { static const char *const TAG = "climate"; +// Memory-efficient lookup tables +struct StringToUint8 { + const char *str; + const uint8_t value; +}; + +constexpr StringToUint8 CLIMATE_MODES_BY_STR[] = { + {"OFF", CLIMATE_MODE_OFF}, + {"AUTO", CLIMATE_MODE_AUTO}, + {"COOL", CLIMATE_MODE_COOL}, + {"HEAT", CLIMATE_MODE_HEAT}, + {"FAN_ONLY", CLIMATE_MODE_FAN_ONLY}, + {"DRY", CLIMATE_MODE_DRY}, + {"HEAT_COOL", CLIMATE_MODE_HEAT_COOL}, +}; + +constexpr StringToUint8 CLIMATE_FAN_MODES_BY_STR[] = { + {"ON", CLIMATE_FAN_ON}, {"OFF", CLIMATE_FAN_OFF}, {"AUTO", CLIMATE_FAN_AUTO}, + {"LOW", CLIMATE_FAN_LOW}, {"MEDIUM", CLIMATE_FAN_MEDIUM}, {"HIGH", CLIMATE_FAN_HIGH}, + {"MIDDLE", CLIMATE_FAN_MIDDLE}, {"FOCUS", CLIMATE_FAN_FOCUS}, {"DIFFUSE", CLIMATE_FAN_DIFFUSE}, + {"QUIET", CLIMATE_FAN_QUIET}, +}; + +constexpr StringToUint8 CLIMATE_PRESETS_BY_STR[] = { + {"ECO", CLIMATE_PRESET_ECO}, {"AWAY", CLIMATE_PRESET_AWAY}, {"BOOST", CLIMATE_PRESET_BOOST}, + {"COMFORT", CLIMATE_PRESET_COMFORT}, {"HOME", CLIMATE_PRESET_HOME}, {"SLEEP", CLIMATE_PRESET_SLEEP}, + {"ACTIVITY", CLIMATE_PRESET_ACTIVITY}, {"NONE", CLIMATE_PRESET_NONE}, +}; + +constexpr StringToUint8 CLIMATE_SWING_MODES_BY_STR[] = { + {"OFF", CLIMATE_SWING_OFF}, + {"BOTH", CLIMATE_SWING_BOTH}, + {"VERTICAL", CLIMATE_SWING_VERTICAL}, + {"HORIZONTAL", CLIMATE_SWING_HORIZONTAL}, +}; + void ClimateCall::perform() { this->parent_->control_callback_.call(*this); ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); @@ -50,47 +86,46 @@ void ClimateCall::perform() { } this->parent_->control(*this); } + void ClimateCall::validate_() { auto traits = this->parent_->get_traits(); if (this->mode_.has_value()) { auto mode = *this->mode_; if (!traits.supports_mode(mode)) { - ESP_LOGW(TAG, " Mode %s is not supported by this device!", LOG_STR_ARG(climate_mode_to_string(mode))); + ESP_LOGW(TAG, " Mode %s not supported", LOG_STR_ARG(climate_mode_to_string(mode))); this->mode_.reset(); } } if (this->custom_fan_mode_.has_value()) { auto custom_fan_mode = *this->custom_fan_mode_; if (!traits.supports_custom_fan_mode(custom_fan_mode)) { - ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", custom_fan_mode.c_str()); + ESP_LOGW(TAG, " Fan Mode %s not supported", custom_fan_mode.c_str()); this->custom_fan_mode_.reset(); } } else if (this->fan_mode_.has_value()) { auto fan_mode = *this->fan_mode_; if (!traits.supports_fan_mode(fan_mode)) { - ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", - LOG_STR_ARG(climate_fan_mode_to_string(fan_mode))); + ESP_LOGW(TAG, " Fan Mode %s not supported", LOG_STR_ARG(climate_fan_mode_to_string(fan_mode))); this->fan_mode_.reset(); } } if (this->custom_preset_.has_value()) { auto custom_preset = *this->custom_preset_; if (!traits.supports_custom_preset(custom_preset)) { - ESP_LOGW(TAG, " Preset %s is not supported by this device!", custom_preset.c_str()); + ESP_LOGW(TAG, " Preset %s not supported", custom_preset.c_str()); this->custom_preset_.reset(); } } else if (this->preset_.has_value()) { auto preset = *this->preset_; if (!traits.supports_preset(preset)) { - ESP_LOGW(TAG, " Preset %s is not supported by this device!", LOG_STR_ARG(climate_preset_to_string(preset))); + ESP_LOGW(TAG, " Preset %s not supported", LOG_STR_ARG(climate_preset_to_string(preset))); this->preset_.reset(); } } if (this->swing_mode_.has_value()) { auto swing_mode = *this->swing_mode_; if (!traits.supports_swing_mode(swing_mode)) { - ESP_LOGW(TAG, " Swing Mode %s is not supported by this device!", - LOG_STR_ARG(climate_swing_mode_to_string(swing_mode))); + ESP_LOGW(TAG, " Swing Mode %s not supported", LOG_STR_ARG(climate_swing_mode_to_string(swing_mode))); this->swing_mode_.reset(); } } @@ -99,159 +134,127 @@ void ClimateCall::validate_() { if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { ESP_LOGW(TAG, " Cannot set target temperature for climate device " - "with two-point target temperature!"); + "with two-point target temperature"); this->target_temperature_.reset(); } else if (std::isnan(target)) { - ESP_LOGW(TAG, " Target temperature must not be NAN!"); + ESP_LOGW(TAG, " Target temperature must not be NAN"); this->target_temperature_.reset(); } } if (this->target_temperature_low_.has_value() || this->target_temperature_high_.has_value()) { if (!traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { - ESP_LOGW(TAG, " Cannot set low/high target temperature for this device!"); + ESP_LOGW(TAG, " Cannot set low/high target temperature"); this->target_temperature_low_.reset(); this->target_temperature_high_.reset(); } } if (this->target_temperature_low_.has_value() && std::isnan(*this->target_temperature_low_)) { - ESP_LOGW(TAG, " Target temperature low must not be NAN!"); + ESP_LOGW(TAG, " Target temperature low must not be NAN"); this->target_temperature_low_.reset(); } if (this->target_temperature_high_.has_value() && std::isnan(*this->target_temperature_high_)) { - ESP_LOGW(TAG, " Target temperature low must not be NAN!"); + ESP_LOGW(TAG, " Target temperature high must not be NAN"); this->target_temperature_high_.reset(); } if (this->target_temperature_low_.has_value() && this->target_temperature_high_.has_value()) { float low = *this->target_temperature_low_; float high = *this->target_temperature_high_; if (low > high) { - ESP_LOGW(TAG, " Target temperature low %.2f must be smaller than target temperature high %.2f!", low, high); + ESP_LOGW(TAG, " Target temperature low %.2f must be less than target temperature high %.2f", low, high); this->target_temperature_low_.reset(); this->target_temperature_high_.reset(); } } } + ClimateCall &ClimateCall::set_mode(ClimateMode mode) { this->mode_ = mode; return *this; } + ClimateCall &ClimateCall::set_mode(const std::string &mode) { - if (str_equals_case_insensitive(mode, "OFF")) { - this->set_mode(CLIMATE_MODE_OFF); - } else if (str_equals_case_insensitive(mode, "AUTO")) { - this->set_mode(CLIMATE_MODE_AUTO); - } else if (str_equals_case_insensitive(mode, "COOL")) { - this->set_mode(CLIMATE_MODE_COOL); - } else if (str_equals_case_insensitive(mode, "HEAT")) { - this->set_mode(CLIMATE_MODE_HEAT); - } else if (str_equals_case_insensitive(mode, "FAN_ONLY")) { - this->set_mode(CLIMATE_MODE_FAN_ONLY); - } else if (str_equals_case_insensitive(mode, "DRY")) { - this->set_mode(CLIMATE_MODE_DRY); - } else if (str_equals_case_insensitive(mode, "HEAT_COOL")) { - this->set_mode(CLIMATE_MODE_HEAT_COOL); - } else { - ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str()); + for (const auto &mode_entry : CLIMATE_MODES_BY_STR) { + if (str_equals_case_insensitive(mode, mode_entry.str)) { + this->set_mode(static_cast(mode_entry.value)); + return *this; + } } + ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str()); return *this; } + ClimateCall &ClimateCall::set_fan_mode(ClimateFanMode fan_mode) { this->fan_mode_ = fan_mode; this->custom_fan_mode_.reset(); return *this; } + ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) { - if (str_equals_case_insensitive(fan_mode, "ON")) { - this->set_fan_mode(CLIMATE_FAN_ON); - } else if (str_equals_case_insensitive(fan_mode, "OFF")) { - this->set_fan_mode(CLIMATE_FAN_OFF); - } else if (str_equals_case_insensitive(fan_mode, "AUTO")) { - this->set_fan_mode(CLIMATE_FAN_AUTO); - } else if (str_equals_case_insensitive(fan_mode, "LOW")) { - this->set_fan_mode(CLIMATE_FAN_LOW); - } else if (str_equals_case_insensitive(fan_mode, "MEDIUM")) { - this->set_fan_mode(CLIMATE_FAN_MEDIUM); - } else if (str_equals_case_insensitive(fan_mode, "HIGH")) { - this->set_fan_mode(CLIMATE_FAN_HIGH); - } else if (str_equals_case_insensitive(fan_mode, "MIDDLE")) { - this->set_fan_mode(CLIMATE_FAN_MIDDLE); - } else if (str_equals_case_insensitive(fan_mode, "FOCUS")) { - this->set_fan_mode(CLIMATE_FAN_FOCUS); - } else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) { - this->set_fan_mode(CLIMATE_FAN_DIFFUSE); - } else if (str_equals_case_insensitive(fan_mode, "QUIET")) { - this->set_fan_mode(CLIMATE_FAN_QUIET); - } else { - if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) { - this->custom_fan_mode_ = fan_mode; - this->fan_mode_.reset(); - } else { - ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str()); + for (const auto &mode_entry : CLIMATE_FAN_MODES_BY_STR) { + if (str_equals_case_insensitive(fan_mode, mode_entry.str)) { + this->set_fan_mode(static_cast(mode_entry.value)); + return *this; } } + if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) { + this->custom_fan_mode_ = fan_mode; + this->fan_mode_.reset(); + } else { + ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str()); + } return *this; } + ClimateCall &ClimateCall::set_fan_mode(optional fan_mode) { if (fan_mode.has_value()) { this->set_fan_mode(fan_mode.value()); } return *this; } + ClimateCall &ClimateCall::set_preset(ClimatePreset preset) { this->preset_ = preset; this->custom_preset_.reset(); return *this; } + ClimateCall &ClimateCall::set_preset(const std::string &preset) { - if (str_equals_case_insensitive(preset, "ECO")) { - this->set_preset(CLIMATE_PRESET_ECO); - } else if (str_equals_case_insensitive(preset, "AWAY")) { - this->set_preset(CLIMATE_PRESET_AWAY); - } else if (str_equals_case_insensitive(preset, "BOOST")) { - this->set_preset(CLIMATE_PRESET_BOOST); - } else if (str_equals_case_insensitive(preset, "COMFORT")) { - this->set_preset(CLIMATE_PRESET_COMFORT); - } else if (str_equals_case_insensitive(preset, "HOME")) { - this->set_preset(CLIMATE_PRESET_HOME); - } else if (str_equals_case_insensitive(preset, "SLEEP")) { - this->set_preset(CLIMATE_PRESET_SLEEP); - } else if (str_equals_case_insensitive(preset, "ACTIVITY")) { - this->set_preset(CLIMATE_PRESET_ACTIVITY); - } else if (str_equals_case_insensitive(preset, "NONE")) { - this->set_preset(CLIMATE_PRESET_NONE); - } else { - if (this->parent_->get_traits().supports_custom_preset(preset)) { - this->custom_preset_ = preset; - this->preset_.reset(); - } else { - ESP_LOGW(TAG, "'%s' - Unrecognized preset %s", this->parent_->get_name().c_str(), preset.c_str()); + for (const auto &preset_entry : CLIMATE_PRESETS_BY_STR) { + if (str_equals_case_insensitive(preset, preset_entry.str)) { + this->set_preset(static_cast(preset_entry.value)); + return *this; } } + if (this->parent_->get_traits().supports_custom_preset(preset)) { + this->custom_preset_ = preset; + this->preset_.reset(); + } else { + ESP_LOGW(TAG, "'%s' - Unrecognized preset %s", this->parent_->get_name().c_str(), preset.c_str()); + } return *this; } + ClimateCall &ClimateCall::set_preset(optional preset) { if (preset.has_value()) { this->set_preset(preset.value()); } return *this; } + ClimateCall &ClimateCall::set_swing_mode(ClimateSwingMode swing_mode) { this->swing_mode_ = swing_mode; return *this; } + ClimateCall &ClimateCall::set_swing_mode(const std::string &swing_mode) { - if (str_equals_case_insensitive(swing_mode, "OFF")) { - this->set_swing_mode(CLIMATE_SWING_OFF); - } else if (str_equals_case_insensitive(swing_mode, "BOTH")) { - this->set_swing_mode(CLIMATE_SWING_BOTH); - } else if (str_equals_case_insensitive(swing_mode, "VERTICAL")) { - this->set_swing_mode(CLIMATE_SWING_VERTICAL); - } else if (str_equals_case_insensitive(swing_mode, "HORIZONTAL")) { - this->set_swing_mode(CLIMATE_SWING_HORIZONTAL); - } else { - ESP_LOGW(TAG, "'%s' - Unrecognized swing mode %s", this->parent_->get_name().c_str(), swing_mode.c_str()); + for (const auto &mode_entry : CLIMATE_SWING_MODES_BY_STR) { + if (str_equals_case_insensitive(swing_mode, mode_entry.str)) { + this->set_swing_mode(static_cast(mode_entry.value)); + return *this; + } } + ESP_LOGW(TAG, "'%s' - Unrecognized swing mode %s", this->parent_->get_name().c_str(), swing_mode.c_str()); return *this; } @@ -259,59 +262,71 @@ ClimateCall &ClimateCall::set_target_temperature(float target_temperature) { this->target_temperature_ = target_temperature; return *this; } + ClimateCall &ClimateCall::set_target_temperature_low(float target_temperature_low) { this->target_temperature_low_ = target_temperature_low; return *this; } + ClimateCall &ClimateCall::set_target_temperature_high(float target_temperature_high) { this->target_temperature_high_ = target_temperature_high; return *this; } + ClimateCall &ClimateCall::set_target_humidity(float target_humidity) { this->target_humidity_ = target_humidity; return *this; } -const optional &ClimateCall::get_mode() const { return this->mode_; } const optional &ClimateCall::get_target_temperature() const { return this->target_temperature_; } const optional &ClimateCall::get_target_temperature_low() const { return this->target_temperature_low_; } const optional &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; } const optional &ClimateCall::get_target_humidity() const { return this->target_humidity_; } + +const optional &ClimateCall::get_mode() const { return this->mode_; } const optional &ClimateCall::get_fan_mode() const { return this->fan_mode_; } -const optional &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; } -const optional &ClimateCall::get_preset() const { return this->preset_; } -const optional &ClimateCall::get_custom_preset() const { return this->custom_preset_; } const optional &ClimateCall::get_swing_mode() const { return this->swing_mode_; } +const optional &ClimateCall::get_preset() const { return this->preset_; } +const optional &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; } +const optional &ClimateCall::get_custom_preset() const { return this->custom_preset_; } + ClimateCall &ClimateCall::set_target_temperature_high(optional target_temperature_high) { this->target_temperature_high_ = target_temperature_high; return *this; } + ClimateCall &ClimateCall::set_target_temperature_low(optional target_temperature_low) { this->target_temperature_low_ = target_temperature_low; return *this; } + ClimateCall &ClimateCall::set_target_temperature(optional target_temperature) { this->target_temperature_ = target_temperature; return *this; } + ClimateCall &ClimateCall::set_target_humidity(optional target_humidity) { this->target_humidity_ = target_humidity; return *this; } + ClimateCall &ClimateCall::set_mode(optional mode) { this->mode_ = mode; return *this; } + ClimateCall &ClimateCall::set_fan_mode(optional fan_mode) { this->fan_mode_ = fan_mode; this->custom_fan_mode_.reset(); return *this; } + ClimateCall &ClimateCall::set_preset(optional preset) { this->preset_ = preset; this->custom_preset_.reset(); return *this; } + ClimateCall &ClimateCall::set_swing_mode(optional swing_mode) { this->swing_mode_ = swing_mode; return *this; @@ -336,6 +351,7 @@ optional Climate::restore_state_() { return {}; return recovered; } + void Climate::save_state_() { #if (defined(USE_ESP_IDF) || (defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0))) && \ !defined(CLANG_TIDY) @@ -398,6 +414,7 @@ void Climate::save_state_() { this->rtc_.save(&state); } + void Climate::publish_state() { ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str()); auto traits = this->get_traits(); @@ -469,16 +486,20 @@ ClimateTraits Climate::get_traits() { void Climate::set_visual_min_temperature_override(float visual_min_temperature_override) { this->visual_min_temperature_override_ = visual_min_temperature_override; } + void Climate::set_visual_max_temperature_override(float visual_max_temperature_override) { this->visual_max_temperature_override_ = visual_max_temperature_override; } + void Climate::set_visual_temperature_step_override(float target, float current) { this->visual_target_temperature_step_override_ = target; this->visual_current_temperature_step_override_ = current; } + void Climate::set_visual_min_humidity_override(float visual_min_humidity_override) { this->visual_min_humidity_override_ = visual_min_humidity_override; } + void Climate::set_visual_max_humidity_override(float visual_max_humidity_override) { this->visual_max_humidity_override_ = visual_max_humidity_override; } @@ -510,6 +531,7 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) { } return call; } + void ClimateDeviceRestoreState::apply(Climate *climate) { auto traits = climate->get_traits(); climate->mode = this->mode; @@ -579,68 +601,68 @@ void Climate::dump_traits_(const char *tag) { auto traits = this->get_traits(); ESP_LOGCONFIG(tag, "ClimateTraits:"); ESP_LOGCONFIG(tag, - " [x] Visual settings:\n" - " - Min temperature: %.1f\n" - " - Max temperature: %.1f\n" - " - Temperature step:\n" - " Target: %.1f", + " Visual settings:\n" + " - Min temperature: %.1f\n" + " - Max temperature: %.1f\n" + " - Temperature step:\n" + " Target: %.1f", traits.get_visual_min_temperature(), traits.get_visual_max_temperature(), traits.get_visual_target_temperature_step()); if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { - ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step()); + ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step()); } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY | climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) { ESP_LOGCONFIG(tag, - " - Min humidity: %.0f\n" - " - Max humidity: %.0f", + " - Min humidity: %.0f\n" + " - Max humidity: %.0f", traits.get_visual_min_humidity(), traits.get_visual_max_humidity()); } if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { - ESP_LOGCONFIG(tag, " [x] Supports two-point target temperature"); + ESP_LOGCONFIG(tag, " Supports two-point target temperature"); } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { - ESP_LOGCONFIG(tag, " [x] Supports current temperature"); + ESP_LOGCONFIG(tag, " Supports current temperature"); } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { - ESP_LOGCONFIG(tag, " [x] Supports target humidity"); + ESP_LOGCONFIG(tag, " Supports target humidity"); } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) { - ESP_LOGCONFIG(tag, " [x] Supports current humidity"); + ESP_LOGCONFIG(tag, " Supports current humidity"); } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { - ESP_LOGCONFIG(tag, " [x] Supports action"); + ESP_LOGCONFIG(tag, " Supports action"); } if (!traits.get_supported_modes().empty()) { - ESP_LOGCONFIG(tag, " [x] Supported modes:"); + ESP_LOGCONFIG(tag, " Supported modes:"); for (ClimateMode m : traits.get_supported_modes()) - ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_mode_to_string(m))); + ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_mode_to_string(m))); } if (!traits.get_supported_fan_modes().empty()) { - ESP_LOGCONFIG(tag, " [x] Supported fan modes:"); + ESP_LOGCONFIG(tag, " Supported fan modes:"); for (ClimateFanMode m : traits.get_supported_fan_modes()) - ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_fan_mode_to_string(m))); + ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_fan_mode_to_string(m))); } if (!traits.get_supported_custom_fan_modes().empty()) { - ESP_LOGCONFIG(tag, " [x] Supported custom fan modes:"); + ESP_LOGCONFIG(tag, " Supported custom fan modes:"); for (const std::string &s : traits.get_supported_custom_fan_modes()) - ESP_LOGCONFIG(tag, " - %s", s.c_str()); + ESP_LOGCONFIG(tag, " - %s", s.c_str()); } if (!traits.get_supported_presets().empty()) { - ESP_LOGCONFIG(tag, " [x] Supported presets:"); + ESP_LOGCONFIG(tag, " Supported presets:"); for (ClimatePreset p : traits.get_supported_presets()) - ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_preset_to_string(p))); + ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_preset_to_string(p))); } if (!traits.get_supported_custom_presets().empty()) { - ESP_LOGCONFIG(tag, " [x] Supported custom presets:"); + ESP_LOGCONFIG(tag, " Supported custom presets:"); for (const std::string &s : traits.get_supported_custom_presets()) - ESP_LOGCONFIG(tag, " - %s", s.c_str()); + ESP_LOGCONFIG(tag, " - %s", s.c_str()); } if (!traits.get_supported_swing_modes().empty()) { - ESP_LOGCONFIG(tag, " [x] Supported swing modes:"); + ESP_LOGCONFIG(tag, " Supported swing modes:"); for (ClimateSwingMode m : traits.get_supported_swing_modes()) - ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_swing_mode_to_string(m))); + ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_swing_mode_to_string(m))); } } diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index b31a2eedf6..495464c6a2 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -93,30 +93,31 @@ class ClimateCall { void perform(); - const optional &get_mode() const; const optional &get_target_temperature() const; const optional &get_target_temperature_low() const; const optional &get_target_temperature_high() const; const optional &get_target_humidity() const; + + const optional &get_mode() const; const optional &get_fan_mode() const; const optional &get_swing_mode() const; - const optional &get_custom_fan_mode() const; const optional &get_preset() const; + const optional &get_custom_fan_mode() const; const optional &get_custom_preset() const; protected: void validate_(); Climate *const parent_; - optional mode_; optional target_temperature_; optional target_temperature_low_; optional target_temperature_high_; optional target_humidity_; + optional mode_; optional fan_mode_; optional swing_mode_; - optional custom_fan_mode_; optional preset_; + optional custom_fan_mode_; optional custom_preset_; }; @@ -169,47 +170,6 @@ class Climate : public EntityBase { public: Climate() {} - /// The active mode of the climate device. - ClimateMode mode{CLIMATE_MODE_OFF}; - - /// The active state of the climate device. - ClimateAction action{CLIMATE_ACTION_OFF}; - - /// The current temperature of the climate device, as reported from the integration. - float current_temperature{NAN}; - - /// The current humidity of the climate device, as reported from the integration. - float current_humidity{NAN}; - - union { - /// The target temperature of the climate device. - float target_temperature; - struct { - /// The minimum target temperature of the climate device, for climate devices with split target temperature. - float target_temperature_low{NAN}; - /// The maximum target temperature of the climate device, for climate devices with split target temperature. - float target_temperature_high{NAN}; - }; - }; - - /// The target humidity of the climate device. - float target_humidity; - - /// The active fan mode of the climate device. - optional fan_mode; - - /// The active swing mode of the climate device. - ClimateSwingMode swing_mode; - - /// The active custom fan mode of the climate device. - optional custom_fan_mode; - - /// The active preset of the climate device. - optional preset; - - /// The active custom preset mode of the climate device. - optional custom_preset; - /** Add a callback for the climate device state, each time the state of the climate device is updated * (using publish_state), this callback will be called. * @@ -251,6 +211,47 @@ class Climate : public EntityBase { void set_visual_min_humidity_override(float visual_min_humidity_override); void set_visual_max_humidity_override(float visual_max_humidity_override); + /// The current temperature of the climate device, as reported from the integration. + float current_temperature{NAN}; + + /// The current humidity of the climate device, as reported from the integration. + float current_humidity{NAN}; + + union { + /// The target temperature of the climate device. + float target_temperature; + struct { + /// The minimum target temperature of the climate device, for climate devices with split target temperature. + float target_temperature_low{NAN}; + /// The maximum target temperature of the climate device, for climate devices with split target temperature. + float target_temperature_high{NAN}; + }; + }; + + /// The target humidity of the climate device. + float target_humidity; + + /// The active fan mode of the climate device. + optional fan_mode; + + /// The active preset of the climate device. + optional preset; + + /// The active custom fan mode of the climate device. + optional custom_fan_mode; + + /// The active custom preset mode of the climate device. + optional custom_preset; + + /// The active mode of the climate device. + ClimateMode mode{CLIMATE_MODE_OFF}; + + /// The active state of the climate device. + ClimateAction action{CLIMATE_ACTION_OFF}; + + /// The active swing mode of the climate device. + ClimateSwingMode swing_mode{CLIMATE_SWING_OFF}; + protected: friend ClimateCall; From 3d82c5baf7402de97a69fa7a0ec8208382f768f1 Mon Sep 17 00:00:00 2001 From: Aman kumar Date: Mon, 20 Oct 2025 11:40:38 +0530 Subject: [PATCH 175/526] [esp32_improv]: add next_url support for WiFi provisioning (#10757) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esp32_improv/__init__.py | 114 ++++++++++-------- .../esp32_improv/esp32_improv_component.cpp | 13 +- .../esp32_improv/esp32_improv_component.h | 3 +- .../components/improv_base/improv_base.cpp | 41 ++++--- tests/components/esp32_improv/common.yaml | 1 + 5 files changed, 101 insertions(+), 71 deletions(-) diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index fa33bd947a..a55c819e6f 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -1,11 +1,11 @@ from esphome import automation import esphome.codegen as cg -from esphome.components import binary_sensor, esp32_ble, output +from esphome.components import binary_sensor, esp32_ble, improv_base, 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 -AUTO_LOAD = ["esp32_ble_server"] +AUTO_LOAD = ["esp32_ble_server", "improv_base"] CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["wifi", "esp32"] @@ -20,6 +20,7 @@ CONF_ON_STOP = "on_stop" CONF_STATUS_INDICATOR = "status_indicator" CONF_WIFI_TIMEOUT = "wifi_timeout" + improv_ns = cg.esphome_ns.namespace("improv") Error = improv_ns.enum("Error") State = improv_ns.enum("State") @@ -43,55 +44,63 @@ ESP32ImprovStoppedTrigger = esp32_improv_ns.class_( ) -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(ESP32ImprovComponent), - cv.Required(CONF_AUTHORIZER): cv.Any( - cv.none, cv.use_id(binary_sensor.BinarySensor) - ), - cv.Optional(CONF_STATUS_INDICATOR): cv.use_id(output.BinaryOutput), - cv.Optional( - CONF_IDENTIFY_DURATION, default="10s" - ): cv.positive_time_period_milliseconds, - cv.Optional( - CONF_AUTHORIZED_DURATION, default="1min" - ): cv.positive_time_period_milliseconds, - cv.Optional( - CONF_WIFI_TIMEOUT, default="1min" - ): cv.positive_time_period_milliseconds, - cv.Optional(CONF_ON_PROVISIONED): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - ESP32ImprovProvisionedTrigger - ), - } - ), - cv.Optional(CONF_ON_PROVISIONING): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - ESP32ImprovProvisioningTrigger - ), - } - ), - cv.Optional(CONF_ON_START): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32ImprovStartTrigger), - } - ), - cv.Optional(CONF_ON_STATE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32ImprovStateTrigger), - } - ), - cv.Optional(CONF_ON_STOP): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - ESP32ImprovStoppedTrigger - ), - } - ), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ESP32ImprovComponent), + cv.Required(CONF_AUTHORIZER): cv.Any( + cv.none, cv.use_id(binary_sensor.BinarySensor) + ), + cv.Optional(CONF_STATUS_INDICATOR): cv.use_id(output.BinaryOutput), + cv.Optional( + CONF_IDENTIFY_DURATION, default="10s" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_AUTHORIZED_DURATION, default="1min" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_WIFI_TIMEOUT, default="1min" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_ON_PROVISIONED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32ImprovProvisionedTrigger + ), + } + ), + cv.Optional(CONF_ON_PROVISIONING): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32ImprovProvisioningTrigger + ), + } + ), + cv.Optional(CONF_ON_START): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32ImprovStartTrigger + ), + } + ), + cv.Optional(CONF_ON_STATE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32ImprovStateTrigger + ), + } + ), + cv.Optional(CONF_ON_STOP): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32ImprovStoppedTrigger + ), + } + ), + } + ) + .extend(improv_base.IMPROV_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) async def to_code(config): @@ -102,7 +111,8 @@ async def to_code(config): await cg.register_component(var, config) cg.add_define("USE_IMPROV") - cg.add_library("improv/Improv", "1.2.4") + + await improv_base.setup_improv_core(var, config) cg.add(var.set_identify_duration(config[CONF_IDENTIFY_DURATION])) cg.add(var.set_authorized_duration(config[CONF_AUTHORIZED_DURATION])) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index d83caf931b..526f7f4b42 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -1,10 +1,10 @@ #include "esp32_improv_component.h" +#include "esphome/components/bytebuffer/bytebuffer.h" #include "esphome/components/esp32_ble/ble.h" #include "esphome/components/esp32_ble_server/ble_2902.h" #include "esphome/core/application.h" #include "esphome/core/log.h" -#include "esphome/components/bytebuffer/bytebuffer.h" #ifdef USE_ESP32 @@ -384,7 +384,16 @@ void ESP32ImprovComponent::check_wifi_connection_() { this->connecting_sta_ = {}; this->cancel_timeout("wifi-connect-timeout"); - std::vector urls = {ESPHOME_MY_LINK}; + std::vector urls; + + // Add next_url if configured (should be first per Improv BLE spec) + std::string next_url = this->get_formatted_next_url_(); + if (!next_url.empty()) { + urls.push_back(next_url); + } + + // Add default URLs for backward compatibility + urls.emplace_back(ESPHOME_MY_LINK); #ifdef USE_WEBSERVER for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) { if (ip.is_ip4()) { diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index 6782430ffe..fd3b2b861d 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -7,6 +7,7 @@ #include "esphome/components/esp32_ble_server/ble_characteristic.h" #include "esphome/components/esp32_ble_server/ble_server.h" +#include "esphome/components/improv_base/improv_base.h" #include "esphome/components/wifi/wifi_component.h" #ifdef USE_ESP32_IMPROV_STATE_CALLBACK @@ -32,7 +33,7 @@ namespace esp32_improv { using namespace esp32_ble_server; -class ESP32ImprovComponent : public Component { +class ESP32ImprovComponent : public Component, public improv_base::ImprovBase { public: ESP32ImprovComponent(); void dump_config() override; diff --git a/esphome/components/improv_base/improv_base.cpp b/esphome/components/improv_base/improv_base.cpp index e890187d1a..89ee5492b5 100644 --- a/esphome/components/improv_base/improv_base.cpp +++ b/esphome/components/improv_base/improv_base.cpp @@ -10,27 +10,36 @@ std::string ImprovBase::get_formatted_next_url_() { if (this->next_url_.empty()) { return ""; } - std::string copy = this->next_url_; - // Device name - std::size_t pos = this->next_url_.find("{{device_name}}"); - if (pos != std::string::npos) { - const std::string &device_name = App.get_name(); - copy.replace(pos, 15, device_name); + + std::string formatted_url = this->next_url_; + + // Replace all occurrences of {{device_name}} + const std::string device_name_placeholder = "{{device_name}}"; + const std::string &device_name = App.get_name(); + size_t pos = 0; + while ((pos = formatted_url.find(device_name_placeholder, pos)) != std::string::npos) { + formatted_url.replace(pos, device_name_placeholder.length(), device_name); + pos += device_name.length(); } - // Ip address - pos = this->next_url_.find("{{ip_address}}"); - if (pos != std::string::npos) { - for (auto &ip : network::get_ip_addresses()) { - if (ip.is_ip4()) { - std::string ipa = ip.str(); - copy.replace(pos, 14, ipa); - break; - } + // Replace all occurrences of {{ip_address}} + const std::string ip_address_placeholder = "{{ip_address}}"; + std::string ip_address_str; + for (auto &ip : network::get_ip_addresses()) { + if (ip.is_ip4()) { + ip_address_str = ip.str(); + break; } } + pos = 0; + while ((pos = formatted_url.find(ip_address_placeholder, pos)) != std::string::npos) { + formatted_url.replace(pos, ip_address_placeholder.length(), ip_address_str); + pos += ip_address_str.length(); + } - return copy; + // Note: {{esphome_version}} is replaced at code generation time in Python + + return formatted_url; } } // namespace improv_base diff --git a/tests/components/esp32_improv/common.yaml b/tests/components/esp32_improv/common.yaml index 7eb3f9c0be..7dc2f7b6c7 100644 --- a/tests/components/esp32_improv/common.yaml +++ b/tests/components/esp32_improv/common.yaml @@ -16,3 +16,4 @@ esp32_improv: authorizer: io0_button authorized_duration: 1min status_indicator: built_in_led + next_url: "https://example.com/setup?device={{device_name}}&ip={{ip_address}}&version={{esphome_version}}" From 12e9c5e60eb9260a0c27e13a705b954629ea7535 Mon Sep 17 00:00:00 2001 From: Enrico Galli Date: Sun, 19 Oct 2025 23:11:09 -0700 Subject: [PATCH 176/526] [epaper_spi] Fix busy pin logic (#11349) Co-authored-by: J. Nick Koston --- esphome/components/epaper_spi/epaper_spi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/epaper_spi/epaper_spi.cpp b/esphome/components/epaper_spi/epaper_spi.cpp index 21be4a2c05..9630ea7f8b 100644 --- a/esphome/components/epaper_spi/epaper_spi.cpp +++ b/esphome/components/epaper_spi/epaper_spi.cpp @@ -103,7 +103,7 @@ bool EPaperBase::is_idle_() { if (this->busy_pin_ == nullptr) { return true; } - return !this->busy_pin_->digital_read(); + return this->busy_pin_->digital_read(); } void EPaperBase::reset() { From ea4e5fd7bd2411034b58a20bf6a5c0d4390374ab Mon Sep 17 00:00:00 2001 From: Juan Antonio Aldea Date: Mon, 20 Oct 2025 10:20:39 +0200 Subject: [PATCH 177/526] [climate] Migrate components to the new API (#11369) Co-authored-by: J. Nick Koston Co-authored-by: Keith Burzinski --- esphome/components/anova/anova.h | 2 +- .../components/bang_bang/bang_bang_climate.cpp | 15 ++++++++------- .../components/bedjet/climate/bedjet_climate.h | 3 +-- esphome/components/climate_ir/climate_ir.cpp | 6 ++++-- esphome/components/daikin_arc/daikin_arc.cpp | 4 +--- esphome/components/demo/demo_climate.h | 10 ++++------ esphome/components/haier/haier_base.cpp | 2 +- esphome/components/midea/air_conditioner.cpp | 2 +- esphome/components/mitsubishi/mitsubishi.cpp | 5 +++-- esphome/components/pid/pid_climate.cpp | 6 ++---- esphome/components/tuya/climate/tuya_climate.cpp | 7 +++++-- .../climate/uponor_smatrix_climate.cpp | 5 ++--- esphome/components/yashima/yashima.cpp | 5 +++-- 13 files changed, 36 insertions(+), 36 deletions(-) diff --git a/esphome/components/anova/anova.h b/esphome/components/anova/anova.h index 560d96baa7..2e43ebfb98 100644 --- a/esphome/components/anova/anova.h +++ b/esphome/components/anova/anova.h @@ -28,7 +28,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode void dump_config() override; climate::ClimateTraits traits() override { auto traits = climate::ClimateTraits(); - traits.set_supports_current_temperature(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::ClimateMode::CLIMATE_MODE_HEAT}); traits.set_visual_min_temperature(25.0); traits.set_visual_max_temperature(100.0); diff --git a/esphome/components/bang_bang/bang_bang_climate.cpp b/esphome/components/bang_bang/bang_bang_climate.cpp index bb85b49238..5d8c0eb7b7 100644 --- a/esphome/components/bang_bang/bang_bang_climate.cpp +++ b/esphome/components/bang_bang/bang_bang_climate.cpp @@ -56,26 +56,27 @@ void BangBangClimate::control(const climate::ClimateCall &call) { } climate::ClimateTraits BangBangClimate::traits() { auto traits = climate::ClimateTraits(); - traits.set_supports_current_temperature(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE | + climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE | climate::CLIMATE_SUPPORTS_ACTION); + if (this->humidity_sensor_ != nullptr) - traits.set_supports_current_humidity(true); - traits.set_supported_modes({ - climate::CLIMATE_MODE_OFF, - }); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY); + + traits.set_supported_modes({climate::CLIMATE_MODE_OFF}); if (supports_cool_) traits.add_supported_mode(climate::CLIMATE_MODE_COOL); if (supports_heat_) traits.add_supported_mode(climate::CLIMATE_MODE_HEAT); if (supports_cool_ && supports_heat_) traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); - traits.set_supports_two_point_target_temperature(true); + if (supports_away_) { traits.set_supported_presets({ climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_AWAY, }); } - traits.set_supports_action(true); + return traits; } void BangBangClimate::compute_state_() { diff --git a/esphome/components/bedjet/climate/bedjet_climate.h b/esphome/components/bedjet/climate/bedjet_climate.h index 7eaa735a3f..963f2e585a 100644 --- a/esphome/components/bedjet/climate/bedjet_climate.h +++ b/esphome/components/bedjet/climate/bedjet_climate.h @@ -33,8 +33,7 @@ class BedJetClimate : public climate::Climate, public BedJetClient, public Polli climate::ClimateTraits traits() override { auto traits = climate::ClimateTraits(); - traits.set_supports_action(true); - traits.set_supports_current_temperature(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_ACTION | climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); traits.set_supported_modes({ climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_HEAT, diff --git a/esphome/components/climate_ir/climate_ir.cpp b/esphome/components/climate_ir/climate_ir.cpp index dc8117f6ae..2b95792a6c 100644 --- a/esphome/components/climate_ir/climate_ir.cpp +++ b/esphome/components/climate_ir/climate_ir.cpp @@ -8,7 +8,10 @@ static const char *const TAG = "climate_ir"; climate::ClimateTraits ClimateIR::traits() { auto traits = climate::ClimateTraits(); - traits.set_supports_current_temperature(this->sensor_ != nullptr); + if (this->sensor_ != nullptr) { + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + } + traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_HEAT_COOL}); if (this->supports_cool_) traits.add_supported_mode(climate::CLIMATE_MODE_COOL); @@ -19,7 +22,6 @@ climate::ClimateTraits ClimateIR::traits() { if (this->supports_fan_only_) traits.add_supported_mode(climate::CLIMATE_MODE_FAN_ONLY); - traits.set_supports_two_point_target_temperature(false); traits.set_visual_min_temperature(this->minimum_temperature_); traits.set_visual_max_temperature(this->maximum_temperature_); traits.set_visual_temperature_step(this->temperature_step_); diff --git a/esphome/components/daikin_arc/daikin_arc.cpp b/esphome/components/daikin_arc/daikin_arc.cpp index 068819ecd1..f05342f482 100644 --- a/esphome/components/daikin_arc/daikin_arc.cpp +++ b/esphome/components/daikin_arc/daikin_arc.cpp @@ -241,9 +241,7 @@ uint8_t DaikinArcClimate::humidity_() { climate::ClimateTraits DaikinArcClimate::traits() { climate::ClimateTraits traits = climate_ir::ClimateIR::traits(); - traits.set_supports_current_temperature(true); - traits.set_supports_current_humidity(false); - traits.set_supports_target_humidity(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE | climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY); traits.set_visual_min_humidity(38); traits.set_visual_max_humidity(52); return traits; diff --git a/esphome/components/demo/demo_climate.h b/esphome/components/demo/demo_climate.h index 1ba80aabf5..84b16e7ec5 100644 --- a/esphome/components/demo/demo_climate.h +++ b/esphome/components/demo/demo_climate.h @@ -82,16 +82,14 @@ class DemoClimate : public climate::Climate, public Component { climate::ClimateTraits traits{}; switch (type_) { case DemoClimateType::TYPE_1: - traits.set_supports_current_temperature(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE | climate::CLIMATE_SUPPORTS_ACTION); traits.set_supported_modes({ climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_HEAT, }); - traits.set_supports_action(true); traits.set_visual_temperature_step(0.5); break; case DemoClimateType::TYPE_2: - traits.set_supports_current_temperature(false); traits.set_supported_modes({ climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_HEAT, @@ -100,7 +98,7 @@ class DemoClimate : public climate::Climate, public Component { climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY, }); - traits.set_supports_action(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_ACTION); traits.set_supported_fan_modes({ climate::CLIMATE_FAN_ON, climate::CLIMATE_FAN_OFF, @@ -123,8 +121,8 @@ class DemoClimate : public climate::Climate, public Component { traits.set_supported_custom_presets({"My Preset"}); break; case DemoClimateType::TYPE_3: - traits.set_supports_current_temperature(true); - traits.set_supports_two_point_target_temperature(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE | + climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE); traits.set_supported_modes({ climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, diff --git a/esphome/components/haier/haier_base.cpp b/esphome/components/haier/haier_base.cpp index 55a2454fca..5709b8e9b5 100644 --- a/esphome/components/haier/haier_base.cpp +++ b/esphome/components/haier/haier_base.cpp @@ -65,7 +65,7 @@ HaierClimateBase::HaierClimateBase() {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH}); this->traits_.set_supported_swing_modes({climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL}); - this->traits_.set_supports_current_temperature(true); + this->traits_.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); } HaierClimateBase::~HaierClimateBase() {} diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index 170a2f6a40..0ad26ebd51 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -77,7 +77,7 @@ void AirConditioner::control(const ClimateCall &call) { ClimateTraits AirConditioner::traits() { auto traits = ClimateTraits(); - traits.set_supports_current_temperature(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); traits.set_visual_min_temperature(17); traits.set_visual_max_temperature(30); traits.set_visual_temperature_step(0.5); diff --git a/esphome/components/mitsubishi/mitsubishi.cpp b/esphome/components/mitsubishi/mitsubishi.cpp index 3d9207dd96..10ab4f3b5c 100644 --- a/esphome/components/mitsubishi/mitsubishi.cpp +++ b/esphome/components/mitsubishi/mitsubishi.cpp @@ -52,8 +52,9 @@ const uint8_t MITSUBISHI_BYTE16 = 0x00; climate::ClimateTraits MitsubishiClimate::traits() { auto traits = climate::ClimateTraits(); - traits.set_supports_current_temperature(this->sensor_ != nullptr); - traits.set_supports_action(false); + if (this->sensor_ != nullptr) { + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + } traits.set_visual_min_temperature(MITSUBISHI_TEMP_MIN); traits.set_visual_max_temperature(MITSUBISHI_TEMP_MAX); traits.set_visual_temperature_step(1.0f); diff --git a/esphome/components/pid/pid_climate.cpp b/esphome/components/pid/pid_climate.cpp index 8b3be36dcc..fd74eabd87 100644 --- a/esphome/components/pid/pid_climate.cpp +++ b/esphome/components/pid/pid_climate.cpp @@ -54,11 +54,10 @@ void PIDClimate::control(const climate::ClimateCall &call) { } climate::ClimateTraits PIDClimate::traits() { auto traits = climate::ClimateTraits(); - traits.set_supports_current_temperature(true); - traits.set_supports_two_point_target_temperature(false); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE | climate::CLIMATE_SUPPORTS_ACTION); if (this->humidity_sensor_ != nullptr) - traits.set_supports_current_humidity(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY); traits.set_supported_modes({climate::CLIMATE_MODE_OFF}); if (supports_cool_()) @@ -68,7 +67,6 @@ climate::ClimateTraits PIDClimate::traits() { if (supports_heat_() && supports_cool_()) traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); - traits.set_supports_action(true); return traits; } void PIDClimate::dump_config() { diff --git a/esphome/components/tuya/climate/tuya_climate.cpp b/esphome/components/tuya/climate/tuya_climate.cpp index 7827a4e3ab..04fb14acff 100644 --- a/esphome/components/tuya/climate/tuya_climate.cpp +++ b/esphome/components/tuya/climate/tuya_climate.cpp @@ -283,8 +283,11 @@ void TuyaClimate::control_fan_mode_(const climate::ClimateCall &call) { climate::ClimateTraits TuyaClimate::traits() { auto traits = climate::ClimateTraits(); - traits.set_supports_action(true); - traits.set_supports_current_temperature(this->current_temperature_id_.has_value()); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_ACTION); + if (this->current_temperature_id_.has_value()) { + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + } + if (supports_heat_) traits.add_supported_mode(climate::CLIMATE_MODE_HEAT); if (supports_cool_) diff --git a/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp b/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp index 8af106dfb7..4256b01c4e 100644 --- a/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +++ b/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp @@ -30,10 +30,9 @@ void UponorSmatrixClimate::loop() { climate::ClimateTraits UponorSmatrixClimate::traits() { auto traits = climate::ClimateTraits(); - traits.set_supports_current_temperature(true); - traits.set_supports_current_humidity(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE | climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY | + climate::CLIMATE_SUPPORTS_ACTION); traits.set_supported_modes({climate::CLIMATE_MODE_HEAT}); - traits.set_supports_action(true); traits.set_supported_presets({climate::CLIMATE_PRESET_ECO}); traits.set_visual_min_temperature(this->min_temperature_); traits.set_visual_max_temperature(this->max_temperature_); diff --git a/esphome/components/yashima/yashima.cpp b/esphome/components/yashima/yashima.cpp index a3cf53ff66..bf91420620 100644 --- a/esphome/components/yashima/yashima.cpp +++ b/esphome/components/yashima/yashima.cpp @@ -81,7 +81,9 @@ const uint32_t YASHIMA_CARRIER_FREQUENCY = 38000; climate::ClimateTraits YashimaClimate::traits() { auto traits = climate::ClimateTraits(); - traits.set_supports_current_temperature(this->sensor_ != nullptr); + if (this->sensor_ != nullptr) { + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + } traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_HEAT_COOL}); if (supports_cool_) @@ -89,7 +91,6 @@ climate::ClimateTraits YashimaClimate::traits() { if (supports_heat_) traits.add_supported_mode(climate::CLIMATE_MODE_HEAT); - traits.set_supports_two_point_target_temperature(false); traits.set_visual_min_temperature(YASHIMA_TEMP_MIN); traits.set_visual_max_temperature(YASHIMA_TEMP_MAX); traits.set_visual_temperature_step(1); From 63f100a8ca8d90e408ef376017327554a768e1b2 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 20 Oct 2025 03:56:25 -0500 Subject: [PATCH 178/526] [bang_bang] Various clean-up (#11356) --- .../bang_bang/bang_bang_climate.cpp | 58 ++++++++++++------- .../components/bang_bang/bang_bang_climate.h | 29 +++++----- 2 files changed, 54 insertions(+), 33 deletions(-) diff --git a/esphome/components/bang_bang/bang_bang_climate.cpp b/esphome/components/bang_bang/bang_bang_climate.cpp index 5d8c0eb7b7..f26377a38a 100644 --- a/esphome/components/bang_bang/bang_bang_climate.cpp +++ b/esphome/components/bang_bang/bang_bang_climate.cpp @@ -6,6 +6,9 @@ namespace bang_bang { static const char *const TAG = "bang_bang.climate"; +BangBangClimate::BangBangClimate() + : idle_trigger_(new Trigger<>()), cool_trigger_(new Trigger<>()), heat_trigger_(new Trigger<>()) {} + void BangBangClimate::setup() { this->sensor_->add_on_state_callback([this](float state) { this->current_temperature = state; @@ -31,54 +34,63 @@ void BangBangClimate::setup() { restore->to_call(this).perform(); } else { // restore from defaults, change_away handles those for us - if (supports_cool_ && supports_heat_) { + if (this->supports_cool_ && this->supports_heat_) { this->mode = climate::CLIMATE_MODE_HEAT_COOL; - } else if (supports_cool_) { + } else if (this->supports_cool_) { this->mode = climate::CLIMATE_MODE_COOL; - } else if (supports_heat_) { + } else if (this->supports_heat_) { this->mode = climate::CLIMATE_MODE_HEAT; } this->change_away_(false); } } + void BangBangClimate::control(const climate::ClimateCall &call) { - if (call.get_mode().has_value()) + if (call.get_mode().has_value()) { this->mode = *call.get_mode(); - if (call.get_target_temperature_low().has_value()) + } + if (call.get_target_temperature_low().has_value()) { this->target_temperature_low = *call.get_target_temperature_low(); - if (call.get_target_temperature_high().has_value()) + } + if (call.get_target_temperature_high().has_value()) { this->target_temperature_high = *call.get_target_temperature_high(); - if (call.get_preset().has_value()) + } + if (call.get_preset().has_value()) { this->change_away_(*call.get_preset() == climate::CLIMATE_PRESET_AWAY); + } this->compute_state_(); this->publish_state(); } + climate::ClimateTraits BangBangClimate::traits() { auto traits = climate::ClimateTraits(); traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE | climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE | climate::CLIMATE_SUPPORTS_ACTION); - - if (this->humidity_sensor_ != nullptr) + if (this->humidity_sensor_ != nullptr) { traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY); - - traits.set_supported_modes({climate::CLIMATE_MODE_OFF}); - if (supports_cool_) + } + traits.set_supported_modes({ + climate::CLIMATE_MODE_OFF, + }); + if (this->supports_cool_) { traits.add_supported_mode(climate::CLIMATE_MODE_COOL); - if (supports_heat_) + } + if (this->supports_heat_) { traits.add_supported_mode(climate::CLIMATE_MODE_HEAT); - if (supports_cool_ && supports_heat_) + } + if (this->supports_cool_ && this->supports_heat_) { traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); - - if (supports_away_) { + } + if (this->supports_away_) { traits.set_supported_presets({ climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_AWAY, }); } - return traits; } + void BangBangClimate::compute_state_() { if (this->mode == climate::CLIMATE_MODE_OFF) { this->switch_to_action_(climate::CLIMATE_ACTION_OFF); @@ -123,6 +135,7 @@ void BangBangClimate::compute_state_() { this->switch_to_action_(target_action); } + void BangBangClimate::switch_to_action_(climate::ClimateAction action) { if (action == this->action) { // already in target mode @@ -167,6 +180,7 @@ void BangBangClimate::switch_to_action_(climate::ClimateAction action) { this->prev_trigger_ = trig; this->publish_state(); } + void BangBangClimate::change_away_(bool away) { if (!away) { this->target_temperature_low = this->normal_config_.default_temperature_low; @@ -177,22 +191,26 @@ void BangBangClimate::change_away_(bool away) { } this->preset = away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME; } + void BangBangClimate::set_normal_config(const BangBangClimateTargetTempConfig &normal_config) { this->normal_config_ = normal_config; } + void BangBangClimate::set_away_config(const BangBangClimateTargetTempConfig &away_config) { this->supports_away_ = true; this->away_config_ = away_config; } -BangBangClimate::BangBangClimate() - : idle_trigger_(new Trigger<>()), cool_trigger_(new Trigger<>()), heat_trigger_(new Trigger<>()) {} + void BangBangClimate::set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; } void BangBangClimate::set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; } + Trigger<> *BangBangClimate::get_idle_trigger() const { return this->idle_trigger_; } Trigger<> *BangBangClimate::get_cool_trigger() const { return this->cool_trigger_; } -void BangBangClimate::set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; } Trigger<> *BangBangClimate::get_heat_trigger() const { return this->heat_trigger_; } + +void BangBangClimate::set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; } void BangBangClimate::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } + void BangBangClimate::dump_config() { LOG_CLIMATE("", "Bang Bang Climate", this); ESP_LOGCONFIG(TAG, diff --git a/esphome/components/bang_bang/bang_bang_climate.h b/esphome/components/bang_bang/bang_bang_climate.h index 96368af34c..2e7da93a07 100644 --- a/esphome/components/bang_bang/bang_bang_climate.h +++ b/esphome/components/bang_bang/bang_bang_climate.h @@ -25,14 +25,15 @@ class BangBangClimate : public climate::Climate, public Component { void set_sensor(sensor::Sensor *sensor); void set_humidity_sensor(sensor::Sensor *humidity_sensor); - Trigger<> *get_idle_trigger() const; - Trigger<> *get_cool_trigger() const; void set_supports_cool(bool supports_cool); - Trigger<> *get_heat_trigger() const; void set_supports_heat(bool supports_heat); void set_normal_config(const BangBangClimateTargetTempConfig &normal_config); void set_away_config(const BangBangClimateTargetTempConfig &away_config); + Trigger<> *get_idle_trigger() const; + Trigger<> *get_cool_trigger() const; + Trigger<> *get_heat_trigger() const; + protected: /// Override control to change settings of the climate device. void control(const climate::ClimateCall &call) override; @@ -56,16 +57,10 @@ class BangBangClimate : public climate::Climate, public Component { * * In idle mode, the controller is assumed to have both heating and cooling disabled. */ - Trigger<> *idle_trigger_; + Trigger<> *idle_trigger_{nullptr}; /** The trigger to call when the controller should switch to cooling mode. */ - Trigger<> *cool_trigger_; - /** Whether the controller supports cooling. - * - * A false value for this attribute means that the controller has no cooling action - * (for example a thermostat, where only heating and not-heating is possible). - */ - bool supports_cool_{false}; + Trigger<> *cool_trigger_{nullptr}; /** The trigger to call when the controller should switch to heating mode. * * A null value for this attribute means that the controller has no heating action @@ -73,15 +68,23 @@ class BangBangClimate : public climate::Climate, public Component { * (blinds open) is possible. */ Trigger<> *heat_trigger_{nullptr}; - bool supports_heat_{false}; /** A reference to the trigger that was previously active. * * This is so that the previous trigger can be stopped before enabling a new one. */ Trigger<> *prev_trigger_{nullptr}; - BangBangClimateTargetTempConfig normal_config_{}; + /** Whether the controller supports cooling/heating + * + * A false value for this attribute means that the controller has no respective action + * (for example a thermostat, where only heating and not-heating is possible). + */ + bool supports_cool_{false}; + bool supports_heat_{false}; + bool supports_away_{false}; + + BangBangClimateTargetTempConfig normal_config_{}; BangBangClimateTargetTempConfig away_config_{}; }; From 03def1391724a25420b9aeae2dd2c5227ac75d88 Mon Sep 17 00:00:00 2001 From: Peter Zich Date: Mon, 20 Oct 2025 06:13:13 -0700 Subject: [PATCH 179/526] [hdc1080] Make HDC1080_CMD_CONFIGURATION failure a warning (and log it) (#11355) Co-authored-by: J. Nick Koston --- esphome/components/hdc1080/hdc1080.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/hdc1080/hdc1080.cpp b/esphome/components/hdc1080/hdc1080.cpp index 71b7cd7e6e..fa293f6fc5 100644 --- a/esphome/components/hdc1080/hdc1080.cpp +++ b/esphome/components/hdc1080/hdc1080.cpp @@ -16,7 +16,8 @@ void HDC1080Component::setup() { // if configuration fails - there is a problem if (this->write_register(HDC1080_CMD_CONFIGURATION, config, 2) != i2c::ERROR_OK) { - this->mark_failed(); + ESP_LOGW(TAG, "Failed to configure HDC1080"); + this->status_set_warning(); return; } } From ca2fe994a17eb420b9243511b839db21f3699efe Mon Sep 17 00:00:00 2001 From: EasilyBoredEngineer <105184462+EasilyBoredEngineer@users.noreply.github.com> Date: Tue, 21 Oct 2025 00:44:20 +1000 Subject: [PATCH 180/526] [espnow] Add transport platform for packet_transport (#11025) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Co-authored-by: J. Nick Koston --- CODEOWNERS | 1 + .../espnow/packet_transport/__init__.py | 39 ++++++++ .../packet_transport/espnow_transport.cpp | 97 +++++++++++++++++++ .../packet_transport/espnow_transport.h | 44 +++++++++ tests/components/espnow/common.yaml | 24 +++++ 5 files changed, 205 insertions(+) create mode 100644 esphome/components/espnow/packet_transport/__init__.py create mode 100644 esphome/components/espnow/packet_transport/espnow_transport.cpp create mode 100644 esphome/components/espnow/packet_transport/espnow_transport.h diff --git a/CODEOWNERS b/CODEOWNERS index 09bd15137a..4f860375d9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -161,6 +161,7 @@ esphome/components/esp32_rmt_led_strip/* @jesserockz esphome/components/esp8266/* @esphome/core esphome/components/esp_ldo/* @clydebarrow esphome/components/espnow/* @jesserockz +esphome/components/espnow/packet_transport/* @EasilyBoredEngineer esphome/components/ethernet_info/* @gtjadsonsantos esphome/components/event/* @nohat esphome/components/exposure_notifications/* @OttoWinter diff --git a/esphome/components/espnow/packet_transport/__init__.py b/esphome/components/espnow/packet_transport/__init__.py new file mode 100644 index 0000000000..e6d66440db --- /dev/null +++ b/esphome/components/espnow/packet_transport/__init__.py @@ -0,0 +1,39 @@ +"""ESP-NOW transport platform for packet_transport component.""" + +import esphome.codegen as cg +from esphome.components.packet_transport import ( + PacketTransport, + new_packet_transport, + transport_schema, +) +import esphome.config_validation as cv +from esphome.core import HexInt +from esphome.cpp_types import PollingComponent + +from .. import ESPNowComponent, espnow_ns + +CODEOWNERS = ["@EasilyBoredEngineer"] +DEPENDENCIES = ["espnow"] + +ESPNowTransport = espnow_ns.class_("ESPNowTransport", PacketTransport, PollingComponent) + +CONF_ESPNOW_ID = "espnow_id" +CONF_PEER_ADDRESS = "peer_address" + +CONFIG_SCHEMA = transport_schema(ESPNowTransport).extend( + { + cv.GenerateID(CONF_ESPNOW_ID): cv.use_id(ESPNowComponent), + cv.Optional(CONF_PEER_ADDRESS, default="FF:FF:FF:FF:FF:FF"): cv.mac_address, + } +) + + +async def to_code(config): + """Set up the ESP-NOW transport component.""" + var, _ = await new_packet_transport(config) + + await cg.register_parented(var, config[CONF_ESPNOW_ID]) + + # Set peer address - convert MAC to parts array like ESP-NOW does + mac = config[CONF_PEER_ADDRESS] + cg.add(var.set_peer_address([HexInt(x) for x in mac.parts])) diff --git a/esphome/components/espnow/packet_transport/espnow_transport.cpp b/esphome/components/espnow/packet_transport/espnow_transport.cpp new file mode 100644 index 0000000000..d30e9447a0 --- /dev/null +++ b/esphome/components/espnow/packet_transport/espnow_transport.cpp @@ -0,0 +1,97 @@ +#include "espnow_transport.h" + +#ifdef USE_ESP32 + +#include "esphome/core/application.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace espnow { + +static const char *const TAG = "espnow.transport"; + +bool ESPNowTransport::should_send() { return this->parent_ != nullptr && !this->parent_->is_failed(); } + +void ESPNowTransport::setup() { + packet_transport::PacketTransport::setup(); + + if (this->parent_ == nullptr) { + ESP_LOGE(TAG, "ESPNow component not set"); + this->mark_failed(); + return; + } + + ESP_LOGI(TAG, "Registering ESP-NOW handlers"); + ESP_LOGI(TAG, "Peer address: %02X:%02X:%02X:%02X:%02X:%02X", this->peer_address_[0], this->peer_address_[1], + this->peer_address_[2], this->peer_address_[3], this->peer_address_[4], this->peer_address_[5]); + + // Register received handler + this->parent_->register_received_handler(static_cast(this)); + + // Register broadcasted handler + this->parent_->register_broadcasted_handler(static_cast(this)); +} + +void ESPNowTransport::update() { + packet_transport::PacketTransport::update(); + this->updated_ = true; +} + +void ESPNowTransport::send_packet(const std::vector &buf) const { + if (this->parent_ == nullptr) { + ESP_LOGE(TAG, "ESPNow component not set"); + return; + } + + if (buf.empty()) { + ESP_LOGW(TAG, "Attempted to send empty packet"); + return; + } + + if (buf.size() > ESP_NOW_MAX_DATA_LEN) { + ESP_LOGE(TAG, "Packet too large: %zu bytes (max %d)", buf.size(), ESP_NOW_MAX_DATA_LEN); + return; + } + + // Send to configured peer address + this->parent_->send(this->peer_address_.data(), buf.data(), buf.size(), [](esp_err_t err) { + if (err != ESP_OK) { + ESP_LOGW(TAG, "Send failed: %d", err); + } + }); +} + +bool ESPNowTransport::on_received(const ESPNowRecvInfo &info, const uint8_t *data, uint8_t size) { + ESP_LOGV(TAG, "Received packet of size %u from %02X:%02X:%02X:%02X:%02X:%02X", size, info.src_addr[0], + info.src_addr[1], info.src_addr[2], info.src_addr[3], info.src_addr[4], info.src_addr[5]); + + if (data == nullptr || size == 0) { + ESP_LOGW(TAG, "Received empty or null packet"); + return false; + } + + this->packet_buffer_.resize(size); + memcpy(this->packet_buffer_.data(), data, size); + this->process_(this->packet_buffer_); + return false; // Allow other handlers to run +} + +bool ESPNowTransport::on_broadcasted(const ESPNowRecvInfo &info, const uint8_t *data, uint8_t size) { + ESP_LOGV(TAG, "Received broadcast packet of size %u from %02X:%02X:%02X:%02X:%02X:%02X", size, info.src_addr[0], + info.src_addr[1], info.src_addr[2], info.src_addr[3], info.src_addr[4], info.src_addr[5]); + + if (data == nullptr || size == 0) { + ESP_LOGW(TAG, "Received empty or null broadcast packet"); + return false; + } + + this->packet_buffer_.resize(size); + memcpy(this->packet_buffer_.data(), data, size); + this->process_(this->packet_buffer_); + return false; // Allow other handlers to run +} + +} // namespace espnow +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/espnow/packet_transport/espnow_transport.h b/esphome/components/espnow/packet_transport/espnow_transport.h new file mode 100644 index 0000000000..3629fad2cd --- /dev/null +++ b/esphome/components/espnow/packet_transport/espnow_transport.h @@ -0,0 +1,44 @@ +#pragma once + +#include "../espnow_component.h" + +#ifdef USE_ESP32 + +#include "esphome/core/component.h" +#include "esphome/components/packet_transport/packet_transport.h" + +#include + +namespace esphome { +namespace espnow { + +class ESPNowTransport : public packet_transport::PacketTransport, + public Parented, + public ESPNowReceivedPacketHandler, + public ESPNowBroadcastedHandler { + public: + void setup() override; + void update() override; + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + + void set_peer_address(peer_address_t address) { + memcpy(this->peer_address_.data(), address.data(), ESP_NOW_ETH_ALEN); + } + + // ESPNow handler interface + bool on_received(const ESPNowRecvInfo &info, const uint8_t *data, uint8_t size) override; + bool on_broadcasted(const ESPNowRecvInfo &info, const uint8_t *data, uint8_t size) override; + + protected: + void send_packet(const std::vector &buf) const override; + size_t get_max_packet_size() override { return ESP_NOW_MAX_DATA_LEN; } + bool should_send() override; + + peer_address_t peer_address_{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; + std::vector packet_buffer_; +}; + +} // namespace espnow +} // namespace esphome + +#endif // USE_ESP32 diff --git a/tests/components/espnow/common.yaml b/tests/components/espnow/common.yaml index abb31c12b8..895ffb9d15 100644 --- a/tests/components/espnow/common.yaml +++ b/tests/components/espnow/common.yaml @@ -1,4 +1,5 @@ espnow: + id: espnow_component auto_add_peer: false channel: 1 peers: @@ -50,3 +51,26 @@ espnow: - format_mac_address_pretty(info.src_addr).c_str() - format_hex_pretty(data, size).c_str() - info.rx_ctrl->rssi + +packet_transport: + - platform: espnow + id: transport1 + espnow_id: espnow_component + peer_address: "FF:FF:FF:FF:FF:FF" + encryption: + key: "0123456789abcdef0123456789abcdef" + sensors: + - temp_sensor + providers: + - name: test_provider + encryption: + key: "0123456789abcdef0123456789abcdef" + +sensor: + - platform: internal_temperature + id: temp_sensor + + - platform: packet_transport + provider: test_provider + remote_id: temp_sensor + id: remote_temp From abb57f08f5ac21085422a9e7a1d34a7720c5b4e9 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 20 Oct 2025 19:08:31 +0200 Subject: [PATCH 181/526] [pipsolar] cleanup / refactoring (#10291) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../pipsolar/binary_sensor/__init__.py | 4 +- .../pipsolar/output/pipsolar_output.cpp | 2 +- esphome/components/pipsolar/pipsolar.cpp | 1157 ++++++++--------- esphome/components/pipsolar/pipsolar.h | 255 ++-- .../pipsolar/switch/pipsolar_switch.cpp | 4 +- 5 files changed, 655 insertions(+), 767 deletions(-) diff --git a/esphome/components/pipsolar/binary_sensor/__init__.py b/esphome/components/pipsolar/binary_sensor/__init__.py index 625c232ed5..5bcf1f75ee 100644 --- a/esphome/components/pipsolar/binary_sensor/__init__.py +++ b/esphome/components/pipsolar/binary_sensor/__init__.py @@ -62,7 +62,7 @@ CONF_WARNING_MPPT_OVERLOAD = "warning_mppt_overload" CONF_WARNING_BATTERY_TOO_LOW_TO_CHARGE = "warning_battery_too_low_to_charge" CONF_FAULT_DC_DC_OVER_CURRENT = "fault_dc_dc_over_current" CONF_FAULT_CODE = "fault_code" -CONF_WARNUNG_LOW_PV_ENERGY = "warnung_low_pv_energy" +CONF_WARNING_LOW_PV_ENERGY = "warning_low_pv_energy" CONF_WARNING_HIGH_AC_INPUT_DURING_BUS_SOFT_START = ( "warning_high_ac_input_during_bus_soft_start" ) @@ -122,7 +122,7 @@ TYPES = [ CONF_WARNING_BATTERY_TOO_LOW_TO_CHARGE, CONF_FAULT_DC_DC_OVER_CURRENT, CONF_FAULT_CODE, - CONF_WARNUNG_LOW_PV_ENERGY, + CONF_WARNING_LOW_PV_ENERGY, CONF_WARNING_HIGH_AC_INPUT_DURING_BUS_SOFT_START, CONF_WARNING_BATTERY_EQUALIZATION, ] diff --git a/esphome/components/pipsolar/output/pipsolar_output.cpp b/esphome/components/pipsolar/output/pipsolar_output.cpp index 00ec73b56a..163fbf4eb2 100644 --- a/esphome/components/pipsolar/output/pipsolar_output.cpp +++ b/esphome/components/pipsolar/output/pipsolar_output.cpp @@ -13,7 +13,7 @@ void PipsolarOutput::write_state(float state) { if (std::find(this->possible_values_.begin(), this->possible_values_.end(), state) != this->possible_values_.end()) { ESP_LOGD(TAG, "Will write: %s out of value %f / %02.0f", tmp, state, state); - this->parent_->switch_command(std::string(tmp)); + this->parent_->queue_command(std::string(tmp)); } else { ESP_LOGD(TAG, "Will not write: %s as it is not in list of allowed values", tmp); } diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index 5751ad59f5..b92cc3be9f 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -65,631 +65,42 @@ void Pipsolar::loop() { } } - if (this->state_ == STATE_POLL_DECODED) { - std::string mode; - switch (this->used_polling_commands_[this->last_polling_command_].identifier) { - case POLLING_QPIRI: - if (this->grid_rating_voltage_) { - this->grid_rating_voltage_->publish_state(value_grid_rating_voltage_); - } - if (this->grid_rating_current_) { - this->grid_rating_current_->publish_state(value_grid_rating_current_); - } - if (this->ac_output_rating_voltage_) { - this->ac_output_rating_voltage_->publish_state(value_ac_output_rating_voltage_); - } - if (this->ac_output_rating_frequency_) { - this->ac_output_rating_frequency_->publish_state(value_ac_output_rating_frequency_); - } - if (this->ac_output_rating_current_) { - this->ac_output_rating_current_->publish_state(value_ac_output_rating_current_); - } - if (this->ac_output_rating_apparent_power_) { - this->ac_output_rating_apparent_power_->publish_state(value_ac_output_rating_apparent_power_); - } - if (this->ac_output_rating_active_power_) { - this->ac_output_rating_active_power_->publish_state(value_ac_output_rating_active_power_); - } - if (this->battery_rating_voltage_) { - this->battery_rating_voltage_->publish_state(value_battery_rating_voltage_); - } - if (this->battery_recharge_voltage_) { - this->battery_recharge_voltage_->publish_state(value_battery_recharge_voltage_); - } - if (this->battery_under_voltage_) { - this->battery_under_voltage_->publish_state(value_battery_under_voltage_); - } - if (this->battery_bulk_voltage_) { - this->battery_bulk_voltage_->publish_state(value_battery_bulk_voltage_); - } - if (this->battery_float_voltage_) { - this->battery_float_voltage_->publish_state(value_battery_float_voltage_); - } - if (this->battery_type_) { - this->battery_type_->publish_state(value_battery_type_); - } - if (this->current_max_ac_charging_current_) { - this->current_max_ac_charging_current_->publish_state(value_current_max_ac_charging_current_); - } - if (this->current_max_charging_current_) { - this->current_max_charging_current_->publish_state(value_current_max_charging_current_); - } - if (this->input_voltage_range_) { - this->input_voltage_range_->publish_state(value_input_voltage_range_); - } - // special for input voltage range switch - if (this->input_voltage_range_switch_) { - this->input_voltage_range_switch_->publish_state(value_input_voltage_range_ == 1); - } - if (this->output_source_priority_) { - this->output_source_priority_->publish_state(value_output_source_priority_); - } - // special for output source priority switches - if (this->output_source_priority_utility_switch_) { - this->output_source_priority_utility_switch_->publish_state(value_output_source_priority_ == 0); - } - if (this->output_source_priority_solar_switch_) { - this->output_source_priority_solar_switch_->publish_state(value_output_source_priority_ == 1); - } - if (this->output_source_priority_battery_switch_) { - this->output_source_priority_battery_switch_->publish_state(value_output_source_priority_ == 2); - } - if (this->output_source_priority_hybrid_switch_) { - this->output_source_priority_hybrid_switch_->publish_state(value_output_source_priority_ == 3); - } - if (this->charger_source_priority_) { - this->charger_source_priority_->publish_state(value_charger_source_priority_); - } - if (this->parallel_max_num_) { - this->parallel_max_num_->publish_state(value_parallel_max_num_); - } - if (this->machine_type_) { - this->machine_type_->publish_state(value_machine_type_); - } - if (this->topology_) { - this->topology_->publish_state(value_topology_); - } - if (this->output_mode_) { - this->output_mode_->publish_state(value_output_mode_); - } - if (this->battery_redischarge_voltage_) { - this->battery_redischarge_voltage_->publish_state(value_battery_redischarge_voltage_); - } - if (this->pv_ok_condition_for_parallel_) { - this->pv_ok_condition_for_parallel_->publish_state(value_pv_ok_condition_for_parallel_); - } - // special for pv ok condition switch - if (this->pv_ok_condition_for_parallel_switch_) { - this->pv_ok_condition_for_parallel_switch_->publish_state(value_pv_ok_condition_for_parallel_ == 1); - } - if (this->pv_power_balance_) { - this->pv_power_balance_->publish_state(value_pv_power_balance_ == 1); - } - // special for power balance switch - if (this->pv_power_balance_switch_) { - this->pv_power_balance_switch_->publish_state(value_pv_power_balance_ == 1); - } - this->state_ = STATE_IDLE; - break; - case POLLING_QPIGS: - if (this->grid_voltage_) { - this->grid_voltage_->publish_state(value_grid_voltage_); - } - if (this->grid_frequency_) { - this->grid_frequency_->publish_state(value_grid_frequency_); - } - if (this->ac_output_voltage_) { - this->ac_output_voltage_->publish_state(value_ac_output_voltage_); - } - if (this->ac_output_frequency_) { - this->ac_output_frequency_->publish_state(value_ac_output_frequency_); - } - if (this->ac_output_apparent_power_) { - this->ac_output_apparent_power_->publish_state(value_ac_output_apparent_power_); - } - if (this->ac_output_active_power_) { - this->ac_output_active_power_->publish_state(value_ac_output_active_power_); - } - if (this->output_load_percent_) { - this->output_load_percent_->publish_state(value_output_load_percent_); - } - if (this->bus_voltage_) { - this->bus_voltage_->publish_state(value_bus_voltage_); - } - if (this->battery_voltage_) { - this->battery_voltage_->publish_state(value_battery_voltage_); - } - if (this->battery_charging_current_) { - this->battery_charging_current_->publish_state(value_battery_charging_current_); - } - if (this->battery_capacity_percent_) { - this->battery_capacity_percent_->publish_state(value_battery_capacity_percent_); - } - if (this->inverter_heat_sink_temperature_) { - this->inverter_heat_sink_temperature_->publish_state(value_inverter_heat_sink_temperature_); - } - if (this->pv_input_current_for_battery_) { - this->pv_input_current_for_battery_->publish_state(value_pv_input_current_for_battery_); - } - if (this->pv_input_voltage_) { - this->pv_input_voltage_->publish_state(value_pv_input_voltage_); - } - if (this->battery_voltage_scc_) { - this->battery_voltage_scc_->publish_state(value_battery_voltage_scc_); - } - if (this->battery_discharge_current_) { - this->battery_discharge_current_->publish_state(value_battery_discharge_current_); - } - if (this->add_sbu_priority_version_) { - this->add_sbu_priority_version_->publish_state(value_add_sbu_priority_version_); - } - if (this->configuration_status_) { - this->configuration_status_->publish_state(value_configuration_status_); - } - if (this->scc_firmware_version_) { - this->scc_firmware_version_->publish_state(value_scc_firmware_version_); - } - if (this->load_status_) { - this->load_status_->publish_state(value_load_status_); - } - if (this->battery_voltage_to_steady_while_charging_) { - this->battery_voltage_to_steady_while_charging_->publish_state( - value_battery_voltage_to_steady_while_charging_); - } - if (this->charging_status_) { - this->charging_status_->publish_state(value_charging_status_); - } - if (this->scc_charging_status_) { - this->scc_charging_status_->publish_state(value_scc_charging_status_); - } - if (this->ac_charging_status_) { - this->ac_charging_status_->publish_state(value_ac_charging_status_); - } - if (this->battery_voltage_offset_for_fans_on_) { - this->battery_voltage_offset_for_fans_on_->publish_state(value_battery_voltage_offset_for_fans_on_ / 10.0f); - } //.1 scale - if (this->eeprom_version_) { - this->eeprom_version_->publish_state(value_eeprom_version_); - } - if (this->pv_charging_power_) { - this->pv_charging_power_->publish_state(value_pv_charging_power_); - } - if (this->charging_to_floating_mode_) { - this->charging_to_floating_mode_->publish_state(value_charging_to_floating_mode_); - } - if (this->switch_on_) { - this->switch_on_->publish_state(value_switch_on_); - } - if (this->dustproof_installed_) { - this->dustproof_installed_->publish_state(value_dustproof_installed_); - } - this->state_ = STATE_IDLE; - break; - case POLLING_QMOD: - if (this->device_mode_) { - mode = value_device_mode_; - this->device_mode_->publish_state(mode); - } - this->state_ = STATE_IDLE; - break; - case POLLING_QFLAG: - if (this->silence_buzzer_open_buzzer_) { - this->silence_buzzer_open_buzzer_->publish_state(value_silence_buzzer_open_buzzer_); - } - if (this->overload_bypass_function_) { - this->overload_bypass_function_->publish_state(value_overload_bypass_function_); - } - if (this->lcd_escape_to_default_) { - this->lcd_escape_to_default_->publish_state(value_lcd_escape_to_default_); - } - if (this->overload_restart_function_) { - this->overload_restart_function_->publish_state(value_overload_restart_function_); - } - if (this->over_temperature_restart_function_) { - this->over_temperature_restart_function_->publish_state(value_over_temperature_restart_function_); - } - if (this->backlight_on_) { - this->backlight_on_->publish_state(value_backlight_on_); - } - if (this->alarm_on_when_primary_source_interrupt_) { - this->alarm_on_when_primary_source_interrupt_->publish_state(value_alarm_on_when_primary_source_interrupt_); - } - if (this->fault_code_record_) { - this->fault_code_record_->publish_state(value_fault_code_record_); - } - if (this->power_saving_) { - this->power_saving_->publish_state(value_power_saving_); - } - this->state_ = STATE_IDLE; - break; - case POLLING_QPIWS: - if (this->warnings_present_) { - this->warnings_present_->publish_state(value_warnings_present_); - } - if (this->faults_present_) { - this->faults_present_->publish_state(value_faults_present_); - } - if (this->warning_power_loss_) { - this->warning_power_loss_->publish_state(value_warning_power_loss_); - } - if (this->fault_inverter_fault_) { - this->fault_inverter_fault_->publish_state(value_fault_inverter_fault_); - } - if (this->fault_bus_over_) { - this->fault_bus_over_->publish_state(value_fault_bus_over_); - } - if (this->fault_bus_under_) { - this->fault_bus_under_->publish_state(value_fault_bus_under_); - } - if (this->fault_bus_soft_fail_) { - this->fault_bus_soft_fail_->publish_state(value_fault_bus_soft_fail_); - } - if (this->warning_line_fail_) { - this->warning_line_fail_->publish_state(value_warning_line_fail_); - } - if (this->fault_opvshort_) { - this->fault_opvshort_->publish_state(value_fault_opvshort_); - } - if (this->fault_inverter_voltage_too_low_) { - this->fault_inverter_voltage_too_low_->publish_state(value_fault_inverter_voltage_too_low_); - } - if (this->fault_inverter_voltage_too_high_) { - this->fault_inverter_voltage_too_high_->publish_state(value_fault_inverter_voltage_too_high_); - } - if (this->warning_over_temperature_) { - this->warning_over_temperature_->publish_state(value_warning_over_temperature_); - } - if (this->warning_fan_lock_) { - this->warning_fan_lock_->publish_state(value_warning_fan_lock_); - } - if (this->warning_battery_voltage_high_) { - this->warning_battery_voltage_high_->publish_state(value_warning_battery_voltage_high_); - } - if (this->warning_battery_low_alarm_) { - this->warning_battery_low_alarm_->publish_state(value_warning_battery_low_alarm_); - } - if (this->warning_battery_under_shutdown_) { - this->warning_battery_under_shutdown_->publish_state(value_warning_battery_under_shutdown_); - } - if (this->warning_battery_derating_) { - this->warning_battery_derating_->publish_state(value_warning_battery_derating_); - } - if (this->warning_over_load_) { - this->warning_over_load_->publish_state(value_warning_over_load_); - } - if (this->warning_eeprom_failed_) { - this->warning_eeprom_failed_->publish_state(value_warning_eeprom_failed_); - } - if (this->fault_inverter_over_current_) { - this->fault_inverter_over_current_->publish_state(value_fault_inverter_over_current_); - } - if (this->fault_inverter_soft_failed_) { - this->fault_inverter_soft_failed_->publish_state(value_fault_inverter_soft_failed_); - } - if (this->fault_self_test_failed_) { - this->fault_self_test_failed_->publish_state(value_fault_self_test_failed_); - } - if (this->fault_op_dc_voltage_over_) { - this->fault_op_dc_voltage_over_->publish_state(value_fault_op_dc_voltage_over_); - } - if (this->fault_battery_open_) { - this->fault_battery_open_->publish_state(value_fault_battery_open_); - } - if (this->fault_current_sensor_failed_) { - this->fault_current_sensor_failed_->publish_state(value_fault_current_sensor_failed_); - } - if (this->fault_battery_short_) { - this->fault_battery_short_->publish_state(value_fault_battery_short_); - } - if (this->warning_power_limit_) { - this->warning_power_limit_->publish_state(value_warning_power_limit_); - } - if (this->warning_pv_voltage_high_) { - this->warning_pv_voltage_high_->publish_state(value_warning_pv_voltage_high_); - } - if (this->fault_mppt_overload_) { - this->fault_mppt_overload_->publish_state(value_fault_mppt_overload_); - } - if (this->warning_mppt_overload_) { - this->warning_mppt_overload_->publish_state(value_warning_mppt_overload_); - } - if (this->warning_battery_too_low_to_charge_) { - this->warning_battery_too_low_to_charge_->publish_state(value_warning_battery_too_low_to_charge_); - } - if (this->fault_dc_dc_over_current_) { - this->fault_dc_dc_over_current_->publish_state(value_fault_dc_dc_over_current_); - } - if (this->fault_code_) { - this->fault_code_->publish_state(value_fault_code_); - } - if (this->warnung_low_pv_energy_) { - this->warnung_low_pv_energy_->publish_state(value_warnung_low_pv_energy_); - } - if (this->warning_high_ac_input_during_bus_soft_start_) { - this->warning_high_ac_input_during_bus_soft_start_->publish_state( - value_warning_high_ac_input_during_bus_soft_start_); - } - if (this->warning_battery_equalization_) { - this->warning_battery_equalization_->publish_state(value_warning_battery_equalization_); - } - this->state_ = STATE_IDLE; - break; - case POLLING_QT: - case POLLING_QMN: - this->state_ = STATE_IDLE; - break; - } - } - if (this->state_ == STATE_POLL_CHECKED) { - bool enabled = true; - std::string fc; - char tmp[PIPSOLAR_READ_BUFFER_LENGTH]; - sprintf(tmp, "%s", this->read_buffer_); - switch (this->used_polling_commands_[this->last_polling_command_].identifier) { + switch (this->enabled_polling_commands_[this->last_polling_command_].identifier) { case POLLING_QPIRI: ESP_LOGD(TAG, "Decode QPIRI"); - sscanf(tmp, "(%f %f %f %f %f %d %d %f %f %f %f %f %d %d %d %d %d %d %d %d %d %d %f %d %d", // NOLINT - &value_grid_rating_voltage_, &value_grid_rating_current_, &value_ac_output_rating_voltage_, // NOLINT - &value_ac_output_rating_frequency_, &value_ac_output_rating_current_, // NOLINT - &value_ac_output_rating_apparent_power_, &value_ac_output_rating_active_power_, // NOLINT - &value_battery_rating_voltage_, &value_battery_recharge_voltage_, // NOLINT - &value_battery_under_voltage_, &value_battery_bulk_voltage_, &value_battery_float_voltage_, // NOLINT - &value_battery_type_, &value_current_max_ac_charging_current_, // NOLINT - &value_current_max_charging_current_, &value_input_voltage_range_, // NOLINT - &value_output_source_priority_, &value_charger_source_priority_, &value_parallel_max_num_, // NOLINT - &value_machine_type_, &value_topology_, &value_output_mode_, // NOLINT - &value_battery_redischarge_voltage_, &value_pv_ok_condition_for_parallel_, // NOLINT - &value_pv_power_balance_); // NOLINT - if (this->last_qpiri_) { - this->last_qpiri_->publish_state(tmp); - } - this->state_ = STATE_POLL_DECODED; + handle_qpiri_((const char *) this->read_buffer_); + this->state_ = STATE_IDLE; break; case POLLING_QPIGS: ESP_LOGD(TAG, "Decode QPIGS"); - sscanf( // NOLINT - tmp, // NOLINT - "(%f %f %f %f %d %d %d %d %f %d %d %d %f %f %f %d %1d%1d%1d%1d%1d%1d%1d%1d %d %d %d %1d%1d%1d", // NOLINT - &value_grid_voltage_, &value_grid_frequency_, &value_ac_output_voltage_, // NOLINT - &value_ac_output_frequency_, // NOLINT - &value_ac_output_apparent_power_, &value_ac_output_active_power_, &value_output_load_percent_, // NOLINT - &value_bus_voltage_, &value_battery_voltage_, &value_battery_charging_current_, // NOLINT - &value_battery_capacity_percent_, &value_inverter_heat_sink_temperature_, // NOLINT - &value_pv_input_current_for_battery_, &value_pv_input_voltage_, &value_battery_voltage_scc_, // NOLINT - &value_battery_discharge_current_, &value_add_sbu_priority_version_, // NOLINT - &value_configuration_status_, &value_scc_firmware_version_, &value_load_status_, // NOLINT - &value_battery_voltage_to_steady_while_charging_, &value_charging_status_, // NOLINT - &value_scc_charging_status_, &value_ac_charging_status_, // NOLINT - &value_battery_voltage_offset_for_fans_on_, &value_eeprom_version_, &value_pv_charging_power_, // NOLINT - &value_charging_to_floating_mode_, &value_switch_on_, // NOLINT - &value_dustproof_installed_); // NOLINT - if (this->last_qpigs_) { - this->last_qpigs_->publish_state(tmp); - } - this->state_ = STATE_POLL_DECODED; + handle_qpigs_((const char *) this->read_buffer_); + this->state_ = STATE_IDLE; break; case POLLING_QMOD: ESP_LOGD(TAG, "Decode QMOD"); - this->value_device_mode_ = char(this->read_buffer_[1]); - if (this->last_qmod_) { - this->last_qmod_->publish_state(tmp); - } - this->state_ = STATE_POLL_DECODED; + handle_qmod_((const char *) this->read_buffer_); + this->state_ = STATE_IDLE; break; case POLLING_QFLAG: ESP_LOGD(TAG, "Decode QFLAG"); - // result like:"(EbkuvxzDajy" - // get through all char: ignore first "(" Enable flag on 'E', Disable on 'D') else set the corresponding value - for (size_t i = 1; i < strlen(tmp); i++) { - switch (tmp[i]) { - case 'E': - enabled = true; - break; - case 'D': - enabled = false; - break; - case 'a': - this->value_silence_buzzer_open_buzzer_ = enabled; - break; - case 'b': - this->value_overload_bypass_function_ = enabled; - break; - case 'k': - this->value_lcd_escape_to_default_ = enabled; - break; - case 'u': - this->value_overload_restart_function_ = enabled; - break; - case 'v': - this->value_over_temperature_restart_function_ = enabled; - break; - case 'x': - this->value_backlight_on_ = enabled; - break; - case 'y': - this->value_alarm_on_when_primary_source_interrupt_ = enabled; - break; - case 'z': - this->value_fault_code_record_ = enabled; - break; - case 'j': - this->value_power_saving_ = enabled; - break; - } - } - if (this->last_qflag_) { - this->last_qflag_->publish_state(tmp); - } - this->state_ = STATE_POLL_DECODED; + handle_qflag_((const char *) this->read_buffer_); + this->state_ = STATE_IDLE; break; case POLLING_QPIWS: ESP_LOGD(TAG, "Decode QPIWS"); - // '(00000000000000000000000000000000' - // iterate over all available flag (as not all models have all flags, but at least in the same order) - this->value_warnings_present_ = false; - this->value_faults_present_ = false; - - for (size_t i = 1; i < strlen(tmp); i++) { - enabled = tmp[i] == '1'; - switch (i) { - case 1: - this->value_warning_power_loss_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 2: - this->value_fault_inverter_fault_ = enabled; - this->value_faults_present_ += enabled; - break; - case 3: - this->value_fault_bus_over_ = enabled; - this->value_faults_present_ += enabled; - break; - case 4: - this->value_fault_bus_under_ = enabled; - this->value_faults_present_ += enabled; - break; - case 5: - this->value_fault_bus_soft_fail_ = enabled; - this->value_faults_present_ += enabled; - break; - case 6: - this->value_warning_line_fail_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 7: - this->value_fault_opvshort_ = enabled; - this->value_faults_present_ += enabled; - break; - case 8: - this->value_fault_inverter_voltage_too_low_ = enabled; - this->value_faults_present_ += enabled; - break; - case 9: - this->value_fault_inverter_voltage_too_high_ = enabled; - this->value_faults_present_ += enabled; - break; - case 10: - this->value_warning_over_temperature_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 11: - this->value_warning_fan_lock_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 12: - this->value_warning_battery_voltage_high_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 13: - this->value_warning_battery_low_alarm_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 15: - this->value_warning_battery_under_shutdown_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 16: - this->value_warning_battery_derating_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 17: - this->value_warning_over_load_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 18: - this->value_warning_eeprom_failed_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 19: - this->value_fault_inverter_over_current_ = enabled; - this->value_faults_present_ += enabled; - break; - case 20: - this->value_fault_inverter_soft_failed_ = enabled; - this->value_faults_present_ += enabled; - break; - case 21: - this->value_fault_self_test_failed_ = enabled; - this->value_faults_present_ += enabled; - break; - case 22: - this->value_fault_op_dc_voltage_over_ = enabled; - this->value_faults_present_ += enabled; - break; - case 23: - this->value_fault_battery_open_ = enabled; - this->value_faults_present_ += enabled; - break; - case 24: - this->value_fault_current_sensor_failed_ = enabled; - this->value_faults_present_ += enabled; - break; - case 25: - this->value_fault_battery_short_ = enabled; - this->value_faults_present_ += enabled; - break; - case 26: - this->value_warning_power_limit_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 27: - this->value_warning_pv_voltage_high_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 28: - this->value_fault_mppt_overload_ = enabled; - this->value_faults_present_ += enabled; - break; - case 29: - this->value_warning_mppt_overload_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 30: - this->value_warning_battery_too_low_to_charge_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 31: - this->value_fault_dc_dc_over_current_ = enabled; - this->value_faults_present_ += enabled; - break; - case 32: - fc = tmp[i]; - fc += tmp[i + 1]; - this->value_fault_code_ = parse_number(fc).value_or(0); - break; - case 34: - this->value_warnung_low_pv_energy_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 35: - this->value_warning_high_ac_input_during_bus_soft_start_ = enabled; - this->value_warnings_present_ += enabled; - break; - case 36: - this->value_warning_battery_equalization_ = enabled; - this->value_warnings_present_ += enabled; - break; - } - } - if (this->last_qpiws_) { - this->last_qpiws_->publish_state(tmp); - } - this->state_ = STATE_POLL_DECODED; + handle_qpiws_((const char *) this->read_buffer_); + this->state_ = STATE_IDLE; break; case POLLING_QT: ESP_LOGD(TAG, "Decode QT"); - if (this->last_qt_) { - this->last_qt_->publish_state(tmp); - } - this->state_ = STATE_POLL_DECODED; + handle_qt_((const char *) this->read_buffer_); + this->state_ = STATE_IDLE; break; case POLLING_QMN: ESP_LOGD(TAG, "Decode QMN"); - if (this->last_qmn_) { - this->last_qmn_->publish_state(tmp); - } - this->state_ = STATE_POLL_DECODED; + handle_qmn_((const char *) this->read_buffer_); + this->state_ = STATE_IDLE; break; default: this->state_ = STATE_IDLE; @@ -706,7 +117,7 @@ void Pipsolar::loop() { return; } // crc ok - this->used_polling_commands_[this->last_polling_command_].needs_update = false; + this->enabled_polling_commands_[this->last_polling_command_].needs_update = false; this->state_ = STATE_POLL_CHECKED; return; } else { @@ -719,9 +130,12 @@ void Pipsolar::loop() { uint8_t byte; this->read_byte(&byte); - if (this->read_pos_ == PIPSOLAR_READ_BUFFER_LENGTH) { + // make sure data and null terminator fit in buffer + if (this->read_pos_ >= PIPSOLAR_READ_BUFFER_LENGTH - 1) { this->read_pos_ = 0; this->empty_uart_buffer_(); + ESP_LOGW(TAG, "response data too long, discarding."); + break; } this->read_buffer_[this->read_pos_] = byte; this->read_pos_++; @@ -755,7 +169,8 @@ void Pipsolar::loop() { if (this->state_ == STATE_POLL) { if (millis() - this->command_start_millis_ > esphome::pipsolar::Pipsolar::COMMAND_TIMEOUT) { // command timeout - ESP_LOGD(TAG, "timeout command to poll: %s", this->used_polling_commands_[this->last_polling_command_].command); + ESP_LOGD(TAG, "timeout command to poll: %s", + this->enabled_polling_commands_[this->last_polling_command_].command); this->state_ = STATE_IDLE; } else { } @@ -786,7 +201,7 @@ uint8_t Pipsolar::check_incoming_crc_() { return 0; } -// send next command used +// send next command from queue bool Pipsolar::send_next_command_() { uint16_t crc16; if (!this->command_queue_[this->command_queue_position_].empty()) { @@ -815,14 +230,13 @@ bool Pipsolar::send_next_command_() { bool Pipsolar::send_next_poll_() { uint16_t crc16; - for (uint8_t i = 0; i < POLLING_COMMANDS_MAX; i++) { this->last_polling_command_ = (this->last_polling_command_ + 1) % POLLING_COMMANDS_MAX; - if (this->used_polling_commands_[this->last_polling_command_].length == 0) { + if (this->enabled_polling_commands_[this->last_polling_command_].length == 0) { // not enabled continue; } - if (!this->used_polling_commands_[this->last_polling_command_].needs_update) { + if (!this->enabled_polling_commands_[this->last_polling_command_].needs_update) { // no update requested continue; } @@ -830,79 +244,530 @@ bool Pipsolar::send_next_poll_() { this->command_start_millis_ = millis(); this->empty_uart_buffer_(); this->read_pos_ = 0; - crc16 = this->pipsolar_crc_(this->used_polling_commands_[this->last_polling_command_].command, - this->used_polling_commands_[this->last_polling_command_].length); - this->write_array(this->used_polling_commands_[this->last_polling_command_].command, - this->used_polling_commands_[this->last_polling_command_].length); + crc16 = this->pipsolar_crc_(this->enabled_polling_commands_[this->last_polling_command_].command, + this->enabled_polling_commands_[this->last_polling_command_].length); + this->write_array(this->enabled_polling_commands_[this->last_polling_command_].command, + this->enabled_polling_commands_[this->last_polling_command_].length); // checksum this->write(((uint8_t) ((crc16) >> 8))); // highbyte this->write(((uint8_t) ((crc16) &0xff))); // lowbyte // end Byte this->write(0x0D); ESP_LOGD(TAG, "Sending polling command : %s with length %d", - this->used_polling_commands_[this->last_polling_command_].command, - this->used_polling_commands_[this->last_polling_command_].length); + this->enabled_polling_commands_[this->last_polling_command_].command, + this->enabled_polling_commands_[this->last_polling_command_].length); return true; } return false; } -void Pipsolar::queue_command_(const char *command, uint8_t length) { +void Pipsolar::queue_command(const std::string &command) { uint8_t next_position = command_queue_position_; for (uint8_t i = 0; i < COMMAND_QUEUE_LENGTH; i++) { uint8_t testposition = (next_position + i) % COMMAND_QUEUE_LENGTH; if (command_queue_[testposition].empty()) { command_queue_[testposition] = command; - ESP_LOGD(TAG, "Command queued successfully: %s with length %u at position %d", command, - command_queue_[testposition].length(), testposition); + ESP_LOGD(TAG, "Command queued successfully: %s at position %d", command.c_str(), testposition); return; } } - ESP_LOGD(TAG, "Command queue full dropping command: %s", command); + ESP_LOGD(TAG, "Command queue full dropping command: %s", command.c_str()); } -void Pipsolar::switch_command(const std::string &command) { - ESP_LOGD(TAG, "got command: %s", command.c_str()); - queue_command_(command.c_str(), command.length()); +void Pipsolar::handle_qpiri_(const char *message) { + if (this->last_qpiri_) { + this->last_qpiri_->publish_state(message); + } + + size_t pos = 0; + this->skip_start_(message, &pos); + + this->read_float_sensor_(message, &pos, this->grid_rating_voltage_); + this->read_float_sensor_(message, &pos, this->grid_rating_current_); + this->read_float_sensor_(message, &pos, this->ac_output_rating_voltage_); + this->read_float_sensor_(message, &pos, this->ac_output_rating_frequency_); + this->read_float_sensor_(message, &pos, this->ac_output_rating_current_); + + this->read_int_sensor_(message, &pos, this->ac_output_rating_apparent_power_); + this->read_int_sensor_(message, &pos, this->ac_output_rating_active_power_); + + this->read_float_sensor_(message, &pos, this->battery_rating_voltage_); + this->read_float_sensor_(message, &pos, this->battery_recharge_voltage_); + this->read_float_sensor_(message, &pos, this->battery_under_voltage_); + this->read_float_sensor_(message, &pos, this->battery_bulk_voltage_); + this->read_float_sensor_(message, &pos, this->battery_float_voltage_); + + this->read_int_sensor_(message, &pos, this->battery_type_); + this->read_int_sensor_(message, &pos, this->current_max_ac_charging_current_); + this->read_int_sensor_(message, &pos, this->current_max_charging_current_); + + esphome::optional input_voltage_range = parse_number(this->read_field_(message, &pos)); + esphome::optional output_source_priority = parse_number(this->read_field_(message, &pos)); + + this->read_int_sensor_(message, &pos, this->charger_source_priority_); + this->read_int_sensor_(message, &pos, this->parallel_max_num_); + this->read_int_sensor_(message, &pos, this->machine_type_); + this->read_int_sensor_(message, &pos, this->topology_); + this->read_int_sensor_(message, &pos, this->output_mode_); + + this->read_float_sensor_(message, &pos, this->battery_redischarge_voltage_); + + esphome::optional pv_ok_condition_for_parallel = parse_number(this->read_field_(message, &pos)); + esphome::optional pv_power_balance = parse_number(this->read_field_(message, &pos)); + + if (this->input_voltage_range_) { + this->input_voltage_range_->publish_state(input_voltage_range.value_or(NAN)); + } + // special for input voltage range switch + if (this->input_voltage_range_switch_ && input_voltage_range.has_value()) { + this->input_voltage_range_switch_->publish_state(input_voltage_range.value() == 1); + } + + if (this->output_source_priority_) { + this->output_source_priority_->publish_state(output_source_priority.value_or(NAN)); + } + // special for output source priority switches + if (this->output_source_priority_utility_switch_ && output_source_priority.has_value()) { + this->output_source_priority_utility_switch_->publish_state(output_source_priority.value() == 0); + } + if (this->output_source_priority_solar_switch_ && output_source_priority.has_value()) { + this->output_source_priority_solar_switch_->publish_state(output_source_priority.value() == 1); + } + if (this->output_source_priority_battery_switch_ && output_source_priority.has_value()) { + this->output_source_priority_battery_switch_->publish_state(output_source_priority.value() == 2); + } + if (this->output_source_priority_hybrid_switch_ && output_source_priority.has_value()) { + this->output_source_priority_hybrid_switch_->publish_state(output_source_priority.value() == 3); + } + + if (this->pv_ok_condition_for_parallel_) { + this->pv_ok_condition_for_parallel_->publish_state(pv_ok_condition_for_parallel.value_or(NAN)); + } + // special for pv ok condition switch + if (this->pv_ok_condition_for_parallel_switch_ && pv_ok_condition_for_parallel.has_value()) { + this->pv_ok_condition_for_parallel_switch_->publish_state(pv_ok_condition_for_parallel.value() == 1); + } + + if (this->pv_power_balance_) { + this->pv_power_balance_->publish_state(pv_power_balance.value_or(NAN)); + } + // special for power balance switch + if (this->pv_power_balance_switch_ && pv_power_balance.has_value()) { + this->pv_power_balance_switch_->publish_state(pv_power_balance.value() == 1); + } } + +void Pipsolar::handle_qpigs_(const char *message) { + if (this->last_qpigs_) { + this->last_qpigs_->publish_state(message); + } + + size_t pos = 0; + this->skip_start_(message, &pos); + + this->read_float_sensor_(message, &pos, this->grid_voltage_); + this->read_float_sensor_(message, &pos, this->grid_frequency_); + this->read_float_sensor_(message, &pos, this->ac_output_voltage_); + this->read_float_sensor_(message, &pos, this->ac_output_frequency_); + + this->read_int_sensor_(message, &pos, this->ac_output_apparent_power_); + this->read_int_sensor_(message, &pos, this->ac_output_active_power_); + this->read_int_sensor_(message, &pos, this->output_load_percent_); + this->read_int_sensor_(message, &pos, this->bus_voltage_); + + this->read_float_sensor_(message, &pos, this->battery_voltage_); + + this->read_int_sensor_(message, &pos, this->battery_charging_current_); + this->read_int_sensor_(message, &pos, this->battery_capacity_percent_); + this->read_int_sensor_(message, &pos, this->inverter_heat_sink_temperature_); + + this->read_float_sensor_(message, &pos, this->pv_input_current_for_battery_); + this->read_float_sensor_(message, &pos, this->pv_input_voltage_); + this->read_float_sensor_(message, &pos, this->battery_voltage_scc_); + + this->read_int_sensor_(message, &pos, this->battery_discharge_current_); + + std::string device_status_1 = this->read_field_(message, &pos); + this->publish_binary_sensor_(this->get_bit_(device_status_1, 0), this->add_sbu_priority_version_); + this->publish_binary_sensor_(this->get_bit_(device_status_1, 1), this->configuration_status_); + this->publish_binary_sensor_(this->get_bit_(device_status_1, 2), this->scc_firmware_version_); + this->publish_binary_sensor_(this->get_bit_(device_status_1, 3), this->load_status_); + this->publish_binary_sensor_(this->get_bit_(device_status_1, 4), this->battery_voltage_to_steady_while_charging_); + this->publish_binary_sensor_(this->get_bit_(device_status_1, 5), this->charging_status_); + this->publish_binary_sensor_(this->get_bit_(device_status_1, 6), this->scc_charging_status_); + this->publish_binary_sensor_(this->get_bit_(device_status_1, 7), this->ac_charging_status_); + + esphome::optional battery_voltage_offset_for_fans_on = parse_number(this->read_field_(message, &pos)); + if (this->battery_voltage_offset_for_fans_on_) { + this->battery_voltage_offset_for_fans_on_->publish_state(battery_voltage_offset_for_fans_on.value_or(NAN) / 10.0f); + } + this->read_int_sensor_(message, &pos, this->eeprom_version_); + this->read_int_sensor_(message, &pos, this->pv_charging_power_); + + std::string device_status_2 = this->read_field_(message, &pos); + this->publish_binary_sensor_(this->get_bit_(device_status_2, 0), this->charging_to_floating_mode_); + this->publish_binary_sensor_(this->get_bit_(device_status_2, 1), this->switch_on_); + this->publish_binary_sensor_(this->get_bit_(device_status_2, 2), this->dustproof_installed_); +} + +void Pipsolar::handle_qmod_(const char *message) { + std::string mode; + char device_mode = char(message[1]); + if (this->last_qmod_) { + this->last_qmod_->publish_state(message); + } + if (this->device_mode_) { + mode = device_mode; + this->device_mode_->publish_state(mode); + } +} + +void Pipsolar::handle_qflag_(const char *message) { + // result like:"(EbkuvxzDajy" + // get through all char: ignore first "(" Enable flag on 'E', Disable on 'D') else set the corresponding value + if (this->last_qflag_) { + this->last_qflag_->publish_state(message); + } + + QFLAGValues values = QFLAGValues(); + bool enabled = true; + for (size_t i = 1; i < strlen(message); i++) { + switch (message[i]) { + case 'E': + enabled = true; + break; + case 'D': + enabled = false; + break; + case 'a': + values.silence_buzzer_open_buzzer = enabled; + break; + case 'b': + values.overload_bypass_function = enabled; + break; + case 'k': + values.lcd_escape_to_default = enabled; + break; + case 'u': + values.overload_restart_function = enabled; + break; + case 'v': + values.over_temperature_restart_function = enabled; + break; + case 'x': + values.backlight_on = enabled; + break; + case 'y': + values.alarm_on_when_primary_source_interrupt = enabled; + break; + case 'z': + values.fault_code_record = enabled; + break; + case 'j': + values.power_saving = enabled; + break; + } + } + + this->publish_binary_sensor_(values.silence_buzzer_open_buzzer, this->silence_buzzer_open_buzzer_); + this->publish_binary_sensor_(values.overload_bypass_function, this->overload_bypass_function_); + this->publish_binary_sensor_(values.lcd_escape_to_default, this->lcd_escape_to_default_); + this->publish_binary_sensor_(values.overload_restart_function, this->overload_restart_function_); + this->publish_binary_sensor_(values.over_temperature_restart_function, this->over_temperature_restart_function_); + this->publish_binary_sensor_(values.backlight_on, this->backlight_on_); + this->publish_binary_sensor_(values.alarm_on_when_primary_source_interrupt, + this->alarm_on_when_primary_source_interrupt_); + this->publish_binary_sensor_(values.fault_code_record, this->fault_code_record_); + this->publish_binary_sensor_(values.power_saving, this->power_saving_); +} + +void Pipsolar::handle_qpiws_(const char *message) { + // '(00000000000000000000000000000000' + // iterate over all available flag (as not all models have all flags, but at least in the same order) + if (this->last_qpiws_) { + this->last_qpiws_->publish_state(message); + } + + size_t pos = 0; + this->skip_start_(message, &pos); + std::string flags = this->read_field_(message, &pos); + + esphome::optional enabled; + bool value_warnings_present = false; + bool value_faults_present = false; + + for (size_t i = 0; i < 36; i++) { + if (i == 31 || i == 32) { + // special case for fault code + continue; + } + enabled = this->get_bit_(flags, i); + switch (i) { + case 0: + this->publish_binary_sensor_(enabled, this->warning_power_loss_); + value_warnings_present |= enabled.value_or(false); + break; + case 1: + this->publish_binary_sensor_(enabled, this->fault_inverter_fault_); + value_faults_present |= enabled.value_or(false); + break; + case 2: + this->publish_binary_sensor_(enabled, this->fault_bus_over_); + value_faults_present |= enabled.value_or(false); + break; + case 3: + this->publish_binary_sensor_(enabled, this->fault_bus_under_); + value_faults_present |= enabled.value_or(false); + break; + case 4: + this->publish_binary_sensor_(enabled, this->fault_bus_soft_fail_); + value_faults_present |= enabled.value_or(false); + break; + case 5: + this->publish_binary_sensor_(enabled, this->warning_line_fail_); + value_warnings_present |= enabled.value_or(false); + break; + case 6: + this->publish_binary_sensor_(enabled, this->fault_opvshort_); + value_faults_present |= enabled.value_or(false); + break; + case 7: + this->publish_binary_sensor_(enabled, this->fault_inverter_voltage_too_low_); + value_faults_present |= enabled.value_or(false); + break; + case 8: + this->publish_binary_sensor_(enabled, this->fault_inverter_voltage_too_high_); + value_faults_present |= enabled.value_or(false); + break; + case 9: + this->publish_binary_sensor_(enabled, this->warning_over_temperature_); + value_warnings_present |= enabled.value_or(false); + break; + case 10: + this->publish_binary_sensor_(enabled, this->warning_fan_lock_); + value_warnings_present |= enabled.value_or(false); + break; + case 11: + this->publish_binary_sensor_(enabled, this->warning_battery_voltage_high_); + value_warnings_present |= enabled.value_or(false); + break; + case 12: + this->publish_binary_sensor_(enabled, this->warning_battery_low_alarm_); + value_warnings_present |= enabled.value_or(false); + break; + case 14: + this->publish_binary_sensor_(enabled, this->warning_battery_under_shutdown_); + value_warnings_present |= enabled.value_or(false); + break; + case 15: + this->publish_binary_sensor_(enabled, this->warning_battery_derating_); + value_warnings_present |= enabled.value_or(false); + break; + case 16: + this->publish_binary_sensor_(enabled, this->warning_over_load_); + value_warnings_present |= enabled.value_or(false); + break; + case 17: + this->publish_binary_sensor_(enabled, this->warning_eeprom_failed_); + value_warnings_present |= enabled.value_or(false); + break; + case 18: + this->publish_binary_sensor_(enabled, this->fault_inverter_over_current_); + value_faults_present |= enabled.value_or(false); + break; + case 19: + this->publish_binary_sensor_(enabled, this->fault_inverter_soft_failed_); + value_faults_present |= enabled.value_or(false); + break; + case 20: + this->publish_binary_sensor_(enabled, this->fault_self_test_failed_); + value_faults_present |= enabled.value_or(false); + break; + case 21: + this->publish_binary_sensor_(enabled, this->fault_op_dc_voltage_over_); + value_faults_present |= enabled.value_or(false); + break; + case 22: + this->publish_binary_sensor_(enabled, this->fault_battery_open_); + value_faults_present |= enabled.value_or(false); + break; + case 23: + this->publish_binary_sensor_(enabled, this->fault_current_sensor_failed_); + value_faults_present |= enabled.value_or(false); + break; + case 24: + this->publish_binary_sensor_(enabled, this->fault_battery_short_); + value_faults_present |= enabled.value_or(false); + break; + case 25: + this->publish_binary_sensor_(enabled, this->warning_power_limit_); + value_warnings_present |= enabled.value_or(false); + break; + case 26: + this->publish_binary_sensor_(enabled, this->warning_pv_voltage_high_); + value_warnings_present |= enabled.value_or(false); + break; + case 27: + this->publish_binary_sensor_(enabled, this->fault_mppt_overload_); + value_faults_present |= enabled.value_or(false); + break; + case 28: + this->publish_binary_sensor_(enabled, this->warning_mppt_overload_); + value_warnings_present |= enabled.value_or(false); + break; + case 29: + this->publish_binary_sensor_(enabled, this->warning_battery_too_low_to_charge_); + value_warnings_present |= enabled.value_or(false); + break; + case 30: + this->publish_binary_sensor_(enabled, this->fault_dc_dc_over_current_); + value_faults_present |= enabled.value_or(false); + break; + case 33: + this->publish_binary_sensor_(enabled, this->warning_low_pv_energy_); + value_warnings_present |= enabled.value_or(false); + break; + case 34: + this->publish_binary_sensor_(enabled, this->warning_high_ac_input_during_bus_soft_start_); + value_warnings_present |= enabled.value_or(false); + case 35: + this->publish_binary_sensor_(enabled, this->warning_battery_equalization_); + value_warnings_present |= enabled.value_or(false); + break; + } + } + + this->publish_binary_sensor_(value_warnings_present, this->warnings_present_); + this->publish_binary_sensor_(value_faults_present, this->faults_present_); + + if (this->fault_code_) { + if (flags.length() < 33) { + this->fault_code_->publish_state(NAN); + } else { + std::string fc(flags, 31, 2); + this->fault_code_->publish_state(parse_number(fc).value_or(NAN)); + } + } +} + +void Pipsolar::handle_qt_(const char *message) { + if (this->last_qt_) { + this->last_qt_->publish_state(message); + } +} + +void Pipsolar::handle_qmn_(const char *message) { + if (this->last_qmn_) { + this->last_qmn_->publish_state(message); + } +} + +void Pipsolar::skip_start_(const char *message, size_t *pos) { + if (message[*pos] == '(') { + (*pos)++; + } +} +void Pipsolar::skip_field_(const char *message, size_t *pos) { + // find delimiter or end of string + while (message[*pos] != '\0' && message[*pos] != ' ') { + (*pos)++; + } + if (message[*pos] != '\0') { + // skip delimiter after this field if there is one + (*pos)++; + } +} +std::string Pipsolar::read_field_(const char *message, size_t *pos) { + size_t begin = *pos; + // find delimiter or end of string + while (message[*pos] != '\0' && message[*pos] != ' ') { + (*pos)++; + } + if (*pos == begin) { + return ""; + } + + std::string field(message, begin, *pos - begin); + + if (message[*pos] != '\0') { + // skip delimiter after this field if there is one + (*pos)++; + } + + return field; +} + +void Pipsolar::read_float_sensor_(const char *message, size_t *pos, sensor::Sensor *sensor) { + if (sensor != nullptr) { + std::string field = this->read_field_(message, pos); + sensor->publish_state(parse_number(field).value_or(NAN)); + } else { + this->skip_field_(message, pos); + } +} +void Pipsolar::read_int_sensor_(const char *message, size_t *pos, sensor::Sensor *sensor) { + if (sensor != nullptr) { + std::string field = this->read_field_(message, pos); + esphome::optional parsed = parse_number(field); + sensor->publish_state(parsed.has_value() ? parsed.value() : NAN); + } else { + this->skip_field_(message, pos); + } +} + +void Pipsolar::publish_binary_sensor_(esphome::optional b, binary_sensor::BinarySensor *sensor) { + if (sensor) { + if (b.has_value()) { + sensor->publish_state(b.value()); + } else { + sensor->invalidate_state(); + } + } +} + +esphome::optional Pipsolar::get_bit_(std::string bits, uint8_t bit_pos) { + if (bit_pos >= bits.length()) { + return {}; + } + return bits[bit_pos] == '1'; +} + void Pipsolar::dump_config() { ESP_LOGCONFIG(TAG, "Pipsolar:\n" - "used commands:"); - for (auto &used_polling_command : this->used_polling_commands_) { - if (used_polling_command.length != 0) { - ESP_LOGCONFIG(TAG, "%s", used_polling_command.command); + "enabled polling commands:"); + for (auto &enabled_polling_command : this->enabled_polling_commands_) { + if (enabled_polling_command.length != 0) { + ESP_LOGCONFIG(TAG, "%s", enabled_polling_command.command); } } } void Pipsolar::update() { - for (auto &used_polling_command : this->used_polling_commands_) { - if (used_polling_command.length != 0) { - used_polling_command.needs_update = true; + for (auto &enabled_polling_command : this->enabled_polling_commands_) { + if (enabled_polling_command.length != 0) { + enabled_polling_command.needs_update = true; } } } void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand polling_command) { - for (auto &used_polling_command : this->used_polling_commands_) { - if (used_polling_command.length == strlen(command)) { + for (auto &enabled_polling_command : this->enabled_polling_commands_) { + if (enabled_polling_command.length == strlen(command)) { uint8_t len = strlen(command); - if (memcmp(used_polling_command.command, command, len) == 0) { + if (memcmp(enabled_polling_command.command, command, len) == 0) { return; } } - if (used_polling_command.length == 0) { - size_t length = strlen(command) + 1; - const char *beg = command; - const char *end = command + length; - used_polling_command.command = new uint8_t[length]; // NOLINT(cppcoreguidelines-owning-memory) - size_t i = 0; - for (; beg != end; ++beg, ++i) { - used_polling_command.command[i] = (uint8_t) (*beg); + if (enabled_polling_command.length == 0) { + size_t length = strlen(command); + + enabled_polling_command.command = new uint8_t[length + 1]; // NOLINT(cppcoreguidelines-owning-memory) + for (size_t i = 0; i < length + 1; i++) { + enabled_polling_command.command[i] = (uint8_t) command[i]; } - used_polling_command.errors = 0; - used_polling_command.identifier = polling_command; - used_polling_command.length = length - 1; - used_polling_command.needs_update = true; + enabled_polling_command.errors = 0; + enabled_polling_command.identifier = polling_command; + enabled_polling_command.length = length; + enabled_polling_command.needs_update = true; return; } } diff --git a/esphome/components/pipsolar/pipsolar.h b/esphome/components/pipsolar/pipsolar.h index 77b18badb9..40056bac9d 100644 --- a/esphome/components/pipsolar/pipsolar.h +++ b/esphome/components/pipsolar/pipsolar.h @@ -7,6 +7,7 @@ #include "esphome/components/uart/uart.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" +#include "esphome/core/helpers.h" namespace esphome { namespace pipsolar { @@ -28,10 +29,17 @@ struct PollingCommand { bool needs_update; }; -#define PIPSOLAR_VALUED_ENTITY_(type, name, polling_command, value_type) \ - protected: \ - value_type value_##name##_; \ - PIPSOLAR_ENTITY_(type, name, polling_command) +struct QFLAGValues { + esphome::optional silence_buzzer_open_buzzer; + esphome::optional overload_bypass_function; + esphome::optional lcd_escape_to_default; + esphome::optional overload_restart_function; + esphome::optional over_temperature_restart_function; + esphome::optional backlight_on; + esphome::optional alarm_on_when_primary_source_interrupt; + esphome::optional fault_code_record; + esphome::optional power_saving; +}; #define PIPSOLAR_ENTITY_(type, name, polling_command) \ protected: \ @@ -43,126 +51,123 @@ struct PollingCommand { this->add_polling_command_(#polling_command, POLLING_##polling_command); \ } -#define PIPSOLAR_SENSOR(name, polling_command, value_type) \ - PIPSOLAR_VALUED_ENTITY_(sensor::Sensor, name, polling_command, value_type) +#define PIPSOLAR_SENSOR(name, polling_command) PIPSOLAR_ENTITY_(sensor::Sensor, name, polling_command) #define PIPSOLAR_SWITCH(name, polling_command) PIPSOLAR_ENTITY_(switch_::Switch, name, polling_command) -#define PIPSOLAR_BINARY_SENSOR(name, polling_command, value_type) \ - PIPSOLAR_VALUED_ENTITY_(binary_sensor::BinarySensor, name, polling_command, value_type) -#define PIPSOLAR_VALUED_TEXT_SENSOR(name, polling_command, value_type) \ - PIPSOLAR_VALUED_ENTITY_(text_sensor::TextSensor, name, polling_command, value_type) +#define PIPSOLAR_BINARY_SENSOR(name, polling_command) \ + PIPSOLAR_ENTITY_(binary_sensor::BinarySensor, name, polling_command) #define PIPSOLAR_TEXT_SENSOR(name, polling_command) PIPSOLAR_ENTITY_(text_sensor::TextSensor, name, polling_command) class Pipsolar : public uart::UARTDevice, public PollingComponent { // QPIGS values - PIPSOLAR_SENSOR(grid_voltage, QPIGS, float) - PIPSOLAR_SENSOR(grid_frequency, QPIGS, float) - PIPSOLAR_SENSOR(ac_output_voltage, QPIGS, float) - PIPSOLAR_SENSOR(ac_output_frequency, QPIGS, float) - PIPSOLAR_SENSOR(ac_output_apparent_power, QPIGS, int) - PIPSOLAR_SENSOR(ac_output_active_power, QPIGS, int) - PIPSOLAR_SENSOR(output_load_percent, QPIGS, int) - PIPSOLAR_SENSOR(bus_voltage, QPIGS, int) - PIPSOLAR_SENSOR(battery_voltage, QPIGS, float) - PIPSOLAR_SENSOR(battery_charging_current, QPIGS, int) - PIPSOLAR_SENSOR(battery_capacity_percent, QPIGS, int) - PIPSOLAR_SENSOR(inverter_heat_sink_temperature, QPIGS, int) - PIPSOLAR_SENSOR(pv_input_current_for_battery, QPIGS, float) - PIPSOLAR_SENSOR(pv_input_voltage, QPIGS, float) - PIPSOLAR_SENSOR(battery_voltage_scc, QPIGS, float) - PIPSOLAR_SENSOR(battery_discharge_current, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(add_sbu_priority_version, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(configuration_status, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(scc_firmware_version, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(load_status, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(battery_voltage_to_steady_while_charging, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(charging_status, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(scc_charging_status, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(ac_charging_status, QPIGS, int) - PIPSOLAR_SENSOR(battery_voltage_offset_for_fans_on, QPIGS, int) //.1 scale - PIPSOLAR_SENSOR(eeprom_version, QPIGS, int) - PIPSOLAR_SENSOR(pv_charging_power, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(charging_to_floating_mode, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(switch_on, QPIGS, int) - PIPSOLAR_BINARY_SENSOR(dustproof_installed, QPIGS, int) + PIPSOLAR_SENSOR(grid_voltage, QPIGS) + PIPSOLAR_SENSOR(grid_frequency, QPIGS) + PIPSOLAR_SENSOR(ac_output_voltage, QPIGS) + PIPSOLAR_SENSOR(ac_output_frequency, QPIGS) + PIPSOLAR_SENSOR(ac_output_apparent_power, QPIGS) + PIPSOLAR_SENSOR(ac_output_active_power, QPIGS) + PIPSOLAR_SENSOR(output_load_percent, QPIGS) + PIPSOLAR_SENSOR(bus_voltage, QPIGS) + PIPSOLAR_SENSOR(battery_voltage, QPIGS) + PIPSOLAR_SENSOR(battery_charging_current, QPIGS) + PIPSOLAR_SENSOR(battery_capacity_percent, QPIGS) + PIPSOLAR_SENSOR(inverter_heat_sink_temperature, QPIGS) + PIPSOLAR_SENSOR(pv_input_current_for_battery, QPIGS) + PIPSOLAR_SENSOR(pv_input_voltage, QPIGS) + PIPSOLAR_SENSOR(battery_voltage_scc, QPIGS) + PIPSOLAR_SENSOR(battery_discharge_current, QPIGS) + PIPSOLAR_BINARY_SENSOR(add_sbu_priority_version, QPIGS) + PIPSOLAR_BINARY_SENSOR(configuration_status, QPIGS) + PIPSOLAR_BINARY_SENSOR(scc_firmware_version, QPIGS) + PIPSOLAR_BINARY_SENSOR(load_status, QPIGS) + PIPSOLAR_BINARY_SENSOR(battery_voltage_to_steady_while_charging, QPIGS) + PIPSOLAR_BINARY_SENSOR(charging_status, QPIGS) + PIPSOLAR_BINARY_SENSOR(scc_charging_status, QPIGS) + PIPSOLAR_BINARY_SENSOR(ac_charging_status, QPIGS) + PIPSOLAR_SENSOR(battery_voltage_offset_for_fans_on, QPIGS) //.1 scale + PIPSOLAR_SENSOR(eeprom_version, QPIGS) + PIPSOLAR_SENSOR(pv_charging_power, QPIGS) + PIPSOLAR_BINARY_SENSOR(charging_to_floating_mode, QPIGS) + PIPSOLAR_BINARY_SENSOR(switch_on, QPIGS) + PIPSOLAR_BINARY_SENSOR(dustproof_installed, QPIGS) // QPIRI values - PIPSOLAR_SENSOR(grid_rating_voltage, QPIRI, float) - PIPSOLAR_SENSOR(grid_rating_current, QPIRI, float) - PIPSOLAR_SENSOR(ac_output_rating_voltage, QPIRI, float) - PIPSOLAR_SENSOR(ac_output_rating_frequency, QPIRI, float) - PIPSOLAR_SENSOR(ac_output_rating_current, QPIRI, float) - PIPSOLAR_SENSOR(ac_output_rating_apparent_power, QPIRI, int) - PIPSOLAR_SENSOR(ac_output_rating_active_power, QPIRI, int) - PIPSOLAR_SENSOR(battery_rating_voltage, QPIRI, float) - PIPSOLAR_SENSOR(battery_recharge_voltage, QPIRI, float) - PIPSOLAR_SENSOR(battery_under_voltage, QPIRI, float) - PIPSOLAR_SENSOR(battery_bulk_voltage, QPIRI, float) - PIPSOLAR_SENSOR(battery_float_voltage, QPIRI, float) - PIPSOLAR_SENSOR(battery_type, QPIRI, int) - PIPSOLAR_SENSOR(current_max_ac_charging_current, QPIRI, int) - PIPSOLAR_SENSOR(current_max_charging_current, QPIRI, int) - PIPSOLAR_SENSOR(input_voltage_range, QPIRI, int) - PIPSOLAR_SENSOR(output_source_priority, QPIRI, int) - PIPSOLAR_SENSOR(charger_source_priority, QPIRI, int) - PIPSOLAR_SENSOR(parallel_max_num, QPIRI, int) - PIPSOLAR_SENSOR(machine_type, QPIRI, int) - PIPSOLAR_SENSOR(topology, QPIRI, int) - PIPSOLAR_SENSOR(output_mode, QPIRI, int) - PIPSOLAR_SENSOR(battery_redischarge_voltage, QPIRI, float) - PIPSOLAR_SENSOR(pv_ok_condition_for_parallel, QPIRI, int) - PIPSOLAR_SENSOR(pv_power_balance, QPIRI, int) + PIPSOLAR_SENSOR(grid_rating_voltage, QPIRI) + PIPSOLAR_SENSOR(grid_rating_current, QPIRI) + PIPSOLAR_SENSOR(ac_output_rating_voltage, QPIRI) + PIPSOLAR_SENSOR(ac_output_rating_frequency, QPIRI) + PIPSOLAR_SENSOR(ac_output_rating_current, QPIRI) + PIPSOLAR_SENSOR(ac_output_rating_apparent_power, QPIRI) + PIPSOLAR_SENSOR(ac_output_rating_active_power, QPIRI) + PIPSOLAR_SENSOR(battery_rating_voltage, QPIRI) + PIPSOLAR_SENSOR(battery_recharge_voltage, QPIRI) + PIPSOLAR_SENSOR(battery_under_voltage, QPIRI) + PIPSOLAR_SENSOR(battery_bulk_voltage, QPIRI) + PIPSOLAR_SENSOR(battery_float_voltage, QPIRI) + PIPSOLAR_SENSOR(battery_type, QPIRI) + PIPSOLAR_SENSOR(current_max_ac_charging_current, QPIRI) + PIPSOLAR_SENSOR(current_max_charging_current, QPIRI) + PIPSOLAR_SENSOR(input_voltage_range, QPIRI) + PIPSOLAR_SENSOR(output_source_priority, QPIRI) + PIPSOLAR_SENSOR(charger_source_priority, QPIRI) + PIPSOLAR_SENSOR(parallel_max_num, QPIRI) + PIPSOLAR_SENSOR(machine_type, QPIRI) + PIPSOLAR_SENSOR(topology, QPIRI) + PIPSOLAR_SENSOR(output_mode, QPIRI) + PIPSOLAR_SENSOR(battery_redischarge_voltage, QPIRI) + PIPSOLAR_SENSOR(pv_ok_condition_for_parallel, QPIRI) + PIPSOLAR_SENSOR(pv_power_balance, QPIRI) // QMOD values - PIPSOLAR_VALUED_TEXT_SENSOR(device_mode, QMOD, char) + PIPSOLAR_TEXT_SENSOR(device_mode, QMOD) // QFLAG values - PIPSOLAR_BINARY_SENSOR(silence_buzzer_open_buzzer, QFLAG, int) - PIPSOLAR_BINARY_SENSOR(overload_bypass_function, QFLAG, int) - PIPSOLAR_BINARY_SENSOR(lcd_escape_to_default, QFLAG, int) - PIPSOLAR_BINARY_SENSOR(overload_restart_function, QFLAG, int) - PIPSOLAR_BINARY_SENSOR(over_temperature_restart_function, QFLAG, int) - PIPSOLAR_BINARY_SENSOR(backlight_on, QFLAG, int) - PIPSOLAR_BINARY_SENSOR(alarm_on_when_primary_source_interrupt, QFLAG, int) - PIPSOLAR_BINARY_SENSOR(fault_code_record, QFLAG, int) - PIPSOLAR_BINARY_SENSOR(power_saving, QFLAG, int) + PIPSOLAR_BINARY_SENSOR(silence_buzzer_open_buzzer, QFLAG) + PIPSOLAR_BINARY_SENSOR(overload_bypass_function, QFLAG) + PIPSOLAR_BINARY_SENSOR(lcd_escape_to_default, QFLAG) + PIPSOLAR_BINARY_SENSOR(overload_restart_function, QFLAG) + PIPSOLAR_BINARY_SENSOR(over_temperature_restart_function, QFLAG) + PIPSOLAR_BINARY_SENSOR(backlight_on, QFLAG) + PIPSOLAR_BINARY_SENSOR(alarm_on_when_primary_source_interrupt, QFLAG) + PIPSOLAR_BINARY_SENSOR(fault_code_record, QFLAG) + PIPSOLAR_BINARY_SENSOR(power_saving, QFLAG) // QPIWS values - PIPSOLAR_BINARY_SENSOR(warnings_present, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(faults_present, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_power_loss, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_inverter_fault, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_bus_over, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_bus_under, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_bus_soft_fail, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_line_fail, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_opvshort, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_inverter_voltage_too_low, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_inverter_voltage_too_high, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_over_temperature, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_fan_lock, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_battery_voltage_high, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_battery_low_alarm, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_battery_under_shutdown, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_battery_derating, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_over_load, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_eeprom_failed, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_inverter_over_current, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_inverter_soft_failed, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_self_test_failed, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_op_dc_voltage_over, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_battery_open, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_current_sensor_failed, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_battery_short, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_power_limit, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_pv_voltage_high, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_mppt_overload, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_mppt_overload, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_battery_too_low_to_charge, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_dc_dc_over_current, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(fault_code, QPIWS, int) - PIPSOLAR_BINARY_SENSOR(warnung_low_pv_energy, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_high_ac_input_during_bus_soft_start, QPIWS, bool) - PIPSOLAR_BINARY_SENSOR(warning_battery_equalization, QPIWS, bool) + PIPSOLAR_BINARY_SENSOR(warnings_present, QPIWS) + PIPSOLAR_BINARY_SENSOR(faults_present, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_power_loss, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_inverter_fault, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_bus_over, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_bus_under, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_bus_soft_fail, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_line_fail, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_opvshort, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_inverter_voltage_too_low, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_inverter_voltage_too_high, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_over_temperature, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_fan_lock, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_battery_voltage_high, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_battery_low_alarm, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_battery_under_shutdown, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_battery_derating, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_over_load, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_eeprom_failed, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_inverter_over_current, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_inverter_soft_failed, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_self_test_failed, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_op_dc_voltage_over, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_battery_open, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_current_sensor_failed, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_battery_short, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_power_limit, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_pv_voltage_high, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_mppt_overload, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_mppt_overload, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_battery_too_low_to_charge, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_dc_dc_over_current, QPIWS) + PIPSOLAR_BINARY_SENSOR(fault_code, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_low_pv_energy, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_high_ac_input_during_bus_soft_start, QPIWS) + PIPSOLAR_BINARY_SENSOR(warning_battery_equalization, QPIWS) PIPSOLAR_TEXT_SENSOR(last_qpigs, QPIGS) PIPSOLAR_TEXT_SENSOR(last_qpiri, QPIRI) @@ -180,14 +185,14 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { PIPSOLAR_SWITCH(pv_ok_condition_for_parallel_switch, QPIRI) PIPSOLAR_SWITCH(pv_power_balance_switch, QPIRI) - void switch_command(const std::string &command); + void queue_command(const std::string &command); void setup() override; void loop() override; void dump_config() override; void update() override; protected: - static const size_t PIPSOLAR_READ_BUFFER_LENGTH = 110; // maximum supported answer length + static const size_t PIPSOLAR_READ_BUFFER_LENGTH = 128; // maximum supported answer length static const size_t COMMAND_QUEUE_LENGTH = 10; static const size_t COMMAND_TIMEOUT = 5000; static const size_t POLLING_COMMANDS_MAX = 15; @@ -198,7 +203,26 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { uint16_t pipsolar_crc_(uint8_t *msg, uint8_t len); bool send_next_command_(); bool send_next_poll_(); - void queue_command_(const char *command, uint8_t length); + + void handle_qpiri_(const char *message); + void handle_qpigs_(const char *message); + void handle_qmod_(const char *message); + void handle_qflag_(const char *message); + void handle_qpiws_(const char *message); + void handle_qt_(const char *message); + void handle_qmn_(const char *message); + + void skip_start_(const char *message, size_t *pos); + void skip_field_(const char *message, size_t *pos); + std::string read_field_(const char *message, size_t *pos); + + void read_float_sensor_(const char *message, size_t *pos, sensor::Sensor *sensor); + void read_int_sensor_(const char *message, size_t *pos, sensor::Sensor *sensor); + + void publish_binary_sensor_(esphome::optional b, binary_sensor::BinarySensor *sensor); + + esphome::optional get_bit_(std::string bits, uint8_t bit_pos); + std::string command_queue_[COMMAND_QUEUE_LENGTH]; uint8_t command_queue_position_ = 0; uint8_t read_buffer_[PIPSOLAR_READ_BUFFER_LENGTH]; @@ -213,11 +237,10 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { STATE_POLL_COMPLETE = 3, STATE_COMMAND_COMPLETE = 4, STATE_POLL_CHECKED = 5, - STATE_POLL_DECODED = 6, }; uint8_t last_polling_command_ = 0; - PollingCommand used_polling_commands_[POLLING_COMMANDS_MAX]; + PollingCommand enabled_polling_commands_[POLLING_COMMANDS_MAX]; }; } // namespace pipsolar diff --git a/esphome/components/pipsolar/switch/pipsolar_switch.cpp b/esphome/components/pipsolar/switch/pipsolar_switch.cpp index be7763226b..649d951618 100644 --- a/esphome/components/pipsolar/switch/pipsolar_switch.cpp +++ b/esphome/components/pipsolar/switch/pipsolar_switch.cpp @@ -11,11 +11,11 @@ void PipsolarSwitch::dump_config() { LOG_SWITCH("", "Pipsolar Switch", this); } void PipsolarSwitch::write_state(bool state) { if (state) { if (!this->on_command_.empty()) { - this->parent_->switch_command(this->on_command_); + this->parent_->queue_command(this->on_command_); } } else { if (!this->off_command_.empty()) { - this->parent_->switch_command(this->off_command_); + this->parent_->queue_command(this->off_command_); } } } From e988905c2f498634a6eabb11718d3dd9a6dbf454 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 08:31:59 -1000 Subject: [PATCH 182/526] [json] Add basic compile tests (#11409) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/components/json/common.yaml | 33 +++++++++++++++++++++ tests/components/json/test.esp32-idf.yaml | 1 + tests/components/json/test.esp8266-ard.yaml | 1 + 3 files changed, 35 insertions(+) create mode 100644 tests/components/json/common.yaml create mode 100644 tests/components/json/test.esp32-idf.yaml create mode 100644 tests/components/json/test.esp8266-ard.yaml diff --git a/tests/components/json/common.yaml b/tests/components/json/common.yaml new file mode 100644 index 0000000000..f4074e1172 --- /dev/null +++ b/tests/components/json/common.yaml @@ -0,0 +1,33 @@ +json: + +interval: + - interval: 60s + then: + - lambda: |- + // Test build_json + std::string json_str = esphome::json::build_json([](JsonObject root) { + root["sensor"] = "temperature"; + root["value"] = 23.5; + root["unit"] = "°C"; + }); + ESP_LOGD("test", "Built JSON: %s", json_str.c_str()); + + // Test parse_json + bool parse_ok = esphome::json::parse_json(json_str, [](JsonObject root) { + if (root.containsKey("sensor") && root.containsKey("value")) { + const char* sensor = root["sensor"]; + float value = root["value"]; + ESP_LOGD("test", "Parsed: sensor=%s, value=%.1f", sensor, value); + } else { + ESP_LOGD("test", "Parsed JSON missing required keys"); + } + }); + ESP_LOGD("test", "Parse result (JSON syntax only): %s", parse_ok ? "success" : "failed"); + + // Test JsonBuilder class + esphome::json::JsonBuilder builder; + JsonObject obj = builder.root(); + obj["test"] = "direct_builder"; + obj["count"] = 42; + std::string result = builder.serialize(); + ESP_LOGD("test", "JsonBuilder result: %s", result.c_str()); diff --git a/tests/components/json/test.esp32-idf.yaml b/tests/components/json/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/json/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/json/test.esp8266-ard.yaml b/tests/components/json/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/json/test.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 46101fd8308d31c2c46c5c161fc320baafa889c0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 09:25:03 -1000 Subject: [PATCH 183/526] Add tests for FilterOutValueFilter and ThrottleWithPriorityFilter (#11408) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/components/sensor/common.yaml | 74 ++++ .../fixtures/sensor_filters_value_list.yaml | 332 ++++++++++++++++++ .../test_sensor_filters_value_list.py | 263 ++++++++++++++ 3 files changed, 669 insertions(+) create mode 100644 tests/integration/fixtures/sensor_filters_value_list.yaml create mode 100644 tests/integration/test_sensor_filters_value_list.py diff --git a/tests/components/sensor/common.yaml b/tests/components/sensor/common.yaml index ace7d0a38a..3f81f3f9ef 100644 --- a/tests/components/sensor/common.yaml +++ b/tests/components/sensor/common.yaml @@ -99,3 +99,77 @@ sensor: window_size: 10 send_every: 10 send_first_at: 1 # Send after first value + + # ValueListFilter-based filters tests + # FilterOutValueFilter - single value + - platform: copy + source_id: source_sensor + name: "Filter Out Single Value" + filters: + - filter_out: 42.0 # Should filter out exactly 42.0 + + # FilterOutValueFilter - multiple values + - platform: copy + source_id: source_sensor + name: "Filter Out Multiple Values" + filters: + - filter_out: [0.0, 42.0, 100.0] # List of values to filter + + # FilterOutValueFilter - with NaN + - platform: copy + source_id: source_sensor + name: "Filter Out NaN" + filters: + - filter_out: nan # Filter out NaN values + + # FilterOutValueFilter - mixed values with NaN + - platform: copy + source_id: source_sensor + name: "Filter Out Mixed with NaN" + filters: + - filter_out: [nan, 0.0, 42.0] + + # ThrottleWithPriorityFilter - single priority value + - platform: copy + source_id: source_sensor + name: "Throttle with Single Priority" + filters: + - throttle_with_priority: + timeout: 1000ms + value: 42.0 # Priority value bypasses throttle + + # ThrottleWithPriorityFilter - multiple priority values + - platform: copy + source_id: source_sensor + name: "Throttle with Multiple Priorities" + filters: + - throttle_with_priority: + timeout: 500ms + value: [0.0, 42.0, 100.0] # Multiple priority values + + # ThrottleWithPriorityFilter - with NaN priority + - platform: copy + source_id: source_sensor + name: "Throttle with NaN Priority" + filters: + - throttle_with_priority: + timeout: 1000ms + value: nan # NaN as priority value + + # Combined filters - FilterOutValueFilter + other filters + - platform: copy + source_id: source_sensor + name: "Filter Out Then Throttle" + filters: + - filter_out: [0.0, 100.0] + - throttle: 500ms + + # Combined filters - ThrottleWithPriorityFilter + other filters + - platform: copy + source_id: source_sensor + name: "Throttle Priority Then Scale" + filters: + - throttle_with_priority: + timeout: 1000ms + value: [42.0] + - multiply: 2.0 diff --git a/tests/integration/fixtures/sensor_filters_value_list.yaml b/tests/integration/fixtures/sensor_filters_value_list.yaml new file mode 100644 index 0000000000..2b796a5be1 --- /dev/null +++ b/tests/integration/fixtures/sensor_filters_value_list.yaml @@ -0,0 +1,332 @@ +esphome: + name: test-value-list-filters + +host: +api: + batch_delay: 0ms # Disable batching to receive all state updates +logger: + level: DEBUG + +# Template sensors - one for each test to avoid cross-test interference +sensor: + - platform: template + name: "Source Sensor 1" + id: source_sensor_1 + accuracy_decimals: 1 + + - platform: template + name: "Source Sensor 2" + id: source_sensor_2 + accuracy_decimals: 1 + + - platform: template + name: "Source Sensor 3" + id: source_sensor_3 + accuracy_decimals: 1 + + - platform: template + name: "Source Sensor 4" + id: source_sensor_4 + accuracy_decimals: 1 + + - platform: template + name: "Source Sensor 5" + id: source_sensor_5 + accuracy_decimals: 1 + + - platform: template + name: "Source Sensor 6" + id: source_sensor_6 + accuracy_decimals: 2 + + - platform: template + name: "Source Sensor 7" + id: source_sensor_7 + accuracy_decimals: 1 + + # FilterOutValueFilter - single value + - platform: copy + source_id: source_sensor_1 + name: "Filter Out Single" + id: filter_out_single + filters: + - filter_out: 42.0 + + # FilterOutValueFilter - multiple values + - platform: copy + source_id: source_sensor_2 + name: "Filter Out Multiple" + id: filter_out_multiple + filters: + - filter_out: [0.0, 42.0, 100.0] + + # FilterOutValueFilter - with NaN + - platform: copy + source_id: source_sensor_1 + name: "Filter Out NaN" + id: filter_out_nan + filters: + - filter_out: nan + + # ThrottleWithPriorityFilter - single priority value + - platform: copy + source_id: source_sensor_3 + name: "Throttle Priority Single" + id: throttle_priority_single + filters: + - throttle_with_priority: + timeout: 200ms + value: 42.0 + + # ThrottleWithPriorityFilter - multiple priority values + - platform: copy + source_id: source_sensor_4 + name: "Throttle Priority Multiple" + id: throttle_priority_multiple + filters: + - throttle_with_priority: + timeout: 200ms + value: [0.0, 42.0, 100.0] + + # Edge case: Filter Out NaN explicitly + - platform: copy + source_id: source_sensor_5 + name: "Filter Out NaN Test" + id: filter_out_nan_test + filters: + - filter_out: nan + + # Edge case: Accuracy decimals - 2 decimals + - platform: copy + source_id: source_sensor_6 + name: "Filter Out Accuracy 2" + id: filter_out_accuracy_2 + filters: + - filter_out: 42.0 + + # Edge case: Throttle with NaN priority + - platform: copy + source_id: source_sensor_7 + name: "Throttle Priority NaN" + id: throttle_priority_nan + filters: + - throttle_with_priority: + timeout: 200ms + value: nan + +# Script to test FilterOutValueFilter +script: + - id: test_filter_out_single + then: + # Should pass through: 1.0, 2.0, 3.0 + # Should filter out: 42.0 + - sensor.template.publish: + id: source_sensor_1 + state: 1.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor_1 + state: 42.0 # Filtered out + - delay: 20ms + - sensor.template.publish: + id: source_sensor_1 + state: 2.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor_1 + state: 42.0 # Filtered out + - delay: 20ms + - sensor.template.publish: + id: source_sensor_1 + state: 3.0 + + - id: test_filter_out_multiple + then: + # Should filter out: 0.0, 42.0, 100.0 + # Should pass through: 1.0, 2.0, 50.0 + - sensor.template.publish: + id: source_sensor_2 + state: 0.0 # Filtered out + - delay: 20ms + - sensor.template.publish: + id: source_sensor_2 + state: 1.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor_2 + state: 42.0 # Filtered out + - delay: 20ms + - sensor.template.publish: + id: source_sensor_2 + state: 2.0 + - delay: 20ms + - sensor.template.publish: + id: source_sensor_2 + state: 100.0 # Filtered out + - delay: 20ms + - sensor.template.publish: + id: source_sensor_2 + state: 50.0 + + - id: test_throttle_priority_single + then: + # 42.0 bypasses throttle, other values are throttled + - sensor.template.publish: + id: source_sensor_3 + state: 1.0 # First value - passes + - delay: 50ms + - sensor.template.publish: + id: source_sensor_3 + state: 2.0 # Throttled + - delay: 50ms + - sensor.template.publish: + id: source_sensor_3 + state: 42.0 # Priority - passes immediately + - delay: 50ms + - sensor.template.publish: + id: source_sensor_3 + state: 3.0 # Throttled + - delay: 250ms # Wait for throttle to expire + - sensor.template.publish: + id: source_sensor_3 + state: 4.0 # Passes after timeout + + - id: test_throttle_priority_multiple + then: + # 0.0, 42.0, 100.0 bypass throttle + - sensor.template.publish: + id: source_sensor_4 + state: 1.0 # First value - passes + - delay: 50ms + - sensor.template.publish: + id: source_sensor_4 + state: 2.0 # Throttled + - delay: 50ms + - sensor.template.publish: + id: source_sensor_4 + state: 0.0 # Priority - passes + - delay: 50ms + - sensor.template.publish: + id: source_sensor_4 + state: 3.0 # Throttled + - delay: 50ms + - sensor.template.publish: + id: source_sensor_4 + state: 42.0 # Priority - passes + - delay: 50ms + - sensor.template.publish: + id: source_sensor_4 + state: 4.0 # Throttled + - delay: 50ms + - sensor.template.publish: + id: source_sensor_4 + state: 100.0 # Priority - passes + + - id: test_filter_out_nan + then: + # NaN should be filtered out, regular values pass + - sensor.template.publish: + id: source_sensor_5 + state: 1.0 # Pass + - delay: 20ms + - sensor.template.publish: + id: source_sensor_5 + state: !lambda 'return NAN;' # Filtered out + - delay: 20ms + - sensor.template.publish: + id: source_sensor_5 + state: 2.0 # Pass + - delay: 20ms + - sensor.template.publish: + id: source_sensor_5 + state: !lambda 'return NAN;' # Filtered out + - delay: 20ms + - sensor.template.publish: + id: source_sensor_5 + state: 3.0 # Pass + + - id: test_filter_out_accuracy_2 + then: + # With 2 decimal places, 42.00 filtered, 42.01 and 42.15 pass + - sensor.template.publish: + id: source_sensor_6 + state: 42.0 # Filtered (rounds to 42.00) + - delay: 20ms + - sensor.template.publish: + id: source_sensor_6 + state: 42.01 # Pass (rounds to 42.01) + - delay: 20ms + - sensor.template.publish: + id: source_sensor_6 + state: 42.15 # Pass (rounds to 42.15) + - delay: 20ms + - sensor.template.publish: + id: source_sensor_6 + state: 42.0 # Filtered (rounds to 42.00) + + - id: test_throttle_priority_nan + then: + # NaN bypasses throttle, regular values throttled + - sensor.template.publish: + id: source_sensor_7 + state: 1.0 # First value - passes + - delay: 50ms + - sensor.template.publish: + id: source_sensor_7 + state: 2.0 # Throttled + - delay: 50ms + - sensor.template.publish: + id: source_sensor_7 + state: !lambda 'return NAN;' # Priority NaN - passes + - delay: 50ms + - sensor.template.publish: + id: source_sensor_7 + state: 3.0 # Throttled + - delay: 50ms + - sensor.template.publish: + id: source_sensor_7 + state: !lambda 'return NAN;' # Priority NaN - passes + +# Buttons to trigger each test +button: + - platform: template + name: "Test Filter Out Single" + id: btn_filter_out_single + on_press: + - script.execute: test_filter_out_single + + - platform: template + name: "Test Filter Out Multiple" + id: btn_filter_out_multiple + on_press: + - script.execute: test_filter_out_multiple + + - platform: template + name: "Test Throttle Priority Single" + id: btn_throttle_priority_single + on_press: + - script.execute: test_throttle_priority_single + + - platform: template + name: "Test Throttle Priority Multiple" + id: btn_throttle_priority_multiple + on_press: + - script.execute: test_throttle_priority_multiple + + - platform: template + name: "Test Filter Out NaN" + id: btn_filter_out_nan + on_press: + - script.execute: test_filter_out_nan + + - platform: template + name: "Test Filter Out Accuracy 2" + id: btn_filter_out_accuracy_2 + on_press: + - script.execute: test_filter_out_accuracy_2 + + - platform: template + name: "Test Throttle Priority NaN" + id: btn_throttle_priority_nan + on_press: + - script.execute: test_throttle_priority_nan diff --git a/tests/integration/test_sensor_filters_value_list.py b/tests/integration/test_sensor_filters_value_list.py new file mode 100644 index 0000000000..87323fc730 --- /dev/null +++ b/tests/integration/test_sensor_filters_value_list.py @@ -0,0 +1,263 @@ +"""Test sensor ValueListFilter functionality (FilterOutValueFilter and ThrottleWithPriorityFilter).""" + +from __future__ import annotations + +import asyncio +import math + +from aioesphomeapi import ButtonInfo, EntityState, SensorState +import pytest + +from .state_utils import InitialStateHelper, build_key_to_entity_mapping +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_sensor_filters_value_list( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that ValueListFilter-based filters work correctly.""" + loop = asyncio.get_running_loop() + + # Track state changes for all sensors + sensor_values: dict[str, list[float]] = { + "filter_out_single": [], + "filter_out_multiple": [], + "throttle_priority_single": [], + "throttle_priority_multiple": [], + "filter_out_nan_test": [], + "filter_out_accuracy_2": [], + "throttle_priority_nan": [], + } + + # Futures for each test + filter_out_single_done = loop.create_future() + filter_out_multiple_done = loop.create_future() + throttle_single_done = loop.create_future() + throttle_multiple_done = loop.create_future() + filter_out_nan_done = loop.create_future() + filter_out_accuracy_2_done = loop.create_future() + throttle_nan_done = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track sensor state updates.""" + if not isinstance(state, SensorState) or state.missing_state: + return + + sensor_name = key_to_sensor.get(state.key) + if sensor_name not in sensor_values: + return + + sensor_values[sensor_name].append(state.state) + + # Check completion conditions + if ( + sensor_name == "filter_out_single" + and len(sensor_values[sensor_name]) == 3 + and not filter_out_single_done.done() + ): + filter_out_single_done.set_result(True) + elif ( + sensor_name == "filter_out_multiple" + and len(sensor_values[sensor_name]) == 3 + and not filter_out_multiple_done.done() + ): + filter_out_multiple_done.set_result(True) + elif ( + sensor_name == "throttle_priority_single" + and len(sensor_values[sensor_name]) == 3 + and not throttle_single_done.done() + ): + throttle_single_done.set_result(True) + elif ( + sensor_name == "throttle_priority_multiple" + and len(sensor_values[sensor_name]) == 4 + and not throttle_multiple_done.done() + ): + throttle_multiple_done.set_result(True) + elif ( + sensor_name == "filter_out_nan_test" + and len(sensor_values[sensor_name]) == 3 + and not filter_out_nan_done.done() + ): + filter_out_nan_done.set_result(True) + elif ( + sensor_name == "filter_out_accuracy_2" + and len(sensor_values[sensor_name]) == 2 + and not filter_out_accuracy_2_done.done() + ): + filter_out_accuracy_2_done.set_result(True) + elif ( + sensor_name == "throttle_priority_nan" + and len(sensor_values[sensor_name]) == 3 + and not throttle_nan_done.done() + ): + throttle_nan_done.set_result(True) + + async with ( + run_compiled(yaml_config), + api_client_connected() as client, + ): + # Get entities and build key mapping + entities, _ = await client.list_entities_services() + key_to_sensor = build_key_to_entity_mapping( + entities, + { + "filter_out_single": "Filter Out Single", + "filter_out_multiple": "Filter Out Multiple", + "throttle_priority_single": "Throttle Priority Single", + "throttle_priority_multiple": "Throttle Priority Multiple", + "filter_out_nan_test": "Filter Out NaN Test", + "filter_out_accuracy_2": "Filter Out Accuracy 2", + "throttle_priority_nan": "Throttle Priority NaN", + }, + ) + + # Set up initial state helper with all entities + initial_state_helper = InitialStateHelper(entities) + + # Subscribe to state changes with wrapper + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + + # Wait for initial states + await initial_state_helper.wait_for_initial_states() + + # Find all buttons + button_name_map = { + "Test Filter Out Single": "filter_out_single", + "Test Filter Out Multiple": "filter_out_multiple", + "Test Throttle Priority Single": "throttle_priority_single", + "Test Throttle Priority Multiple": "throttle_priority_multiple", + "Test Filter Out NaN": "filter_out_nan", + "Test Filter Out Accuracy 2": "filter_out_accuracy_2", + "Test Throttle Priority NaN": "throttle_priority_nan", + } + buttons = {} + for entity in entities: + if isinstance(entity, ButtonInfo) and entity.name in button_name_map: + buttons[button_name_map[entity.name]] = entity.key + + assert len(buttons) == 7, f"Expected 7 buttons, found {len(buttons)}" + + # Test 1: FilterOutValueFilter - single value + sensor_values["filter_out_single"].clear() + client.button_command(buttons["filter_out_single"]) + try: + await asyncio.wait_for(filter_out_single_done, timeout=2.0) + except TimeoutError: + pytest.fail( + f"Test 1 timed out. Values: {sensor_values['filter_out_single']}" + ) + + expected = [1.0, 2.0, 3.0] + assert sensor_values["filter_out_single"] == pytest.approx(expected), ( + f"Test 1 failed: expected {expected}, got {sensor_values['filter_out_single']}" + ) + + # Test 2: FilterOutValueFilter - multiple values + sensor_values["filter_out_multiple"].clear() + filter_out_multiple_done = loop.create_future() + client.button_command(buttons["filter_out_multiple"]) + try: + await asyncio.wait_for(filter_out_multiple_done, timeout=2.0) + except TimeoutError: + pytest.fail( + f"Test 2 timed out. Values: {sensor_values['filter_out_multiple']}" + ) + + expected = [1.0, 2.0, 50.0] + assert sensor_values["filter_out_multiple"] == pytest.approx(expected), ( + f"Test 2 failed: expected {expected}, got {sensor_values['filter_out_multiple']}" + ) + + # Test 3: ThrottleWithPriorityFilter - single priority + sensor_values["throttle_priority_single"].clear() + throttle_single_done = loop.create_future() + client.button_command(buttons["throttle_priority_single"]) + try: + await asyncio.wait_for(throttle_single_done, timeout=2.0) + except TimeoutError: + pytest.fail( + f"Test 3 timed out. Values: {sensor_values['throttle_priority_single']}" + ) + + expected = [1.0, 42.0, 4.0] + assert sensor_values["throttle_priority_single"] == pytest.approx(expected), ( + f"Test 3 failed: expected {expected}, got {sensor_values['throttle_priority_single']}" + ) + + # Test 4: ThrottleWithPriorityFilter - multiple priorities + sensor_values["throttle_priority_multiple"].clear() + throttle_multiple_done = loop.create_future() + client.button_command(buttons["throttle_priority_multiple"]) + try: + await asyncio.wait_for(throttle_multiple_done, timeout=2.0) + except TimeoutError: + pytest.fail( + f"Test 4 timed out. Values: {sensor_values['throttle_priority_multiple']}" + ) + + expected = [1.0, 0.0, 42.0, 100.0] + assert sensor_values["throttle_priority_multiple"] == pytest.approx(expected), ( + f"Test 4 failed: expected {expected}, got {sensor_values['throttle_priority_multiple']}" + ) + + # Test 5: FilterOutValueFilter - NaN handling + sensor_values["filter_out_nan_test"].clear() + filter_out_nan_done = loop.create_future() + client.button_command(buttons["filter_out_nan"]) + try: + await asyncio.wait_for(filter_out_nan_done, timeout=2.0) + except TimeoutError: + pytest.fail( + f"Test 5 timed out. Values: {sensor_values['filter_out_nan_test']}" + ) + + expected = [1.0, 2.0, 3.0] + assert sensor_values["filter_out_nan_test"] == pytest.approx(expected), ( + f"Test 5 failed: expected {expected}, got {sensor_values['filter_out_nan_test']}" + ) + + # Test 6: FilterOutValueFilter - Accuracy decimals (2) + sensor_values["filter_out_accuracy_2"].clear() + filter_out_accuracy_2_done = loop.create_future() + client.button_command(buttons["filter_out_accuracy_2"]) + try: + await asyncio.wait_for(filter_out_accuracy_2_done, timeout=2.0) + except TimeoutError: + pytest.fail( + f"Test 6 timed out. Values: {sensor_values['filter_out_accuracy_2']}" + ) + + expected = [42.01, 42.15] + assert sensor_values["filter_out_accuracy_2"] == pytest.approx(expected), ( + f"Test 6 failed: expected {expected}, got {sensor_values['filter_out_accuracy_2']}" + ) + + # Test 7: ThrottleWithPriorityFilter - NaN priority + sensor_values["throttle_priority_nan"].clear() + throttle_nan_done = loop.create_future() + client.button_command(buttons["throttle_priority_nan"]) + try: + await asyncio.wait_for(throttle_nan_done, timeout=2.0) + except TimeoutError: + pytest.fail( + f"Test 7 timed out. Values: {sensor_values['throttle_priority_nan']}" + ) + + # First value (1.0) + two NaN priority values + # NaN values will be compared using math.isnan + assert len(sensor_values["throttle_priority_nan"]) == 3, ( + f"Test 7 failed: expected 3 values, got {len(sensor_values['throttle_priority_nan'])}" + ) + assert sensor_values["throttle_priority_nan"][0] == pytest.approx(1.0), ( + f"Test 7 failed: first value should be 1.0, got {sensor_values['throttle_priority_nan'][0]}" + ) + assert math.isnan(sensor_values["throttle_priority_nan"][1]), ( + f"Test 7 failed: second value should be NaN, got {sensor_values['throttle_priority_nan'][1]}" + ) + assert math.isnan(sensor_values["throttle_priority_nan"][2]), ( + f"Test 7 failed: third value should be NaN, got {sensor_values['throttle_priority_nan'][2]}" + ) From e23d66a8cf9b260e4b06152009587e87885f1904 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 10:38:34 -1000 Subject: [PATCH 184/526] [esp32] Automatic CONFIG_LWIP_MAX_SOCKETS configuration based on component needs (#11378) --- esphome/components/api/__init__.py | 12 +++ esphome/components/esp32/__init__.py | 74 +++++++++++++++++++ .../esp32_camera_web_server/__init__.py | 29 ++++++-- esphome/components/esphome/ota/__init__.py | 14 +++- esphome/components/mdns/__init__.py | 15 ++++ esphome/components/mqtt/__init__.py | 11 +++ esphome/components/socket/__init__.py | 28 +++++++ esphome/components/web_server/__init__.py | 13 ++++ 8 files changed, 187 insertions(+), 9 deletions(-) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index e8dacf51bc..e91e922204 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -155,6 +155,17 @@ def _validate_api_config(config: ConfigType) -> ConfigType: return config +def _consume_api_sockets(config: ConfigType) -> ConfigType: + """Register socket needs for API component.""" + from esphome.components import socket + + # API needs 1 listening socket + typically 3 concurrent client connections + # (not max_connections, which is the upper limit rarely reached) + sockets_needed = 1 + 3 + socket.consume_sockets(sockets_needed, "api")(config) + return config + + CONFIG_SCHEMA = cv.All( cv.Schema( { @@ -222,6 +233,7 @@ CONFIG_SCHEMA = cv.All( ).extend(cv.COMPONENT_SCHEMA), cv.rename_key(CONF_SERVICES, CONF_ACTIONS), _validate_api_config, + _consume_api_sockets, ) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index af84692615..99a87e06f9 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -1,3 +1,4 @@ +import contextlib from dataclasses import dataclass import itertools import logging @@ -102,6 +103,10 @@ COMPILER_OPTIMIZATIONS = { "SIZE": "CONFIG_COMPILER_OPTIMIZATION_SIZE", } +# Socket limit configuration for ESP-IDF +# ESP-IDF CONFIG_LWIP_MAX_SOCKETS has range 1-253, default 10 +DEFAULT_MAX_SOCKETS = 10 # ESP-IDF default + ARDUINO_ALLOWED_VARIANTS = [ VARIANT_ESP32, VARIANT_ESP32C3, @@ -746,6 +751,72 @@ CONFIG_SCHEMA = cv.All( FINAL_VALIDATE_SCHEMA = cv.Schema(final_validate) +def _configure_lwip_max_sockets(conf: dict) -> None: + """Calculate and set CONFIG_LWIP_MAX_SOCKETS based on component needs. + + Socket component tracks consumer needs via consume_sockets() called during config validation. + This function runs in to_code() after all components have registered their socket needs. + User-provided sdkconfig_options take precedence. + """ + from esphome.components.socket import KEY_SOCKET_CONSUMERS + + # Check if user manually specified CONFIG_LWIP_MAX_SOCKETS + user_max_sockets = conf.get(CONF_SDKCONFIG_OPTIONS, {}).get( + "CONFIG_LWIP_MAX_SOCKETS" + ) + + socket_consumers: dict[str, int] = CORE.data.get(KEY_SOCKET_CONSUMERS, {}) + total_sockets = sum(socket_consumers.values()) + + # Early return if no sockets registered and no user override + if total_sockets == 0 and user_max_sockets is None: + return + + components_list = ", ".join( + f"{name}={count}" for name, count in sorted(socket_consumers.items()) + ) + + # User specified their own value - respect it but warn if insufficient + if user_max_sockets is not None: + _LOGGER.info( + "Using user-provided CONFIG_LWIP_MAX_SOCKETS: %s", + user_max_sockets, + ) + + # Warn if user's value is less than what components need + if total_sockets > 0: + user_sockets_int = 0 + with contextlib.suppress(ValueError, TypeError): + user_sockets_int = int(user_max_sockets) + + if user_sockets_int < total_sockets: + _LOGGER.warning( + "CONFIG_LWIP_MAX_SOCKETS is set to %d but your configuration " + "needs %d sockets (registered: %s). You may experience socket " + "exhaustion errors. Consider increasing to at least %d.", + user_sockets_int, + total_sockets, + components_list, + total_sockets, + ) + # User's value already added via sdkconfig_options processing + return + + # Auto-calculate based on component needs + # Use at least the ESP-IDF default (10), or the total needed by components + max_sockets = max(DEFAULT_MAX_SOCKETS, total_sockets) + + log_level = logging.INFO if max_sockets > DEFAULT_MAX_SOCKETS else logging.DEBUG + _LOGGER.log( + log_level, + "Setting CONFIG_LWIP_MAX_SOCKETS to %d (registered: %s)", + max_sockets, + components_list, + ) + + add_idf_sdkconfig_option("CONFIG_LWIP_MAX_SOCKETS", max_sockets) + + async def to_code(config): cg.add_platformio_option("board", config[CONF_BOARD]) cg.add_platformio_option("board_upload.flash_size", config[CONF_FLASH_SIZE]) @@ -866,6 +937,9 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES", False) if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False): add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0) + + _configure_lwip_max_sockets(conf) + if advanced.get(CONF_EXECUTE_FROM_PSRAM, False): add_idf_sdkconfig_option("CONFIG_SPIRAM_FETCH_INSTRUCTIONS", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_RODATA", True) diff --git a/esphome/components/esp32_camera_web_server/__init__.py b/esphome/components/esp32_camera_web_server/__init__.py index a6a7ac3630..ed1aaa2e07 100644 --- a/esphome/components/esp32_camera_web_server/__init__.py +++ b/esphome/components/esp32_camera_web_server/__init__.py @@ -1,6 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_MODE, CONF_PORT +from esphome.types import ConfigType CODEOWNERS = ["@ayufan"] AUTO_LOAD = ["camera"] @@ -13,13 +14,27 @@ Mode = esp32_camera_web_server_ns.enum("Mode") MODES = {"STREAM": Mode.STREAM, "SNAPSHOT": Mode.SNAPSHOT} -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CameraWebServer), - cv.Required(CONF_PORT): cv.port, - cv.Required(CONF_MODE): cv.enum(MODES, upper=True), - }, -).extend(cv.COMPONENT_SCHEMA) + +def _consume_camera_web_server_sockets(config: ConfigType) -> ConfigType: + """Register socket needs for camera web server.""" + from esphome.components import socket + + # Each camera web server instance needs 1 listening socket + 2 client connections + sockets_needed = 3 + socket.consume_sockets(sockets_needed, "esp32_camera_web_server")(config) + return config + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(CameraWebServer), + cv.Required(CONF_PORT): cv.port, + cv.Required(CONF_MODE): cv.enum(MODES, upper=True), + }, + ).extend(cv.COMPONENT_SCHEMA), + _consume_camera_web_server_sockets, +) async def to_code(config): diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index 69a50a2de9..e56e85b231 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -103,7 +103,16 @@ def ota_esphome_final_validate(config): ) -CONFIG_SCHEMA = ( +def _consume_ota_sockets(config: ConfigType) -> ConfigType: + """Register socket needs for OTA component.""" + from esphome.components import socket + + # OTA needs 1 listening socket (client connections are temporary during updates) + socket.consume_sockets(1, "ota")(config) + return config + + +CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(ESPHomeOTAComponent), @@ -130,7 +139,8 @@ CONFIG_SCHEMA = ( } ) .extend(BASE_OTA_SCHEMA) - .extend(cv.COMPONENT_SCHEMA) + .extend(cv.COMPONENT_SCHEMA), + _consume_ota_sockets, ) FINAL_VALIDATE_SCHEMA = ota_esphome_final_validate diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index c6a9ee1a0c..4776bef22f 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -13,6 +13,7 @@ from esphome.const import ( ) from esphome.core import CORE, Lambda, coroutine_with_priority from esphome.coroutine import CoroPriority +from esphome.types import ConfigType CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] @@ -46,6 +47,19 @@ SERVICE_SCHEMA = cv.Schema( } ) + +def _consume_mdns_sockets(config: ConfigType) -> ConfigType: + """Register socket needs for mDNS component.""" + if config.get(CONF_DISABLED): + return config + + from esphome.components import socket + + # mDNS needs 2 sockets (IPv4 + IPv6 multicast) + socket.consume_sockets(2, "mdns")(config) + return config + + CONFIG_SCHEMA = cv.All( cv.Schema( { @@ -55,6 +69,7 @@ CONFIG_SCHEMA = cv.All( } ), _remove_id_if_disabled, + _consume_mdns_sockets, ) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 814fb566d4..641c70a367 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -58,6 +58,7 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, CoroPriority, coroutine_with_priority +from esphome.types import ConfigType DEPENDENCIES = ["network"] @@ -210,6 +211,15 @@ def validate_fingerprint(value): return value +def _consume_mqtt_sockets(config: ConfigType) -> ConfigType: + """Register socket needs for MQTT component.""" + from esphome.components import socket + + # MQTT needs 1 socket for the broker connection + socket.consume_sockets(1, "mqtt")(config) + return config + + CONFIG_SCHEMA = cv.All( cv.Schema( { @@ -306,6 +316,7 @@ CONFIG_SCHEMA = cv.All( ), validate_config, cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX]), + _consume_mqtt_sockets, ) diff --git a/esphome/components/socket/__init__.py b/esphome/components/socket/__init__.py index e085a09eac..e6a4cfc07f 100644 --- a/esphome/components/socket/__init__.py +++ b/esphome/components/socket/__init__.py @@ -1,3 +1,5 @@ +from collections.abc import Callable, MutableMapping + import esphome.codegen as cg import esphome.config_validation as cv from esphome.core import CORE @@ -9,6 +11,32 @@ IMPLEMENTATION_LWIP_TCP = "lwip_tcp" IMPLEMENTATION_LWIP_SOCKETS = "lwip_sockets" IMPLEMENTATION_BSD_SOCKETS = "bsd_sockets" +# Socket tracking infrastructure +# Components register their socket needs and platforms read this to configure appropriately +KEY_SOCKET_CONSUMERS = "socket_consumers" + + +def consume_sockets( + value: int, consumer: str +) -> Callable[[MutableMapping], MutableMapping]: + """Register socket usage for a component. + + Args: + value: Number of sockets needed by the component + consumer: Name of the component consuming the sockets + + Returns: + A validator function that records the socket usage + """ + + def _consume_sockets(config: MutableMapping) -> MutableMapping: + consumers: dict[str, int] = CORE.data.setdefault(KEY_SOCKET_CONSUMERS, {}) + consumers[consumer] = consumers.get(consumer, 0) + value + return config + + return _consume_sockets + + CONFIG_SCHEMA = cv.Schema( { cv.SplitDefault( diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index 288d928e80..a7fdf30eef 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -136,6 +136,18 @@ def _final_validate_sorting(config: ConfigType) -> ConfigType: FINAL_VALIDATE_SCHEMA = _final_validate_sorting + +def _consume_web_server_sockets(config: ConfigType) -> ConfigType: + """Register socket needs for web_server component.""" + from esphome.components import socket + + # Web server needs 1 listening socket + typically 2 concurrent client connections + # (browser makes 2 connections for page + event stream) + sockets_needed = 3 + socket.consume_sockets(sockets_needed, "web_server")(config) + return config + + sorting_group = { cv.Required(CONF_ID): cv.declare_id(cg.int_), cv.Required(CONF_NAME): cv.string, @@ -205,6 +217,7 @@ CONFIG_SCHEMA = cv.All( validate_local, validate_sorting_groups, validate_ota, + _consume_web_server_sockets, ) From 1706a69fad9e101b9e1afd2c613109bc273b6a41 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 10:38:49 -1000 Subject: [PATCH 185/526] [sensor] Optimize filter memory usage with ValueListFilter base class (#11407) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/sensor/__init__.py | 7 ++- esphome/components/sensor/filter.cpp | 73 +++++++++++++-------------- esphome/components/sensor/filter.h | 28 +++++++--- 3 files changed, 59 insertions(+), 49 deletions(-) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index d9724a741d..e603896f6d 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -261,9 +261,12 @@ ThrottleAverageFilter = sensor_ns.class_("ThrottleAverageFilter", Filter, cg.Com LambdaFilter = sensor_ns.class_("LambdaFilter", Filter) OffsetFilter = sensor_ns.class_("OffsetFilter", Filter) MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter) -FilterOutValueFilter = sensor_ns.class_("FilterOutValueFilter", Filter) +ValueListFilter = sensor_ns.class_("ValueListFilter", Filter) +FilterOutValueFilter = sensor_ns.class_("FilterOutValueFilter", ValueListFilter) ThrottleFilter = sensor_ns.class_("ThrottleFilter", Filter) -ThrottleWithPriorityFilter = sensor_ns.class_("ThrottleWithPriorityFilter", Filter) +ThrottleWithPriorityFilter = sensor_ns.class_( + "ThrottleWithPriorityFilter", ValueListFilter +) TimeoutFilter = sensor_ns.class_("TimeoutFilter", Filter, cg.Component) DebounceFilter = sensor_ns.class_("DebounceFilter", Filter, cg.Component) HeartbeatFilter = sensor_ns.class_("HeartbeatFilter", Filter, cg.Component) diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index 1eb0b84964..0d57c792db 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -228,27 +228,40 @@ MultiplyFilter::MultiplyFilter(TemplatableValue multiplier) : multiplier_ optional MultiplyFilter::new_value(float value) { return value * this->multiplier_.value(); } -// FilterOutValueFilter -FilterOutValueFilter::FilterOutValueFilter(std::vector> values_to_filter_out) - : values_to_filter_out_(std::move(values_to_filter_out)) {} +// ValueListFilter (base class) +ValueListFilter::ValueListFilter(std::initializer_list> values) : values_(values) {} -optional FilterOutValueFilter::new_value(float value) { +bool ValueListFilter::value_matches_any_(float sensor_value) { int8_t accuracy = this->parent_->get_accuracy_decimals(); float accuracy_mult = powf(10.0f, accuracy); - for (auto filter_value : this->values_to_filter_out_) { - if (std::isnan(filter_value.value())) { - if (std::isnan(value)) { - return {}; - } + float rounded_sensor = roundf(accuracy_mult * sensor_value); + + for (auto &filter_value : this->values_) { + float fv = filter_value.value(); + + // Handle NaN comparison + if (std::isnan(fv)) { + if (std::isnan(sensor_value)) + return true; continue; } - float rounded_filter_out = roundf(accuracy_mult * filter_value.value()); - float rounded_value = roundf(accuracy_mult * value); - if (rounded_filter_out == rounded_value) { - return {}; - } + + // Compare rounded values + if (roundf(accuracy_mult * fv) == rounded_sensor) + return true; } - return value; + + return false; +} + +// FilterOutValueFilter +FilterOutValueFilter::FilterOutValueFilter(std::initializer_list> values_to_filter_out) + : ValueListFilter(values_to_filter_out) {} + +optional FilterOutValueFilter::new_value(float value) { + if (this->value_matches_any_(value)) + return {}; // Filter out + return value; // Pass through } // ThrottleFilter @@ -263,33 +276,15 @@ optional ThrottleFilter::new_value(float value) { } // ThrottleWithPriorityFilter -ThrottleWithPriorityFilter::ThrottleWithPriorityFilter(uint32_t min_time_between_inputs, - std::vector> prioritized_values) - : min_time_between_inputs_(min_time_between_inputs), prioritized_values_(std::move(prioritized_values)) {} +ThrottleWithPriorityFilter::ThrottleWithPriorityFilter( + uint32_t min_time_between_inputs, std::initializer_list> prioritized_values) + : ValueListFilter(prioritized_values), min_time_between_inputs_(min_time_between_inputs) {} optional ThrottleWithPriorityFilter::new_value(float value) { - bool is_prioritized_value = false; - int8_t accuracy = this->parent_->get_accuracy_decimals(); - float accuracy_mult = powf(10.0f, accuracy); const uint32_t now = App.get_loop_component_start_time(); - // First, determine if the new value is one of the prioritized values - for (auto prioritized_value : this->prioritized_values_) { - if (std::isnan(prioritized_value.value())) { - if (std::isnan(value)) { - is_prioritized_value = true; - break; - } - continue; - } - float rounded_prioritized_value = roundf(accuracy_mult * prioritized_value.value()); - float rounded_value = roundf(accuracy_mult * value); - if (rounded_prioritized_value == rounded_value) { - is_prioritized_value = true; - break; - } - } - // Finally, determine if the new value should be throttled and pass it through if not - if (this->last_input_ == 0 || now - this->last_input_ >= min_time_between_inputs_ || is_prioritized_value) { + // Allow value through if: no previous input, time expired, or is prioritized + if (this->last_input_ == 0 || now - this->last_input_ >= min_time_between_inputs_ || + this->value_matches_any_(value)) { this->last_input_ = now; return value; } diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 57bb06b517..e09c66afcb 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -317,15 +317,28 @@ class MultiplyFilter : public Filter { TemplatableValue multiplier_; }; +/** Base class for filters that compare sensor values against a list of configured values. + * + * This base class provides common functionality for filters that need to check if a sensor + * value matches any value in a configured list, with proper handling of NaN values and + * accuracy-based rounding for comparisons. + */ +class ValueListFilter : public Filter { + protected: + explicit ValueListFilter(std::initializer_list> values); + + /// Check if sensor value matches any configured value (with accuracy rounding) + bool value_matches_any_(float sensor_value); + + FixedVector> values_; +}; + /// A simple filter that only forwards the filter chain if it doesn't receive `value_to_filter_out`. -class FilterOutValueFilter : public Filter { +class FilterOutValueFilter : public ValueListFilter { public: - explicit FilterOutValueFilter(std::vector> values_to_filter_out); + explicit FilterOutValueFilter(std::initializer_list> values_to_filter_out); optional new_value(float value) override; - - protected: - std::vector> values_to_filter_out_; }; class ThrottleFilter : public Filter { @@ -340,17 +353,16 @@ class ThrottleFilter : public Filter { }; /// Same as 'throttle' but will immediately publish values contained in `value_to_prioritize`. -class ThrottleWithPriorityFilter : public Filter { +class ThrottleWithPriorityFilter : public ValueListFilter { public: explicit ThrottleWithPriorityFilter(uint32_t min_time_between_inputs, - std::vector> prioritized_values); + std::initializer_list> prioritized_values); optional new_value(float value) override; protected: uint32_t last_input_{0}; uint32_t min_time_between_inputs_; - std::vector> prioritized_values_; }; class TimeoutFilter : public Filter, public Component { From 0f4b54aa828ed5403e8f88ffd0db9861e42e97f9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 11:07:39 -1000 Subject: [PATCH 186/526] [esp32_improv, improv_base] Reduce flash usage by 352 bytes (#11406) --- .../esp32_improv/esp32_improv_component.cpp | 18 +++++++---- .../components/improv_base/improv_base.cpp | 32 ++++++++++--------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 526f7f4b42..329349b531 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -384,26 +384,32 @@ void ESP32ImprovComponent::check_wifi_connection_() { this->connecting_sta_ = {}; this->cancel_timeout("wifi-connect-timeout"); - std::vector urls; + // Build URL list with minimal allocations + // Maximum 3 URLs: custom next_url + ESPHOME_MY_LINK + webserver URL + std::string url_strings[3]; + size_t url_count = 0; // Add next_url if configured (should be first per Improv BLE spec) std::string next_url = this->get_formatted_next_url_(); if (!next_url.empty()) { - urls.push_back(next_url); + url_strings[url_count++] = std::move(next_url); } // Add default URLs for backward compatibility - urls.emplace_back(ESPHOME_MY_LINK); + url_strings[url_count++] = ESPHOME_MY_LINK; #ifdef USE_WEBSERVER for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) { if (ip.is_ip4()) { - std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT); - urls.push_back(webserver_url); + char url_buffer[64]; + snprintf(url_buffer, sizeof(url_buffer), "http://%s:%d", ip.str().c_str(), USE_WEBSERVER_PORT); + url_strings[url_count++] = url_buffer; break; } } #endif - std::vector data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls); + // Pass to build_rpc_response using vector constructor from iterators to avoid extra copies + std::vector data = improv::build_rpc_response( + improv::WIFI_SETTINGS, std::vector(url_strings, url_strings + url_count)); this->send_response_(data); } else if (this->is_active() && this->state_ != improv::STATE_PROVISIONED) { ESP_LOGD(TAG, "WiFi provisioned externally"); diff --git a/esphome/components/improv_base/improv_base.cpp b/esphome/components/improv_base/improv_base.cpp index 89ee5492b5..233098e6cd 100644 --- a/esphome/components/improv_base/improv_base.cpp +++ b/esphome/components/improv_base/improv_base.cpp @@ -6,6 +6,21 @@ namespace esphome { namespace improv_base { +static constexpr const char DEVICE_NAME_PLACEHOLDER[] = "{{device_name}}"; +static constexpr size_t DEVICE_NAME_PLACEHOLDER_LEN = sizeof(DEVICE_NAME_PLACEHOLDER) - 1; +static constexpr const char IP_ADDRESS_PLACEHOLDER[] = "{{ip_address}}"; +static constexpr size_t IP_ADDRESS_PLACEHOLDER_LEN = sizeof(IP_ADDRESS_PLACEHOLDER) - 1; + +static void replace_all_in_place(std::string &str, const char *placeholder, size_t placeholder_len, + const std::string &replacement) { + size_t pos = 0; + const size_t replacement_len = replacement.length(); + while ((pos = str.find(placeholder, pos)) != std::string::npos) { + str.replace(pos, placeholder_len, replacement); + pos += replacement_len; + } +} + std::string ImprovBase::get_formatted_next_url_() { if (this->next_url_.empty()) { return ""; @@ -14,28 +29,15 @@ std::string ImprovBase::get_formatted_next_url_() { std::string formatted_url = this->next_url_; // Replace all occurrences of {{device_name}} - const std::string device_name_placeholder = "{{device_name}}"; - const std::string &device_name = App.get_name(); - size_t pos = 0; - while ((pos = formatted_url.find(device_name_placeholder, pos)) != std::string::npos) { - formatted_url.replace(pos, device_name_placeholder.length(), device_name); - pos += device_name.length(); - } + replace_all_in_place(formatted_url, DEVICE_NAME_PLACEHOLDER, DEVICE_NAME_PLACEHOLDER_LEN, App.get_name()); // Replace all occurrences of {{ip_address}} - const std::string ip_address_placeholder = "{{ip_address}}"; - std::string ip_address_str; for (auto &ip : network::get_ip_addresses()) { if (ip.is_ip4()) { - ip_address_str = ip.str(); + replace_all_in_place(formatted_url, IP_ADDRESS_PLACEHOLDER, IP_ADDRESS_PLACEHOLDER_LEN, ip.str()); break; } } - pos = 0; - while ((pos = formatted_url.find(ip_address_placeholder, pos)) != std::string::npos) { - formatted_url.replace(pos, ip_address_placeholder.length(), ip_address_str); - pos += ip_address_str.length(); - } // Note: {{esphome_version}} is replaced at code generation time in Python From 97d91fee854a24f2a679ee4cce7f6e2da1f910e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:10:33 -1000 Subject: [PATCH 187/526] Bump pylint from 4.0.1 to 4.0.2 (#11418) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 4c60a31d7f..5f94329e3f 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -pylint==4.0.1 +pylint==4.0.2 flake8==7.3.0 # also change in .pre-commit-config.yaml when updating ruff==0.14.1 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating From 426511e78d89fe5db5a19888f61a4739a8373693 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:11:15 -1000 Subject: [PATCH 188/526] Bump actions/download-artifact from 4.3.0 to 5.0.0 (#11419) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f96f2ac14..0bfbfde527 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -764,13 +764,13 @@ jobs: python-version: ${{ env.DEFAULT_PYTHON }} cache-key: ${{ needs.common.outputs.cache-key }} - name: Download target analysis JSON - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: name: memory-analysis-target path: ./memory-analysis continue-on-error: true - name: Download PR analysis JSON - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: name: memory-analysis-pr path: ./memory-analysis From 6fbd0e3385a7ee350950397dd863c7b01061f3ef Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:12:07 -0400 Subject: [PATCH 189/526] [esp32_hosted] Bump esp hosted (#11414) --- esphome/components/esp32_hosted/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32_hosted/__init__.py b/esphome/components/esp32_hosted/__init__.py index 7e9f1b05b5..fde75517eb 100644 --- a/esphome/components/esp32_hosted/__init__.py +++ b/esphome/components/esp32_hosted/__init__.py @@ -95,7 +95,7 @@ async def to_code(config): if framework_ver >= cv.Version(5, 5, 0): esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="1.1.5") esp32.add_idf_component(name="espressif/eppp_link", ref="1.1.3") - esp32.add_idf_component(name="espressif/esp_hosted", ref="2.5.11") + esp32.add_idf_component(name="espressif/esp_hosted", ref="2.6.1") else: esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="0.13.0") esp32.add_idf_component(name="espressif/eppp_link", ref="0.2.0") From ffb0e854b6a2d6f7be371104197bc3e453b4c7c6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 11:24:46 -1000 Subject: [PATCH 190/526] [ci] Optimize clang-tidy for small PRs by avoiding unnecessary job spitting (#11402) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/ci.yml | 186 +++++++++++++++++++++++++--- script/determine-jobs.py | 32 +++++ tests/script/test_determine_jobs.py | 48 +++++++ 3 files changed, 248 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bfbfde527..f085aedcc0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -170,11 +170,13 @@ jobs: outputs: integration-tests: ${{ steps.determine.outputs.integration-tests }} clang-tidy: ${{ steps.determine.outputs.clang-tidy }} + clang-tidy-mode: ${{ steps.determine.outputs.clang-tidy-mode }} python-linters: ${{ steps.determine.outputs.python-linters }} changed-components: ${{ steps.determine.outputs.changed-components }} changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }} directly-changed-components-with-tests: ${{ steps.determine.outputs.directly-changed-components-with-tests }} component-test-count: ${{ steps.determine.outputs.component-test-count }} + changed-cpp-file-count: ${{ steps.determine.outputs.changed-cpp-file-count }} memory_impact: ${{ steps.determine.outputs.memory-impact }} steps: - name: Check out code from GitHub @@ -200,11 +202,13 @@ jobs: # Extract individual fields echo "integration-tests=$(echo "$output" | jq -r '.integration_tests')" >> $GITHUB_OUTPUT echo "clang-tidy=$(echo "$output" | jq -r '.clang_tidy')" >> $GITHUB_OUTPUT + echo "clang-tidy-mode=$(echo "$output" | jq -r '.clang_tidy_mode')" >> $GITHUB_OUTPUT echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT echo "changed-components=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT echo "changed-components-with-tests=$(echo "$output" | jq -c '.changed_components_with_tests')" >> $GITHUB_OUTPUT echo "directly-changed-components-with-tests=$(echo "$output" | jq -c '.directly_changed_components_with_tests')" >> $GITHUB_OUTPUT echo "component-test-count=$(echo "$output" | jq -r '.component_test_count')" >> $GITHUB_OUTPUT + echo "changed-cpp-file-count=$(echo "$output" | jq -r '.changed_cpp_file_count')" >> $GITHUB_OUTPUT echo "memory-impact=$(echo "$output" | jq -c '.memory_impact')" >> $GITHUB_OUTPUT integration-tests: @@ -243,7 +247,7 @@ jobs: . venv/bin/activate pytest -vv --no-cov --tb=native -n auto tests/integration/ - clang-tidy: + clang-tidy-single: name: ${{ matrix.name }} runs-on: ubuntu-24.04 needs: @@ -261,22 +265,6 @@ jobs: name: Run script/clang-tidy for ESP8266 options: --environment esp8266-arduino-tidy --grep USE_ESP8266 pio_cache_key: tidyesp8266 - - id: clang-tidy - name: Run script/clang-tidy for ESP32 Arduino 1/4 - options: --environment esp32-arduino-tidy --split-num 4 --split-at 1 - pio_cache_key: tidyesp32 - - id: clang-tidy - name: Run script/clang-tidy for ESP32 Arduino 2/4 - options: --environment esp32-arduino-tidy --split-num 4 --split-at 2 - pio_cache_key: tidyesp32 - - id: clang-tidy - name: Run script/clang-tidy for ESP32 Arduino 3/4 - options: --environment esp32-arduino-tidy --split-num 4 --split-at 3 - pio_cache_key: tidyesp32 - - id: clang-tidy - name: Run script/clang-tidy for ESP32 Arduino 4/4 - options: --environment esp32-arduino-tidy --split-num 4 --split-at 4 - pio_cache_key: tidyesp32 - id: clang-tidy name: Run script/clang-tidy for ESP32 IDF options: --environment esp32-idf-tidy --grep USE_ESP_IDF @@ -357,6 +345,166 @@ jobs: # yamllint disable-line rule:line-length if: always() + clang-tidy-nosplit: + name: Run script/clang-tidy for ESP32 Arduino + runs-on: ubuntu-24.04 + needs: + - common + - determine-jobs + if: needs.determine-jobs.outputs.clang-tidy-mode == 'nosplit' + env: + GH_TOKEN: ${{ github.token }} + steps: + - name: Check out code from GitHub + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + # Need history for HEAD~1 to work for checking changed files + fetch-depth: 2 + + - name: Restore Python + uses: ./.github/actions/restore-python + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache-key: ${{ needs.common.outputs.cache-key }} + + - name: Cache platformio + if: github.ref == 'refs/heads/dev' + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.platformio + key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }} + + - name: Cache platformio + if: github.ref != 'refs/heads/dev' + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.platformio + key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }} + + - name: Register problem matchers + run: | + echo "::add-matcher::.github/workflows/matchers/gcc.json" + echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" + + - name: Check if full clang-tidy scan needed + id: check_full_scan + run: | + . venv/bin/activate + if python script/clang_tidy_hash.py --check; then + echo "full_scan=true" >> $GITHUB_OUTPUT + echo "reason=hash_changed" >> $GITHUB_OUTPUT + else + echo "full_scan=false" >> $GITHUB_OUTPUT + echo "reason=normal" >> $GITHUB_OUTPUT + fi + + - name: Run clang-tidy + run: | + . venv/bin/activate + if [ "${{ steps.check_full_scan.outputs.full_scan }}" = "true" ]; then + echo "Running FULL clang-tidy scan (hash changed)" + script/clang-tidy --all-headers --fix --environment esp32-arduino-tidy + else + echo "Running clang-tidy on changed files only" + script/clang-tidy --all-headers --fix --changed --environment esp32-arduino-tidy + fi + env: + # Also cache libdeps, store them in a ~/.platformio subfolder + PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps + + - name: Suggested changes + run: script/ci-suggest-changes + if: always() + + clang-tidy-split: + name: ${{ matrix.name }} + runs-on: ubuntu-24.04 + needs: + - common + - determine-jobs + if: needs.determine-jobs.outputs.clang-tidy-mode == 'split' + env: + GH_TOKEN: ${{ github.token }} + strategy: + fail-fast: false + max-parallel: 1 + matrix: + include: + - id: clang-tidy + name: Run script/clang-tidy for ESP32 Arduino 1/4 + options: --environment esp32-arduino-tidy --split-num 4 --split-at 1 + - id: clang-tidy + name: Run script/clang-tidy for ESP32 Arduino 2/4 + options: --environment esp32-arduino-tidy --split-num 4 --split-at 2 + - id: clang-tidy + name: Run script/clang-tidy for ESP32 Arduino 3/4 + options: --environment esp32-arduino-tidy --split-num 4 --split-at 3 + - id: clang-tidy + name: Run script/clang-tidy for ESP32 Arduino 4/4 + options: --environment esp32-arduino-tidy --split-num 4 --split-at 4 + + steps: + - name: Check out code from GitHub + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + # Need history for HEAD~1 to work for checking changed files + fetch-depth: 2 + + - name: Restore Python + uses: ./.github/actions/restore-python + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache-key: ${{ needs.common.outputs.cache-key }} + + - name: Cache platformio + if: github.ref == 'refs/heads/dev' + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.platformio + key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }} + + - name: Cache platformio + if: github.ref != 'refs/heads/dev' + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.platformio + key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }} + + - name: Register problem matchers + run: | + echo "::add-matcher::.github/workflows/matchers/gcc.json" + echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" + + - name: Check if full clang-tidy scan needed + id: check_full_scan + run: | + . venv/bin/activate + if python script/clang_tidy_hash.py --check; then + echo "full_scan=true" >> $GITHUB_OUTPUT + echo "reason=hash_changed" >> $GITHUB_OUTPUT + else + echo "full_scan=false" >> $GITHUB_OUTPUT + echo "reason=normal" >> $GITHUB_OUTPUT + fi + + - name: Run clang-tidy + run: | + . venv/bin/activate + if [ "${{ steps.check_full_scan.outputs.full_scan }}" = "true" ]; then + echo "Running FULL clang-tidy scan (hash changed)" + script/clang-tidy --all-headers --fix ${{ matrix.options }} + else + echo "Running clang-tidy on changed files only" + script/clang-tidy --all-headers --fix --changed ${{ matrix.options }} + fi + env: + # Also cache libdeps, store them in a ~/.platformio subfolder + PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps + + - name: Suggested changes + run: script/ci-suggest-changes + if: always() + test-build-components-splitter: name: Split components for intelligent grouping (40 weighted per batch) runs-on: ubuntu-24.04 @@ -797,7 +945,9 @@ jobs: - pylint - pytest - integration-tests - - clang-tidy + - clang-tidy-single + - clang-tidy-nosplit + - clang-tidy-split - determine-jobs - test-build-components-splitter - test-build-components-split diff --git a/script/determine-jobs.py b/script/determine-jobs.py index a0e04a256e..c9aebd2cb7 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -61,6 +61,11 @@ from helpers import ( root_path, ) +# Threshold for splitting clang-tidy jobs +# For small PRs (< 65 files), use nosplit for faster CI +# For large PRs (>= 65 files), use split for better parallelization +CLANG_TIDY_SPLIT_THRESHOLD = 65 + class Platform(StrEnum): """Platform identifiers for memory impact analysis.""" @@ -210,6 +215,22 @@ def should_run_clang_tidy(branch: str | None = None) -> bool: return _any_changed_file_endswith(branch, CPP_FILE_EXTENSIONS) +def count_changed_cpp_files(branch: str | None = None) -> int: + """Count the number of changed C++ files. + + This is used to determine whether to split clang-tidy jobs or run them as a single job. + For PRs with < 65 changed C++ files, running a single job is faster than splitting. + + Args: + branch: Branch to compare against. If None, uses default. + + Returns: + Number of changed C++ files. + """ + files = changed_files(branch) + return sum(1 for file in files if file.endswith(CPP_FILE_EXTENSIONS)) + + def should_run_clang_format(branch: str | None = None) -> bool: """Determine if clang-format should run based on changed files. @@ -412,6 +433,7 @@ def main() -> None: run_clang_tidy = should_run_clang_tidy(args.branch) run_clang_format = should_run_clang_format(args.branch) run_python_linters = should_run_python_linters(args.branch) + changed_cpp_file_count = count_changed_cpp_files(args.branch) # Get both directly changed and all changed components (with dependencies) in one call script_path = Path(__file__).parent / "list-components.py" @@ -449,10 +471,19 @@ def main() -> None: # Detect components for memory impact analysis (merged config) memory_impact = detect_memory_impact_config(args.branch) + if run_clang_tidy: + if changed_cpp_file_count < CLANG_TIDY_SPLIT_THRESHOLD: + clang_tidy_mode = "nosplit" + else: + clang_tidy_mode = "split" + else: + clang_tidy_mode = "disabled" + # Build output output: dict[str, Any] = { "integration_tests": run_integration, "clang_tidy": run_clang_tidy, + "clang_tidy_mode": clang_tidy_mode, "clang_format": run_clang_format, "python_linters": run_python_linters, "changed_components": changed_components, @@ -462,6 +493,7 @@ def main() -> None: "component_test_count": len(changed_components_with_tests), "directly_changed_count": len(directly_changed_with_tests), "dependency_only_count": len(dependency_only_components), + "changed_cpp_file_count": changed_cpp_file_count, "memory_impact": memory_impact, } diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 7587dbee69..02aaad2e3a 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -107,6 +107,7 @@ def test_main_all_tests_should_run( assert output["integration_tests"] is True assert output["clang_tidy"] is True + assert output["clang_tidy_mode"] in ["nosplit", "split"] assert output["clang_format"] is True assert output["python_linters"] is True assert output["changed_components"] == ["wifi", "api", "sensor"] @@ -117,6 +118,9 @@ def test_main_all_tests_should_run( assert output["component_test_count"] == len( output["changed_components_with_tests"] ) + # changed_cpp_file_count should be present + assert "changed_cpp_file_count" in output + assert isinstance(output["changed_cpp_file_count"], int) # memory_impact should be present assert "memory_impact" in output assert output["memory_impact"]["should_run"] == "false" # No files changed @@ -156,11 +160,14 @@ def test_main_no_tests_should_run( assert output["integration_tests"] is False assert output["clang_tidy"] is False + assert output["clang_tidy_mode"] == "disabled" assert output["clang_format"] is False assert output["python_linters"] is False assert output["changed_components"] == [] assert output["changed_components_with_tests"] == [] assert output["component_test_count"] == 0 + # changed_cpp_file_count should be 0 + assert output["changed_cpp_file_count"] == 0 # memory_impact should be present assert "memory_impact" in output assert output["memory_impact"]["should_run"] == "false" @@ -239,6 +246,7 @@ def test_main_with_branch_argument( assert output["integration_tests"] is False assert output["clang_tidy"] is True + assert output["clang_tidy_mode"] in ["nosplit", "split"] assert output["clang_format"] is False assert output["python_linters"] is True assert output["changed_components"] == ["mqtt"] @@ -249,6 +257,9 @@ def test_main_with_branch_argument( assert output["component_test_count"] == len( output["changed_components_with_tests"] ) + # changed_cpp_file_count should be present + assert "changed_cpp_file_count" in output + assert isinstance(output["changed_cpp_file_count"], int) # memory_impact should be present assert "memory_impact" in output assert output["memory_impact"]["should_run"] == "false" @@ -433,6 +444,40 @@ def test_should_run_clang_format_with_branch() -> None: mock_changed.assert_called_once_with("release") +@pytest.mark.parametrize( + ("changed_files", "expected_count"), + [ + (["esphome/core.cpp"], 1), + (["esphome/core.h"], 1), + (["test.hpp"], 1), + (["test.cc"], 1), + (["test.cxx"], 1), + (["test.c"], 1), + (["test.tcc"], 1), + (["esphome/core.cpp", "esphome/core.h"], 2), + (["esphome/core.cpp", "esphome/core.h", "test.cc"], 3), + (["README.md"], 0), + (["esphome/config.py"], 0), + (["README.md", "esphome/config.py"], 0), + (["esphome/core.cpp", "README.md", "esphome/config.py"], 1), + ([], 0), + ], +) +def test_count_changed_cpp_files(changed_files: list[str], expected_count: int) -> None: + """Test count_changed_cpp_files function.""" + with patch.object(determine_jobs, "changed_files", return_value=changed_files): + result = determine_jobs.count_changed_cpp_files() + assert result == expected_count + + +def test_count_changed_cpp_files_with_branch() -> None: + """Test count_changed_cpp_files with branch argument.""" + with patch.object(determine_jobs, "changed_files") as mock_changed: + mock_changed.return_value = [] + determine_jobs.count_changed_cpp_files("release") + mock_changed.assert_called_once_with("release") + + def test_main_filters_components_without_tests( mock_should_run_integration_tests: Mock, mock_should_run_clang_tidy: Mock, @@ -501,6 +546,9 @@ def test_main_filters_components_without_tests( assert set(output["changed_components_with_tests"]) == {"wifi", "sensor"} # component_test_count should be based on components with tests assert output["component_test_count"] == 2 + # changed_cpp_file_count should be present + assert "changed_cpp_file_count" in output + assert isinstance(output["changed_cpp_file_count"], int) # memory_impact should be present assert "memory_impact" in output assert output["memory_impact"]["should_run"] == "false" From 6a239f4d1c27c2dd157ea1e23c38af350cf02d66 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 11:25:33 -1000 Subject: [PATCH 191/526] [ci] Prefer platform-specific tests for memory impact analysis (#11398) --- script/determine-jobs.py | 145 +++++++++++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 15 deletions(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index c9aebd2cb7..1877894fc4 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -83,11 +83,16 @@ MEMORY_IMPACT_FALLBACK_COMPONENT = "api" # Representative component for core ch MEMORY_IMPACT_FALLBACK_PLATFORM = Platform.ESP32_IDF # Most representative platform # Platform preference order for memory impact analysis -# Prefer newer platforms first as they represent the future of ESPHome -# ESP8266 is most constrained but many new features don't support it +# This order is used when no platform-specific hints are detected from filenames +# Priority rationale: +# 1. ESP32-C6 IDF - Newest platform, supports Thread/Zigbee +# 2. ESP8266 Arduino - Most memory constrained (best for detecting memory impact), +# fastest build times, most sensitive to code size changes +# 3. ESP32 IDF - Primary ESP32 platform, most representative of modern ESPHome +# 4-6. Other ESP32 variants - Less commonly used but still supported MEMORY_IMPACT_PLATFORM_PREFERENCE = [ Platform.ESP32_C6_IDF, # ESP32-C6 IDF (newest, supports Thread/Zigbee) - Platform.ESP8266_ARD, # ESP8266 Arduino (most memory constrained - best for impact analysis) + Platform.ESP8266_ARD, # ESP8266 Arduino (most memory constrained, fastest builds) Platform.ESP32_IDF, # ESP32 IDF platform (primary ESP32 platform, most representative) Platform.ESP32_C3_IDF, # ESP32-C3 IDF Platform.ESP32_S2_IDF, # ESP32-S2 IDF @@ -285,6 +290,91 @@ def _component_has_tests(component: str) -> bool: return bool(get_component_test_files(component)) +def _select_platform_by_preference( + platforms: list[Platform] | set[Platform], +) -> Platform: + """Select the most preferred platform from a list/set based on MEMORY_IMPACT_PLATFORM_PREFERENCE. + + Args: + platforms: List or set of platforms to choose from + + Returns: + The most preferred platform (earliest in MEMORY_IMPACT_PLATFORM_PREFERENCE) + """ + return min(platforms, key=MEMORY_IMPACT_PLATFORM_PREFERENCE.index) + + +def _select_platform_by_count( + platform_counts: Counter[Platform], +) -> Platform: + """Select platform by count, using MEMORY_IMPACT_PLATFORM_PREFERENCE as tiebreaker. + + Args: + platform_counts: Counter mapping platforms to their counts + + Returns: + Platform with highest count, breaking ties by preference order + """ + return min( + platform_counts.keys(), + key=lambda p: ( + -platform_counts[p], # Negative to prefer higher counts + MEMORY_IMPACT_PLATFORM_PREFERENCE.index(p), + ), + ) + + +def _detect_platform_hint_from_filename(filename: str) -> Platform | None: + """Detect platform hint from filename patterns. + + Detects platform-specific files using patterns like: + - wifi_component_esp_idf.cpp, *_idf.h -> ESP32 IDF variants + - wifi_component_esp8266.cpp, *_esp8266.h -> ESP8266_ARD + - *_esp32*.cpp -> ESP32 IDF (generic) + - *_libretiny.cpp, *_retiny.* -> LibreTiny (not in preference list) + - *_pico.cpp, *_rp2040.* -> RP2040 (not in preference list) + + Args: + filename: File path to check + + Returns: + Platform enum if a specific platform is detected, None otherwise + """ + filename_lower = filename.lower() + + # ESP-IDF platforms (check specific variants first) + if "esp_idf" in filename_lower or "_idf" in filename_lower: + # Check for specific ESP32 variants + if "c6" in filename_lower or "esp32c6" in filename_lower: + return Platform.ESP32_C6_IDF + if "c3" in filename_lower or "esp32c3" in filename_lower: + return Platform.ESP32_C3_IDF + if "s2" in filename_lower or "esp32s2" in filename_lower: + return Platform.ESP32_S2_IDF + if "s3" in filename_lower or "esp32s3" in filename_lower: + return Platform.ESP32_S3_IDF + # Default to ESP32 IDF for generic esp_idf files + return Platform.ESP32_IDF + + # ESP8266 Arduino + if "esp8266" in filename_lower: + return Platform.ESP8266_ARD + + # Generic ESP32 (without _idf suffix, could be Arduino or shared code) + # Prefer IDF as it's the modern platform + if "esp32" in filename_lower: + return Platform.ESP32_IDF + + # LibreTiny and RP2040 are not in MEMORY_IMPACT_PLATFORM_PREFERENCE + # so we don't return them as hints + # if "retiny" in filename_lower or "libretiny" in filename_lower: + # return None # No specific LibreTiny platform preference + # if "pico" in filename_lower or "rp2040" in filename_lower: + # return None # No RP2040 platform preference + + return None + + def detect_memory_impact_config( branch: str | None = None, ) -> dict[str, Any]: @@ -294,6 +384,9 @@ def detect_memory_impact_config( building a merged configuration with all changed components (like test_build_components.py does) to get comprehensive memory analysis. + When platform-specific files are detected (e.g., wifi_component_esp_idf.cpp), + prefers that platform for testing to ensure the most relevant memory analysis. + For core C++ file changes without component changes, runs a fallback analysis using a representative component to measure the impact. @@ -312,8 +405,10 @@ def detect_memory_impact_config( files = changed_files(branch) # Find all changed components (excluding core and base bus components) + # Also collect platform hints from platform-specific filenames changed_component_set: set[str] = set() has_core_cpp_changes = False + platform_hints: list[Platform] = [] for file in files: component = get_component_from_path(file) @@ -321,6 +416,10 @@ def detect_memory_impact_config( # Skip base bus components as they're used across many builds if component not in BASE_BUS_COMPONENTS: changed_component_set.add(component) + # Check if this is a platform-specific file + platform_hint = _detect_platform_hint_from_filename(file) + if platform_hint: + platform_hints.append(platform_hint) elif file.startswith("esphome/") and file.endswith(CPP_FILE_EXTENSIONS): # Core ESPHome C++ files changed (not component-specific) # Only C++ files affect memory usage @@ -377,27 +476,42 @@ def detect_memory_impact_config( common_platforms &= platforms # Select the most preferred platform from the common set - # Exception: for core changes, use fallback platform (most representative of codebase) - if force_fallback_platform: + # Priority order: + # 1. Platform hints from filenames (e.g., wifi_component_esp_idf.cpp suggests ESP32_IDF) + # 2. Core changes use fallback platform (most representative of codebase) + # 3. Common platforms supported by all components + # 4. Most commonly supported platform + if platform_hints: + # Use most common platform hint that's also supported by all components + hint_counts = Counter(platform_hints) + # Filter to only hints that are in common_platforms (if any common platforms exist) + valid_hints = ( + [h for h in hint_counts if h in common_platforms] + if common_platforms + else list(hint_counts.keys()) + ) + if valid_hints: + platform = _select_platform_by_count( + Counter({p: hint_counts[p] for p in valid_hints}) + ) + elif common_platforms: + # Hints exist but none match common platforms, use common platform logic + platform = _select_platform_by_preference(common_platforms) + else: + # Use the most common hint even if it's not in common platforms + platform = _select_platform_by_count(hint_counts) + elif force_fallback_platform: platform = MEMORY_IMPACT_FALLBACK_PLATFORM elif common_platforms: # Pick the most preferred platform that all components support - platform = min(common_platforms, key=MEMORY_IMPACT_PLATFORM_PREFERENCE.index) + platform = _select_platform_by_preference(common_platforms) else: # No common platform - pick the most commonly supported platform - # This allows testing components individually even if they can't be merged # Count how many components support each platform platform_counts = Counter( p for platforms in component_platforms_map.values() for p in platforms ) - # Pick the platform supported by most components, preferring earlier in MEMORY_IMPACT_PLATFORM_PREFERENCE - platform = max( - platform_counts.keys(), - key=lambda p: ( - platform_counts[p], - -MEMORY_IMPACT_PLATFORM_PREFERENCE.index(p), - ), - ) + platform = _select_platform_by_count(platform_counts) # Debug output print("Memory impact analysis:", file=sys.stderr) @@ -407,6 +521,7 @@ def detect_memory_impact_config( f" Component platforms: {dict(sorted(component_platforms_map.items()))}", file=sys.stderr, ) + print(f" Platform hints from filenames: {platform_hints}", file=sys.stderr) print(f" Common platforms: {sorted(common_platforms)}", file=sys.stderr) print(f" Selected platform: {platform}", file=sys.stderr) From 9f668b0c4b6d81cc2213d21534d39abc0973e22f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 16:26:41 -1000 Subject: [PATCH 192/526] Add basic text_sensor tests (#11424) --- tests/components/text_sensor/common.yaml | 66 +++++++++++++++++++ .../text_sensor/test.esp8266-ard.yaml | 1 + 2 files changed, 67 insertions(+) create mode 100644 tests/components/text_sensor/common.yaml create mode 100644 tests/components/text_sensor/test.esp8266-ard.yaml diff --git a/tests/components/text_sensor/common.yaml b/tests/components/text_sensor/common.yaml new file mode 100644 index 0000000000..4459c0fa44 --- /dev/null +++ b/tests/components/text_sensor/common.yaml @@ -0,0 +1,66 @@ +text_sensor: + - platform: template + name: "Test Substitute Single" + id: test_substitute_single + filters: + - substitute: + - ERROR -> Error + + - platform: template + name: "Test Substitute Multiple" + id: test_substitute_multiple + filters: + - substitute: + - ERROR -> Error + - WARN -> Warning + - INFO -> Information + - DEBUG -> Debug + + - platform: template + name: "Test Substitute Chained" + id: test_substitute_chained + filters: + - substitute: + - foo -> bar + - to_upper + - substitute: + - BAR -> baz + + - platform: template + name: "Test Map Single" + id: test_map_single + filters: + - map: + - ON -> Active + + - platform: template + name: "Test Map Multiple" + id: test_map_multiple + filters: + - map: + - ON -> Active + - OFF -> Inactive + - UNKNOWN -> Error + - IDLE -> Standby + + - platform: template + name: "Test Map Passthrough" + id: test_map_passthrough + filters: + - map: + - Good -> Excellent + - Bad -> Poor + + - platform: template + name: "Test All Filters" + id: test_all_filters + filters: + - to_upper + - to_lower + - append: " suffix" + - prepend: "prefix " + - substitute: + - prefix -> PREFIX + - suffix -> SUFFIX + - map: + - PREFIX text SUFFIX -> mapped diff --git a/tests/components/text_sensor/test.esp8266-ard.yaml b/tests/components/text_sensor/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/text_sensor/test.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 05216db5f0cb397ba3bce637c8ef0d31c1ae0b77 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 16:26:49 -1000 Subject: [PATCH 193/526] ESP8266: Complete testing mode memory patches with DRAM and Flash (#11427) --- esphome/components/esp8266/__init__.py | 16 +- esphome/components/esp8266/iram_fix.py.script | 44 ----- .../components/esp8266/testing_mode.py.script | 166 ++++++++++++++++++ .../build_components_base.esp8266-ard.yaml | 2 +- 4 files changed, 176 insertions(+), 52 deletions(-) delete mode 100644 esphome/components/esp8266/iram_fix.py.script create mode 100644 esphome/components/esp8266/testing_mode.py.script diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 9d8e6b7d1e..a74f9ee8ce 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -190,7 +190,9 @@ async def to_code(config): cg.add_define("ESPHOME_VARIANT", "ESP8266") cg.add_define(ThreadModel.SINGLE) - cg.add_platformio_option("extra_scripts", ["pre:iram_fix.py", "post:post_build.py"]) + cg.add_platformio_option( + "extra_scripts", ["pre:testing_mode.py", "post:post_build.py"] + ) conf = config[CONF_FRAMEWORK] cg.add_platformio_option("framework", "arduino") @@ -230,9 +232,9 @@ async def to_code(config): # For cases where nullptrs can be handled, use nothrow: `new (std::nothrow) T;` cg.add_build_flag("-DNEW_OOM_ABORT") - # In testing mode, fake a larger IRAM to allow linking grouped component tests - # Real ESP8266 hardware only has 32KB IRAM, but for CI testing we pretend it has 2MB - # This is done via a pre-build script that generates a custom linker script + # In testing mode, fake larger memory to allow linking grouped component tests + # Real ESP8266 hardware only has 32KB IRAM and ~80KB RAM, but for CI testing + # we pretend it has much larger memory to test that components compile together if CORE.testing_mode: cg.add_build_flag("-DESPHOME_TESTING_MODE") @@ -271,8 +273,8 @@ def copy_files(): post_build_file, CORE.relative_build_path("post_build.py"), ) - iram_fix_file = dir / "iram_fix.py.script" + testing_mode_file = dir / "testing_mode.py.script" copy_file_if_changed( - iram_fix_file, - CORE.relative_build_path("iram_fix.py"), + testing_mode_file, + CORE.relative_build_path("testing_mode.py"), ) diff --git a/esphome/components/esp8266/iram_fix.py.script b/esphome/components/esp8266/iram_fix.py.script deleted file mode 100644 index 96bddc2ced..0000000000 --- a/esphome/components/esp8266/iram_fix.py.script +++ /dev/null @@ -1,44 +0,0 @@ -import os -import re - -# pylint: disable=E0602 -Import("env") # noqa - - -def patch_linker_script_after_preprocess(source, target, env): - """Patch the local linker script after PlatformIO preprocesses it.""" - # Check if we're in testing mode by looking for the define - build_flags = env.get("BUILD_FLAGS", []) - testing_mode = any("-DESPHOME_TESTING_MODE" in flag for flag in build_flags) - - if not testing_mode: - return - - # Get the local linker script path - build_dir = env.subst("$BUILD_DIR") - local_ld = os.path.join(build_dir, "ld", "local.eagle.app.v6.common.ld") - - if not os.path.exists(local_ld): - return - - # Read the linker script - with open(local_ld, "r") as f: - content = f.read() - - # Replace IRAM size from 0x8000 (32KB) to 0x200000 (2MB) - # The line looks like: iram1_0_seg : org = 0x40100000, len = 0x8000 - updated = re.sub( - r"(iram1_0_seg\s*:\s*org\s*=\s*0x40100000\s*,\s*len\s*=\s*)0x8000", - r"\g<1>0x200000", - content, - ) - - if updated != content: - with open(local_ld, "w") as f: - f.write(updated) - print("ESPHome: Patched IRAM size to 2MB for testing mode") - - -# Hook into the build process right before linking -# This runs after PlatformIO has already preprocessed the linker scripts -env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", patch_linker_script_after_preprocess) diff --git a/esphome/components/esp8266/testing_mode.py.script b/esphome/components/esp8266/testing_mode.py.script new file mode 100644 index 0000000000..44d84b765c --- /dev/null +++ b/esphome/components/esp8266/testing_mode.py.script @@ -0,0 +1,166 @@ +import os +import re + +# pylint: disable=E0602 +Import("env") # noqa + + +# Memory sizes for testing mode (allow larger builds for CI component grouping) +TESTING_IRAM_SIZE = "0x200000" # 2MB +TESTING_DRAM_SIZE = "0x200000" # 2MB +TESTING_FLASH_SIZE = "0x2000000" # 32MB + + +def patch_segment_size(content, segment_name, new_size, label): + """Patch a memory segment's length in linker script. + + Args: + content: Linker script content + segment_name: Name of the segment (e.g., 'iram1_0_seg') + new_size: New size as hex string (e.g., '0x200000') + label: Human-readable label for logging (e.g., 'IRAM') + + Returns: + Tuple of (patched_content, was_patched) + """ + # Match: segment_name : org = 0x..., len = 0x... + pattern = rf"({segment_name}\s*:\s*org\s*=\s*0x[0-9a-fA-F]+\s*,\s*len\s*=\s*)0x[0-9a-fA-F]+" + new_content = re.sub(pattern, rf"\g<1>{new_size}", content) + return new_content, new_content != content + + +def apply_memory_patches(content): + """Apply IRAM, DRAM, and Flash patches to linker script content. + + Args: + content: Linker script content as string + + Returns: + Patched content as string + """ + patches_applied = [] + + # Patch IRAM (for larger code in IRAM) + content, patched = patch_segment_size(content, "iram1_0_seg", TESTING_IRAM_SIZE, "IRAM") + if patched: + patches_applied.append("IRAM") + + # Patch DRAM (for larger BSS/data sections) + content, patched = patch_segment_size(content, "dram0_0_seg", TESTING_DRAM_SIZE, "DRAM") + if patched: + patches_applied.append("DRAM") + + # Patch Flash (for larger code sections) + content, patched = patch_segment_size(content, "irom0_0_seg", TESTING_FLASH_SIZE, "Flash") + if patched: + patches_applied.append("Flash") + + if patches_applied: + iram_mb = int(TESTING_IRAM_SIZE, 16) // (1024 * 1024) + dram_mb = int(TESTING_DRAM_SIZE, 16) // (1024 * 1024) + flash_mb = int(TESTING_FLASH_SIZE, 16) // (1024 * 1024) + print(f" Patched memory segments: {', '.join(patches_applied)} (IRAM/DRAM: {iram_mb}MB, Flash: {flash_mb}MB)") + + return content + + +def patch_linker_script_file(filepath, description): + """Patch a linker script file in the build directory with enlarged memory segments. + + This function modifies linker scripts in the build directory only (never SDK files). + It patches IRAM, DRAM, and Flash segments to allow larger builds in testing mode. + + Args: + filepath: Path to the linker script file in the build directory + description: Human-readable description for logging + + Returns: + True if the file was patched, False if already patched or not found + """ + if not os.path.exists(filepath): + print(f"ESPHome: {description} not found at {filepath}") + return False + + print(f"ESPHome: Patching {description}...") + with open(filepath, "r") as f: + content = f.read() + + patched_content = apply_memory_patches(content) + + if patched_content != content: + with open(filepath, "w") as f: + f.write(patched_content) + print(f"ESPHome: Successfully patched {description}") + return True + else: + print(f"ESPHome: {description} already patched or no changes needed") + return False + + +def patch_local_linker_script(source, target, env): + """Patch the local.eagle.app.v6.common.ld in build directory. + + This patches the preprocessed linker script that PlatformIO creates in the build + directory, enlarging IRAM, DRAM, and Flash segments for testing mode. + + Args: + source: SCons source nodes + target: SCons target nodes + env: SCons environment + """ + # Check if we're in testing mode + build_flags = env.get("BUILD_FLAGS", []) + testing_mode = any("-DESPHOME_TESTING_MODE" in flag for flag in build_flags) + + if not testing_mode: + return + + # Patch the local linker script if it exists + build_dir = env.subst("$BUILD_DIR") + ld_dir = os.path.join(build_dir, "ld") + if os.path.exists(ld_dir): + local_ld = os.path.join(ld_dir, "local.eagle.app.v6.common.ld") + if os.path.exists(local_ld): + patch_linker_script_file(local_ld, "local.eagle.app.v6.common.ld") + + +# Check if we're in testing mode +build_flags = env.get("BUILD_FLAGS", []) +testing_mode = any("-DESPHOME_TESTING_MODE" in flag for flag in build_flags) + +if testing_mode: + # Create a custom linker script in the build directory with patched memory limits + # This allows larger IRAM/DRAM/Flash for CI component grouping tests + build_dir = env.subst("$BUILD_DIR") + ldscript = env.GetProjectOption("board_build.ldscript", "") + assert ldscript, "No linker script configured in board_build.ldscript" + + framework_dir = env.PioPlatform().get_package_dir("framework-arduinoespressif8266") + assert framework_dir is not None, "Could not find framework-arduinoespressif8266 package" + + # Read the original SDK linker script (read-only, SDK is never modified) + sdk_ld = os.path.join(framework_dir, "tools", "sdk", "ld", ldscript) + # Create a custom version in the build directory (isolated, temporary) + custom_ld = os.path.join(build_dir, f"testing_{ldscript}") + + if os.path.exists(sdk_ld) and not os.path.exists(custom_ld): + # Read the SDK linker script + with open(sdk_ld, "r") as f: + content = f.read() + + # Apply memory patches (IRAM: 2MB, DRAM: 2MB, Flash: 32MB) + patched_content = apply_memory_patches(content) + + # Write the patched linker script to the build directory + with open(custom_ld, "w") as f: + f.write(patched_content) + + print(f"ESPHome: Created custom linker script: {custom_ld}") + + # Tell the linker to use our custom script from the build directory + assert os.path.exists(custom_ld), f"Custom linker script not found: {custom_ld}" + env.Replace(LDSCRIPT_PATH=custom_ld) + print(f"ESPHome: Using custom linker script with patched memory limits") + + # Also patch local.eagle.app.v6.common.ld after PlatformIO creates it + env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", patch_local_linker_script) diff --git a/tests/test_build_components/build_components_base.esp8266-ard.yaml b/tests/test_build_components/build_components_base.esp8266-ard.yaml index e4d6607c86..1e2d614392 100644 --- a/tests/test_build_components/build_components_base.esp8266-ard.yaml +++ b/tests/test_build_components/build_components_base.esp8266-ard.yaml @@ -3,7 +3,7 @@ esphome: friendly_name: $component_name esp8266: - board: d1_mini + board: d1_mini_pro logger: level: VERY_VERBOSE From 3b6ff615e8e6c34252a9fd8c82e5f003a78516e5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 16:39:15 -1000 Subject: [PATCH 194/526] [ci] Fix clang-tidy split decision to account for component dependencies (#11430) --- script/determine-jobs.py | 63 +++++++++--- tests/script/test_determine_jobs.py | 143 +++++++++++++++++++++++++--- 2 files changed, 180 insertions(+), 26 deletions(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 1877894fc4..0d77177e28 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -57,6 +57,7 @@ from helpers import ( get_component_from_path, get_component_test_files, get_components_from_integration_fixtures, + git_ls_files, parse_test_filename, root_path, ) @@ -162,6 +163,26 @@ def should_run_integration_tests(branch: str | None = None) -> bool: return False +@cache +def _is_clang_tidy_full_scan() -> bool: + """Check if clang-tidy configuration changed (requires full scan). + + Returns: + True if full scan is needed (hash changed), False otherwise. + """ + try: + result = subprocess.run( + [os.path.join(root_path, "script", "clang_tidy_hash.py"), "--check"], + capture_output=True, + check=False, + ) + # Exit 0 means hash changed (full scan needed) + return result.returncode == 0 + except Exception: + # If hash check fails, run full scan to be safe + return True + + def should_run_clang_tidy(branch: str | None = None) -> bool: """Determine if clang-tidy should run based on changed files. @@ -198,17 +219,7 @@ def should_run_clang_tidy(branch: str | None = None) -> bool: True if clang-tidy should run, False otherwise. """ # First check if clang-tidy configuration changed (full scan needed) - try: - result = subprocess.run( - [os.path.join(root_path, "script", "clang_tidy_hash.py"), "--check"], - capture_output=True, - check=False, - ) - # Exit 0 means hash changed (full scan needed) - if result.returncode == 0: - return True - except Exception: - # If hash check fails, run clang-tidy to be safe + if _is_clang_tidy_full_scan(): return True # Check if .clang-tidy.hash file itself was changed @@ -586,13 +597,37 @@ def main() -> None: # Detect components for memory impact analysis (merged config) memory_impact = detect_memory_impact_config(args.branch) + # Determine clang-tidy mode based on actual files that will be checked if run_clang_tidy: - if changed_cpp_file_count < CLANG_TIDY_SPLIT_THRESHOLD: - clang_tidy_mode = "nosplit" - else: + is_full_scan = _is_clang_tidy_full_scan() + + if is_full_scan: + # Full scan checks all files - always use split mode for efficiency clang_tidy_mode = "split" + files_to_check_count = -1 # Sentinel value for "all files" + else: + # Targeted scan - calculate actual files that will be checked + # This accounts for component dependencies, not just directly changed files + if changed_components: + # Count C++ files in all changed components (including dependencies) + all_cpp_files = list(git_ls_files(["*.cpp"]).keys()) + component_set = set(changed_components) + files_to_check_count = sum( + 1 + for f in all_cpp_files + if get_component_from_path(f) in component_set + ) + else: + # If no components changed, use the simple count of changed C++ files + files_to_check_count = changed_cpp_file_count + + if files_to_check_count < CLANG_TIDY_SPLIT_THRESHOLD: + clang_tidy_mode = "nosplit" + else: + clang_tidy_mode = "split" else: clang_tidy_mode = "disabled" + files_to_check_count = 0 # Build output output: dict[str, Any] = { diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 02aaad2e3a..44aea73990 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -71,6 +71,12 @@ def mock_changed_files() -> Generator[Mock, None, None]: yield mock +@pytest.fixture(autouse=True) +def clear_clang_tidy_cache() -> None: + """Clear the clang-tidy full scan cache before each test.""" + determine_jobs._is_clang_tidy_full_scan.cache_clear() + + def test_main_all_tests_should_run( mock_should_run_integration_tests: Mock, mock_should_run_clang_tidy: Mock, @@ -98,7 +104,10 @@ def test_main_all_tests_should_run( mock_subprocess_run.return_value = mock_result # Run main function with mocked argv - with patch("sys.argv", ["determine-jobs.py"]): + with ( + patch("sys.argv", ["determine-jobs.py"]), + patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=False), + ): determine_jobs.main() # Check output @@ -224,7 +233,10 @@ def test_main_with_branch_argument( ) mock_subprocess_run.return_value = mock_result - with patch("sys.argv", ["script.py", "-b", "main"]): + with ( + patch("sys.argv", ["script.py", "-b", "main"]), + patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=False), + ): determine_jobs.main() # Check that functions were called with branch @@ -363,16 +375,6 @@ def test_should_run_clang_tidy_hash_check_exception() -> None: result = determine_jobs.should_run_clang_tidy() assert result is True # Fail safe - run clang-tidy - # Even with C++ files, exception should trigger clang-tidy - with ( - patch.object( - determine_jobs, "changed_files", return_value=["esphome/core.cpp"] - ), - patch("subprocess.run", side_effect=Exception("Hash check failed")), - ): - result = determine_jobs.should_run_clang_tidy() - assert result is True - def test_should_run_clang_tidy_with_branch() -> None: """Test should_run_clang_tidy with branch argument.""" @@ -763,3 +765,120 @@ def test_detect_memory_impact_config_skips_base_bus_components(tmp_path: Path) - assert result["should_run"] == "true" assert result["components"] == ["wifi"] assert "i2c" not in result["components"] + + +# Tests for clang-tidy split mode logic + + +def test_clang_tidy_mode_full_scan( + mock_should_run_integration_tests: Mock, + mock_should_run_clang_tidy: Mock, + mock_should_run_clang_format: Mock, + mock_should_run_python_linters: Mock, + mock_subprocess_run: Mock, + mock_changed_files: Mock, + capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test that full scan (hash changed) always uses split mode.""" + monkeypatch.delenv("GITHUB_ACTIONS", raising=False) + + mock_should_run_integration_tests.return_value = False + mock_should_run_clang_tidy.return_value = True + mock_should_run_clang_format.return_value = False + mock_should_run_python_linters.return_value = False + + # Mock list-components.py output + mock_result = Mock() + mock_result.stdout = json.dumps({"directly_changed": [], "all_changed": []}) + mock_subprocess_run.return_value = mock_result + + # Mock full scan (hash changed) + with ( + patch("sys.argv", ["determine-jobs.py"]), + patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=True), + ): + determine_jobs.main() + + captured = capsys.readouterr() + output = json.loads(captured.out) + + # Full scan should always use split mode + assert output["clang_tidy_mode"] == "split" + + +@pytest.mark.parametrize( + ("component_count", "files_per_component", "expected_mode"), + [ + # Small PR: 5 files in 1 component -> nosplit + (1, 5, "nosplit"), + # Medium PR: 30 files in 2 components -> nosplit + (2, 15, "nosplit"), + # Medium PR: 64 files total -> nosplit (just under threshold) + (2, 32, "nosplit"), + # Large PR: 65 files total -> split (at threshold) + (2, 33, "split"), # 2 * 33 = 66 files + # Large PR: 100 files in 10 components -> split + (10, 10, "split"), + ], + ids=[ + "1_comp_5_files_nosplit", + "2_comp_30_files_nosplit", + "2_comp_64_files_nosplit_under_threshold", + "2_comp_66_files_split_at_threshold", + "10_comp_100_files_split", + ], +) +def test_clang_tidy_mode_targeted_scan( + component_count: int, + files_per_component: int, + expected_mode: str, + mock_should_run_integration_tests: Mock, + mock_should_run_clang_tidy: Mock, + mock_should_run_clang_format: Mock, + mock_should_run_python_linters: Mock, + mock_subprocess_run: Mock, + mock_changed_files: Mock, + capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test clang-tidy mode selection based on files_to_check count.""" + monkeypatch.delenv("GITHUB_ACTIONS", raising=False) + + mock_should_run_integration_tests.return_value = False + mock_should_run_clang_tidy.return_value = True + mock_should_run_clang_format.return_value = False + mock_should_run_python_linters.return_value = False + + # Create component names + components = [f"comp{i}" for i in range(component_count)] + + # Mock list-components.py output + mock_result = Mock() + mock_result.stdout = json.dumps( + {"directly_changed": components, "all_changed": components} + ) + mock_subprocess_run.return_value = mock_result + + # Mock git_ls_files to return files for each component + cpp_files = { + f"esphome/components/{comp}/file{i}.cpp": 0 + for comp in components + for i in range(files_per_component) + } + + # Create a mock that returns the cpp_files dict for any call + def mock_git_ls_files(patterns=None): + return cpp_files + + with ( + patch("sys.argv", ["determine-jobs.py"]), + patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=False), + patch.object(determine_jobs, "git_ls_files", side_effect=mock_git_ls_files), + ): + determine_jobs.main() + + captured = capsys.readouterr() + output = json.loads(captured.out) + + assert output["clang_tidy_mode"] == expected_mode From a809a137294239e6587b6c7d2df63b7062d44cdc Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:46:50 -0400 Subject: [PATCH 195/526] [core] Add support for extern "C" includes (#11422) --- esphome/const.py | 1 + esphome/core/config.py | 54 ++++++++++++++++++++-------- tests/unit_tests/core/test_config.py | 29 +++++++++++++++ 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/esphome/const.py b/esphome/const.py index ce1c033e41..3bbc6b8b3f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -471,6 +471,7 @@ CONF_IMPORT_REACTIVE_ENERGY = "import_reactive_energy" CONF_INC_PIN = "inc_pin" CONF_INCLUDE_INTERNAL = "include_internal" CONF_INCLUDES = "includes" +CONF_INCLUDES_C = "includes_c" CONF_INDEX = "index" CONF_INDOOR = "indoor" CONF_INFRARED = "infrared" diff --git a/esphome/core/config.py b/esphome/core/config.py index 8a5876dbcf..2740453808 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -21,6 +21,7 @@ from esphome.const import ( CONF_FRIENDLY_NAME, CONF_ID, CONF_INCLUDES, + CONF_INCLUDES_C, CONF_LIBRARIES, CONF_MIN_VERSION, CONF_NAME, @@ -227,6 +228,7 @@ CONFIG_SCHEMA = cv.All( } ), cv.Optional(CONF_INCLUDES, default=[]): cv.ensure_list(valid_include), + cv.Optional(CONF_INCLUDES_C, default=[]): cv.ensure_list(valid_include), cv.Optional(CONF_LIBRARIES, default=[]): cv.ensure_list(cv.string_strict), cv.Optional(CONF_NAME_ADD_MAC_SUFFIX, default=False): cv.boolean, cv.Optional(CONF_DEBUG_SCHEDULER, default=False): cv.boolean, @@ -302,6 +304,17 @@ def _list_target_platforms(): return target_platforms +def _sort_includes_by_type(includes: list[str]) -> tuple[list[str], list[str]]: + system_includes = [] + other_includes = [] + for include in includes: + if include.startswith("<") and include.endswith(">"): + system_includes.append(include) + else: + other_includes.append(include) + return system_includes, other_includes + + def preload_core_config(config, result) -> str: with cv.prepend_path(CONF_ESPHOME): conf = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME]) @@ -339,7 +352,7 @@ def preload_core_config(config, result) -> str: return target_platforms[0] -def include_file(path: Path, basename: Path): +def include_file(path: Path, basename: Path, is_c_header: bool = False): parts = basename.parts dst = CORE.relative_src_path(*parts) copy_file_if_changed(path, dst) @@ -347,7 +360,14 @@ def include_file(path: Path, basename: Path): ext = path.suffix if ext in [".h", ".hpp", ".tcc"]: # Header, add include statement - cg.add_global(cg.RawStatement(f'#include "{basename}"')) + if is_c_header: + # Wrap in extern "C" block for C headers + cg.add_global( + cg.RawStatement(f'extern "C" {{\n #include "{basename}"\n}}') + ) + else: + # Regular include + cg.add_global(cg.RawStatement(f'#include "{basename}"')) ARDUINO_GLUE_CODE = """\ @@ -377,7 +397,7 @@ async def add_arduino_global_workaround(): @coroutine_with_priority(CoroPriority.FINAL) -async def add_includes(includes: list[str]) -> None: +async def add_includes(includes: list[str], is_c_header: bool = False) -> None: # Add includes at the very end, so that the included files can access global variables for include in includes: path = CORE.relative_config_path(include) @@ -385,11 +405,11 @@ async def add_includes(includes: list[str]) -> None: # Directory, copy tree for p in walk_files(path): basename = p.relative_to(path.parent) - include_file(p, basename) + include_file(p, basename, is_c_header) else: # Copy file basename = Path(path.name) - include_file(path, basename) + include_file(path, basename, is_c_header) @coroutine_with_priority(CoroPriority.FINAL) @@ -494,19 +514,25 @@ async def to_code(config: ConfigType) -> None: CORE.add_job(add_arduino_global_workaround) if config[CONF_INCLUDES]: - # Get the <...> includes - system_includes = [] - other_includes = [] - for include in config[CONF_INCLUDES]: - if include.startswith("<") and include.endswith(">"): - system_includes.append(include) - else: - other_includes.append(include) + system_includes, other_includes = _sort_includes_by_type(config[CONF_INCLUDES]) # <...> includes should be at the start for include in system_includes: cg.add_global(cg.RawStatement(f"#include {include}"), prepend=True) # Other includes should be at the end - CORE.add_job(add_includes, other_includes) + CORE.add_job(add_includes, other_includes, False) + + if config[CONF_INCLUDES_C]: + system_includes, other_includes = _sort_includes_by_type( + config[CONF_INCLUDES_C] + ) + # <...> includes should be at the start + for include in system_includes: + cg.add_global( + cg.RawStatement(f'extern "C" {{\n #include {include}\n}}'), + prepend=True, + ) + # Other includes should be at the end + CORE.add_job(add_includes, other_includes, True) if project_conf := config.get(CONF_PROJECT): cg.add_define("ESPHOME_PROJECT_NAME", project_conf[CONF_NAME]) diff --git a/tests/unit_tests/core/test_config.py b/tests/unit_tests/core/test_config.py index 4fddfc9678..a1e4627dc9 100644 --- a/tests/unit_tests/core/test_config.py +++ b/tests/unit_tests/core/test_config.py @@ -517,6 +517,35 @@ def test_include_file_cpp(tmp_path: Path, mock_copy_file_if_changed: Mock) -> No mock_cg.add_global.assert_not_called() +def test_include_file_with_c_header( + tmp_path: Path, mock_copy_file_if_changed: Mock +) -> None: + """Test include_file wraps header in extern C block when is_c_header is True.""" + src_file = tmp_path / "c_library.h" + src_file.write_text("// C library header") + + CORE.build_path = tmp_path / "build" + + with patch("esphome.core.config.cg") as mock_cg: + # Mock RawStatement to capture the text + mock_raw_statement = MagicMock() + mock_raw_statement.text = "" + + def raw_statement_side_effect(text): + mock_raw_statement.text = text + return mock_raw_statement + + mock_cg.RawStatement.side_effect = raw_statement_side_effect + + config.include_file(src_file, Path("c_library.h"), is_c_header=True) + + mock_copy_file_if_changed.assert_called_once() + mock_cg.add_global.assert_called_once() + # Check that include statement is wrapped in extern "C" block + assert 'extern "C"' in mock_raw_statement.text + assert '#include "c_library.h"' in mock_raw_statement.text + + def test_get_usable_cpu_count() -> None: """Test get_usable_cpu_count returns CPU count.""" count = config.get_usable_cpu_count() From 85959e3004218deb774c7e996a788c913d71830e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 16:47:13 -1000 Subject: [PATCH 196/526] [sensor,text_sensor,binary_sensor] Optimize filter parameters with std::initializer_list (#11426) --- esphome/components/binary_sensor/binary_sensor.cpp | 2 +- esphome/components/binary_sensor/binary_sensor.h | 4 ++-- esphome/components/sensor/sensor.cpp | 4 ++-- esphome/components/sensor/sensor.h | 6 +++--- esphome/components/text_sensor/text_sensor.cpp | 4 ++-- esphome/components/text_sensor/text_sensor.h | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 39319d3c1c..33b3de6d72 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -51,7 +51,7 @@ void BinarySensor::add_filter(Filter *filter) { last_filter->next_ = filter; } } -void BinarySensor::add_filters(const std::vector &filters) { +void BinarySensor::add_filters(std::initializer_list filters) { for (Filter *filter : filters) { this->add_filter(filter); } diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 2bd17d97c9..c1661d710f 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -4,7 +4,7 @@ #include "esphome/core/helpers.h" #include "esphome/components/binary_sensor/filter.h" -#include +#include namespace esphome { @@ -48,7 +48,7 @@ class BinarySensor : public StatefulEntityBase, public EntityBase_DeviceCl void publish_initial_state(bool new_state); void add_filter(Filter *filter); - void add_filters(const std::vector &filters); + void add_filters(std::initializer_list filters); // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index 4292b8c0bc..92da4345b7 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -107,12 +107,12 @@ void Sensor::add_filter(Filter *filter) { } filter->initialize(this, nullptr); } -void Sensor::add_filters(const std::vector &filters) { +void Sensor::add_filters(std::initializer_list filters) { for (Filter *filter : filters) { this->add_filter(filter); } } -void Sensor::set_filters(const std::vector &filters) { +void Sensor::set_filters(std::initializer_list filters) { this->clear_filters(); this->add_filters(filters); } diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index f3fa601a5e..a4210e5e6c 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -6,7 +6,7 @@ #include "esphome/core/log.h" #include "esphome/components/sensor/filter.h" -#include +#include #include namespace esphome { @@ -77,10 +77,10 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa * SlidingWindowMovingAverageFilter(15, 15), // average over last 15 values * }); */ - void add_filters(const std::vector &filters); + void add_filters(std::initializer_list filters); /// Clear the filters and replace them by filters. - void set_filters(const std::vector &filters); + void set_filters(std::initializer_list filters); /// Clear the entire filter chain. void clear_filters(); diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp index 17bf20466e..0294d65861 100644 --- a/esphome/components/text_sensor/text_sensor.cpp +++ b/esphome/components/text_sensor/text_sensor.cpp @@ -51,12 +51,12 @@ void TextSensor::add_filter(Filter *filter) { } filter->initialize(this, nullptr); } -void TextSensor::add_filters(const std::vector &filters) { +void TextSensor::add_filters(std::initializer_list filters) { for (Filter *filter : filters) { this->add_filter(filter); } } -void TextSensor::set_filters(const std::vector &filters) { +void TextSensor::set_filters(std::initializer_list filters) { this->clear_filters(); this->add_filters(filters); } diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index abbea27b59..db2e857ae3 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -5,7 +5,7 @@ #include "esphome/core/helpers.h" #include "esphome/components/text_sensor/filter.h" -#include +#include #include namespace esphome { @@ -37,10 +37,10 @@ class TextSensor : public EntityBase, public EntityBase_DeviceClass { void add_filter(Filter *filter); /// Add a list of vectors to the back of the filter chain. - void add_filters(const std::vector &filters); + void add_filters(std::initializer_list filters); /// Clear the filters and replace them by filters. - void set_filters(const std::vector &filters); + void set_filters(std::initializer_list filters); /// Clear the entire filter chain. void clear_filters(); From 040130e35712fcd6e7237c73be575605138cce4a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 17:02:07 -1000 Subject: [PATCH 197/526] [ci] Fix memory impact workflow for new components (#11421) --- script/test_build_components.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/script/test_build_components.py b/script/test_build_components.py index 77c97a8773..e369b0364e 100755 --- a/script/test_build_components.py +++ b/script/test_build_components.py @@ -966,11 +966,33 @@ def test_components( # Find all component tests all_tests = {} for pattern in component_patterns: + # Skip empty patterns (happens when components list is empty string) + if not pattern: + continue all_tests.update(find_component_tests(tests_dir, pattern, base_only)) + # If no components found, build a reference configuration for baseline comparison + # Create a synthetic "empty" component test that will build just the base config if not all_tests: print(f"No components found matching: {component_patterns}") - return 1 + print( + "Building reference configuration with no components for baseline comparison..." + ) + + # Create empty test files for each platform (or filtered platform) + reference_tests: list[Path] = [] + for platform_name, base_file in platform_bases.items(): + if platform_filter and not platform_name.startswith(platform_filter): + continue + # Create an empty test file named to match the platform + empty_test_file = build_dir / f"reference.{platform_name}.yaml" + empty_test_file.write_text( + "# Empty component test for baseline reference\n" + ) + reference_tests.append(empty_test_file) + + # Add to all_tests dict with component name "reference" + all_tests["reference"] = reference_tests print(f"Found {len(all_tests)} components to test") From 77203f0cb4428412191714605c8b5f4e1ee31a4f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 17:24:51 -1000 Subject: [PATCH 198/526] [text_sensor] Optimize filters with FixedVector (1.6KB flash savings) (#11423) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/text_sensor/__init__.py | 25 ++++++++---- esphome/components/text_sensor/filter.cpp | 18 ++++++--- esphome/components/text_sensor/filter.h | 44 ++++++++++++++++------ 3 files changed, 64 insertions(+), 23 deletions(-) diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index f7b3b5c55e..7a9e947abd 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -110,17 +110,28 @@ def validate_mapping(value): "substitute", SubstituteFilter, cv.ensure_list(validate_mapping) ) async def substitute_filter_to_code(config, filter_id): - from_strings = [conf[CONF_FROM] for conf in config] - to_strings = [conf[CONF_TO] for conf in config] - return cg.new_Pvariable(filter_id, from_strings, to_strings) + substitutions = [ + cg.StructInitializer( + cg.MockObj("Substitution", "esphome::text_sensor::"), + ("from", conf[CONF_FROM]), + ("to", conf[CONF_TO]), + ) + for conf in config + ] + return cg.new_Pvariable(filter_id, substitutions) @FILTER_REGISTRY.register("map", MapFilter, cv.ensure_list(validate_mapping)) async def map_filter_to_code(config, filter_id): - map_ = cg.std_ns.class_("map").template(cg.std_string, cg.std_string) - return cg.new_Pvariable( - filter_id, map_([(item[CONF_FROM], item[CONF_TO]) for item in config]) - ) + mappings = [ + cg.StructInitializer( + cg.MockObj("Substitution", "esphome::text_sensor::"), + ("from", conf[CONF_FROM]), + ("to", conf[CONF_TO]), + ) + for conf in config + ] + return cg.new_Pvariable(filter_id, mappings) validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") diff --git a/esphome/components/text_sensor/filter.cpp b/esphome/components/text_sensor/filter.cpp index 80edae2b6c..a242b43b1c 100644 --- a/esphome/components/text_sensor/filter.cpp +++ b/esphome/components/text_sensor/filter.cpp @@ -62,19 +62,27 @@ optional AppendFilter::new_value(std::string value) { return value optional PrependFilter::new_value(std::string value) { return this->prefix_ + value; } // Substitute +SubstituteFilter::SubstituteFilter(const std::initializer_list &substitutions) + : substitutions_(substitutions) {} + optional SubstituteFilter::new_value(std::string value) { std::size_t pos; - for (size_t i = 0; i < this->from_strings_.size(); i++) { - while ((pos = value.find(this->from_strings_[i])) != std::string::npos) - value.replace(pos, this->from_strings_[i].size(), this->to_strings_[i]); + for (const auto &sub : this->substitutions_) { + while ((pos = value.find(sub.from)) != std::string::npos) + value.replace(pos, sub.from.size(), sub.to); } return value; } // Map +MapFilter::MapFilter(const std::initializer_list &mappings) : mappings_(mappings) {} + optional MapFilter::new_value(std::string value) { - auto item = mappings_.find(value); - return item == mappings_.end() ? value : item->second; + for (const auto &mapping : this->mappings_) { + if (mapping.from == value) + return mapping.to; + } + return value; // Pass through if no match } } // namespace text_sensor diff --git a/esphome/components/text_sensor/filter.h b/esphome/components/text_sensor/filter.h index 2de9010b88..c77c221235 100644 --- a/esphome/components/text_sensor/filter.h +++ b/esphome/components/text_sensor/filter.h @@ -2,10 +2,6 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include -#include -#include -#include namespace esphome { namespace text_sensor { @@ -98,26 +94,52 @@ class PrependFilter : public Filter { std::string prefix_; }; +struct Substitution { + std::string from; + std::string to; +}; + /// A simple filter that replaces a substring with another substring class SubstituteFilter : public Filter { public: - SubstituteFilter(std::vector from_strings, std::vector to_strings) - : from_strings_(std::move(from_strings)), to_strings_(std::move(to_strings)) {} + explicit SubstituteFilter(const std::initializer_list &substitutions); optional new_value(std::string value) override; protected: - std::vector from_strings_; - std::vector to_strings_; + FixedVector substitutions_; }; -/// A filter that maps values from one set to another +/** A filter that maps values from one set to another + * + * Uses linear search instead of std::map for typical small datasets (2-20 mappings). + * Linear search on contiguous memory is faster than red-black tree lookups when: + * - Dataset is small (< ~30 items) + * - Memory is contiguous (cache-friendly, better CPU cache utilization) + * - No pointer chasing overhead (tree node traversal) + * - String comparison cost dominates lookup time + * + * Benchmark results (see benchmark_map_filter.cpp): + * - 2 mappings: Linear 1.26x faster than std::map + * - 5 mappings: Linear 2.25x faster than std::map + * - 10 mappings: Linear 1.83x faster than std::map + * - 20 mappings: Linear 1.59x faster than std::map + * - 30 mappings: Linear 1.09x faster than std::map + * - 40 mappings: std::map 1.27x faster than Linear (break-even) + * + * Benefits over std::map: + * - ~2KB smaller flash (no red-black tree code) + * - ~24-32 bytes less RAM per mapping (no tree node overhead) + * - Faster for typical ESPHome usage (2-10 mappings common, 20+ rare) + * + * Break-even point: ~35-40 mappings, but ESPHome configs rarely exceed 20 + */ class MapFilter : public Filter { public: - MapFilter(std::map mappings) : mappings_(std::move(mappings)) {} + explicit MapFilter(const std::initializer_list &mappings); optional new_value(std::string value) override; protected: - std::map mappings_; + FixedVector mappings_; }; } // namespace text_sensor From 0938609f7af4c146401e646aa20051c843f63c22 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:58:26 +1300 Subject: [PATCH 199/526] [improv] Put next_url behind defines to save flash (#11420) Co-authored-by: J. Nick Koston --- esphome/components/esp32_improv/__init__.py | 2 +- .../components/esp32_improv/esp32_improv_component.cpp | 2 ++ esphome/components/improv_base/__init__.py | 10 +++++++--- esphome/components/improv_base/improv_base.cpp | 3 +++ esphome/components/improv_base/improv_base.h | 5 +++++ esphome/components/improv_serial/__init__.py | 2 +- .../improv_serial/improv_serial_component.cpp | 2 ++ esphome/core/defines.h | 2 ++ 8 files changed, 23 insertions(+), 5 deletions(-) diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index a55c819e6f..1a7194da81 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -112,7 +112,7 @@ async def to_code(config): cg.add_define("USE_IMPROV") - await improv_base.setup_improv_core(var, config) + await improv_base.setup_improv_core(var, config, "esp32_improv") cg.add(var.set_identify_duration(config[CONF_IDENTIFY_DURATION])) cg.add(var.set_authorized_duration(config[CONF_AUTHORIZED_DURATION])) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 329349b531..56436b9d3d 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -389,11 +389,13 @@ void ESP32ImprovComponent::check_wifi_connection_() { std::string url_strings[3]; size_t url_count = 0; +#ifdef USE_ESP32_IMPROV_NEXT_URL // Add next_url if configured (should be first per Improv BLE spec) std::string next_url = this->get_formatted_next_url_(); if (!next_url.empty()) { url_strings[url_count++] = std::move(next_url); } +#endif // Add default URLs for backward compatibility url_strings[url_count++] = ESPHOME_MY_LINK; diff --git a/esphome/components/improv_base/__init__.py b/esphome/components/improv_base/__init__.py index aa75f4d89c..e175aa2220 100644 --- a/esphome/components/improv_base/__init__.py +++ b/esphome/components/improv_base/__init__.py @@ -3,6 +3,8 @@ import re import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import __version__ +from esphome.cpp_generator import MockObj +from esphome.types import ConfigType CODEOWNERS = ["@esphome/core"] @@ -35,7 +37,9 @@ def _process_next_url(url: str): return url -async def setup_improv_core(var, config): - if CONF_NEXT_URL in config: - cg.add(var.set_next_url(_process_next_url(config[CONF_NEXT_URL]))) +async def setup_improv_core(var: MockObj, config: ConfigType, component: str): + if next_url := config.get(CONF_NEXT_URL): + cg.add(var.set_next_url(_process_next_url(next_url))) + cg.add_define(f"USE_{component.upper()}_NEXT_URL") + cg.add_library("improv/Improv", "1.2.4") diff --git a/esphome/components/improv_base/improv_base.cpp b/esphome/components/improv_base/improv_base.cpp index 233098e6cd..2091390f95 100644 --- a/esphome/components/improv_base/improv_base.cpp +++ b/esphome/components/improv_base/improv_base.cpp @@ -2,10 +2,12 @@ #include "esphome/components/network/util.h" #include "esphome/core/application.h" +#include "esphome/core/defines.h" namespace esphome { namespace improv_base { +#if defined(USE_ESP32_IMPROV_NEXT_URL) || defined(USE_IMPROV_SERIAL_NEXT_URL) static constexpr const char DEVICE_NAME_PLACEHOLDER[] = "{{device_name}}"; static constexpr size_t DEVICE_NAME_PLACEHOLDER_LEN = sizeof(DEVICE_NAME_PLACEHOLDER) - 1; static constexpr const char IP_ADDRESS_PLACEHOLDER[] = "{{ip_address}}"; @@ -43,6 +45,7 @@ std::string ImprovBase::get_formatted_next_url_() { return formatted_url; } +#endif } // namespace improv_base } // namespace esphome diff --git a/esphome/components/improv_base/improv_base.h b/esphome/components/improv_base/improv_base.h index 90cd02a4ab..e4138479df 100644 --- a/esphome/components/improv_base/improv_base.h +++ b/esphome/components/improv_base/improv_base.h @@ -1,17 +1,22 @@ #pragma once #include +#include "esphome/core/defines.h" namespace esphome { namespace improv_base { class ImprovBase { public: +#if defined(USE_ESP32_IMPROV_NEXT_URL) || defined(USE_IMPROV_SERIAL_NEXT_URL) void set_next_url(const std::string &next_url) { this->next_url_ = next_url; } +#endif protected: +#if defined(USE_ESP32_IMPROV_NEXT_URL) || defined(USE_IMPROV_SERIAL_NEXT_URL) std::string get_formatted_next_url_(); std::string next_url_; +#endif }; } // namespace improv_base diff --git a/esphome/components/improv_serial/__init__.py b/esphome/components/improv_serial/__init__.py index 568b200a85..fb2b541707 100644 --- a/esphome/components/improv_serial/__init__.py +++ b/esphome/components/improv_serial/__init__.py @@ -43,4 +43,4 @@ FINAL_VALIDATE_SCHEMA = validate_logger async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) - await improv_base.setup_improv_core(var, config) + await improv_base.setup_improv_core(var, config, "improv_serial") diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 28245dcfdf..ce82504d3c 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -146,9 +146,11 @@ void ImprovSerialComponent::loop() { std::vector ImprovSerialComponent::build_rpc_settings_response_(improv::Command command) { std::vector urls; +#ifdef USE_IMPROV_SERIAL_NEXT_URL if (!this->next_url_.empty()) { urls.push_back(this->get_formatted_next_url_()); } +#endif #ifdef USE_WEBSERVER for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) { if (ip.is_ip4()) { diff --git a/esphome/core/defines.h b/esphome/core/defines.h index ff9afb9114..4e9fb078a0 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -44,6 +44,7 @@ #define USE_GRAPHICAL_DISPLAY_MENU #define USE_HOMEASSISTANT_TIME #define USE_HTTP_REQUEST_OTA_WATCHDOG_TIMEOUT 8000 // NOLINT +#define USE_IMPROV_SERIAL_NEXT_URL #define USE_JSON #define USE_LIGHT #define USE_LOCK @@ -186,6 +187,7 @@ #define USE_ESP32_CAMERA_JPEG_ENCODER #define USE_I2C #define USE_IMPROV +#define USE_ESP32_IMPROV_NEXT_URL #define USE_MICROPHONE #define USE_PSRAM #define USE_SOCKET_IMPL_BSD_SOCKETS From 73f5d01c2dec18f675cfae7ebfad6dbe8326e305 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 18:32:58 -1000 Subject: [PATCH 200/526] [core] Optimize automation actions memory usage with std::initializer_list (#11433) --- esphome/core/automation.h | 4 ++-- esphome/core/base_automation.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/core/automation.h b/esphome/core/automation.h index e156818312..0512752d50 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -243,7 +243,7 @@ template class ActionList { } this->actions_end_ = action; } - void add_actions(const std::vector *> &actions) { + void add_actions(const std::initializer_list *> &actions) { for (auto *action : actions) { this->add_action(action); } @@ -286,7 +286,7 @@ template class Automation { explicit Automation(Trigger *trigger) : trigger_(trigger) { this->trigger_->set_automation_parent(this); } void add_action(Action *action) { this->actions_.add_action(action); } - void add_actions(const std::vector *> &actions) { this->actions_.add_actions(actions); } + void add_actions(const std::initializer_list *> &actions) { this->actions_.add_actions(actions); } void stop() { this->actions_.stop(); } diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index f1248e0035..af8cde971b 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -194,12 +194,12 @@ template class IfAction : public Action { public: explicit IfAction(Condition *condition) : condition_(condition) {} - void add_then(const std::vector *> &actions) { + void add_then(const std::initializer_list *> &actions) { this->then_.add_actions(actions); this->then_.add_action(new LambdaAction([this](Ts... x) { this->play_next_(x...); })); } - void add_else(const std::vector *> &actions) { + void add_else(const std::initializer_list *> &actions) { this->else_.add_actions(actions); this->else_.add_action(new LambdaAction([this](Ts... x) { this->play_next_(x...); })); } @@ -240,7 +240,7 @@ template class WhileAction : public Action { public: WhileAction(Condition *condition) : condition_(condition) {} - void add_then(const std::vector *> &actions) { + void add_then(const std::initializer_list *> &actions) { this->then_.add_actions(actions); this->then_.add_action(new LambdaAction([this](Ts... x) { if (this->num_running_ > 0 && this->condition_->check_tuple(this->var_)) { @@ -287,7 +287,7 @@ template class RepeatAction : public Action { public: TEMPLATABLE_VALUE(uint32_t, count) - void add_then(const std::vector *> &actions) { + void add_then(const std::initializer_list *> &actions) { this->then_.add_actions(actions); this->then_.add_action(new LambdaAction([this](uint32_t iteration, Ts... x) { iteration++; From cd2d3f061d78018b2b16b35adb137e1e9b47cff6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 19:58:24 -1000 Subject: [PATCH 201/526] [espnow] Fix compilation error with initializer_list after #11433 (#11436) --- esphome/components/espnow/automation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/espnow/automation.h b/esphome/components/espnow/automation.h index 2416377859..5415b088fd 100644 --- a/esphome/components/espnow/automation.h +++ b/esphome/components/espnow/automation.h @@ -14,13 +14,13 @@ template class SendAction : public Action, public Parente TEMPLATABLE_VALUE(std::vector, data); public: - void add_on_sent(const std::vector *> &actions) { + void add_on_sent(const std::initializer_list *> &actions) { this->sent_.add_actions(actions); if (this->flags_.wait_for_sent) { this->sent_.add_action(new LambdaAction([this](Ts... x) { this->play_next_(x...); })); } } - void add_on_error(const std::vector *> &actions) { + void add_on_error(const std::initializer_list *> &actions) { this->error_.add_actions(actions); if (this->flags_.wait_for_sent) { this->error_.add_action(new LambdaAction([this](Ts... x) { From 7a2887e2ed24544da3ca6510d2ee1494c4685eba Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 20:39:05 -1000 Subject: [PATCH 202/526] [analyze-memory] Improve symbol categorization accuracy (#11440) --- esphome/analyze_memory/cli.py | 19 ++- esphome/analyze_memory/const.py | 289 ++++++++++++++++++++++++-------- 2 files changed, 235 insertions(+), 73 deletions(-) diff --git a/esphome/analyze_memory/cli.py b/esphome/analyze_memory/cli.py index 1695a00c19..718f42330d 100644 --- a/esphome/analyze_memory/cli.py +++ b/esphome/analyze_memory/cli.py @@ -231,9 +231,22 @@ class MemoryAnalyzerCLI(MemoryAnalyzer): api_component = (name, mem) break - # Combine all components to analyze: top ESPHome + all external + API if not already included - components_to_analyze = list(top_esphome_components) + list( - top_external_components + # Also include wifi_stack and other important system components if they exist + system_components_to_include = [ + # Empty list - we've finished debugging symbol categorization + # Add component names here if you need to debug their symbols + ] + system_components = [ + (name, mem) + for name, mem in components + if name in system_components_to_include + ] + + # Combine all components to analyze: top ESPHome + all external + API if not already included + system components + components_to_analyze = ( + list(top_esphome_components) + + list(top_external_components) + + system_components ) if api_component and api_component not in components_to_analyze: components_to_analyze.append(api_component) diff --git a/esphome/analyze_memory/const.py b/esphome/analyze_memory/const.py index c60b70aeec..78af82059f 100644 --- a/esphome/analyze_memory/const.py +++ b/esphome/analyze_memory/const.py @@ -127,40 +127,39 @@ SYMBOL_PATTERNS = { "tryget_socket_unconn", "cs_create_ctrl_sock", "netbuf_alloc", + "tcp_", # TCP protocol functions + "udp_", # UDP protocol functions + "lwip_", # LwIP stack functions + "eagle_lwip", # ESP-specific LwIP functions + "new_linkoutput", # Link output function + "acd_", # Address Conflict Detection (ACD) + "eth_", # Ethernet functions + "mac_enable_bb", # MAC baseband enable + "reassemble_and_dispatch", # Packet reassembly ], + # dhcp must come before libc to avoid "dhcp_select" matching "select" pattern + "dhcp": ["dhcp", "handle_dhcp"], "ipv6_stack": ["nd6_", "ip6_", "mld6_", "icmp6_", "icmp6_input"], - "wifi_stack": [ - "ieee80211", - "hostap", - "sta_", - "ap_", - "scan_", - "wifi_", - "wpa_", - "wps_", - "esp_wifi", - "cnx_", - "wpa3_", - "sae_", - "wDev_", - "ic_", - "mac_", - "esf_buf", - "gWpaSm", - "sm_WPA", - "eapol_", - "owe_", - "wifiLowLevelInit", - "s_do_mapping", - "gScanStruct", - "ppSearchTxframe", - "ppMapWaitTxq", - "ppFillAMPDUBar", - "ppCheckTxConnTrafficIdle", - "ppCalTkipMic", + # Order matters! More specific categories must come before general ones. + # mdns must come before bluetooth to avoid "_mdns_disable_pcb" matching "ble_" pattern + "mdns_lib": ["mdns"], + # memory_mgmt must come before wifi_stack to catch mmu_hal_* symbols + "memory_mgmt": [ + "mem_", + "memory_", + "tlsf_", + "memp_", + "pbuf_", + "pbuf_alloc", + "pbuf_copy_partial_pbuf", + "esp_mmu_map", + "mmu_hal_", + "s_do_mapping", # Memory mapping function, not WiFi + "hash_map_", # Hash map data structure + "umm_assimilate", # UMM malloc assimilation ], - "bluetooth": ["bt_", "ble_", "l2c_", "gatt_", "gap_", "hci_", "BT_init"], - "wifi_bt_coex": ["coex"], + # Bluetooth categories must come BEFORE wifi_stack to avoid misclassification + # Many BLE symbols contain patterns like "ble_" that would otherwise match wifi patterns "bluetooth_rom": ["r_ble", "r_lld", "r_llc", "r_llm"], "bluedroid_bt": [ "bluedroid", @@ -207,6 +206,61 @@ SYMBOL_PATTERNS = { "copy_extra_byte_in_db", "parse_read_local_supported_commands_response", ], + "bluetooth": [ + "bt_", + "_ble_", # More specific than "ble_" to avoid matching "able_", "enable_", "disable_" + "l2c_", + "l2ble_", # L2CAP for BLE + "gatt_", + "gap_", + "hci_", + "btsnd_hcic_", # Bluetooth HCI command send functions + "BT_init", + "BT_tx_", # Bluetooth transmit functions + "esp_ble_", # Catch esp_ble_* functions + ], + "bluetooth_ll": [ + "llm_", # Link layer manager + "llc_", # Link layer control + "lld_", # Link layer driver + "ld_acl_", # Link layer ACL (Asynchronous Connection-Oriented) + "llcp_", # Link layer control protocol + "lmp_", # Link manager protocol + ], + "wifi_bt_coex": ["coex"], + "wifi_stack": [ + "ieee80211", + "hostap", + "sta_", + "wifi_ap_", # More specific than "ap_" to avoid matching "cap_", "map_" + "wifi_scan_", # More specific than "scan_" to avoid matching "_scan_" in other contexts + "wifi_", + "wpa_", + "wps_", + "esp_wifi", + "cnx_", + "wpa3_", + "sae_", + "wDev_", + "ic_mac_", # More specific than "mac_" to avoid matching emac_ + "esf_buf", + "gWpaSm", + "sm_WPA", + "eapol_", + "owe_", + "wifiLowLevelInit", + # Removed "s_do_mapping" - this is memory management, not WiFi + "gScanStruct", + "ppSearchTxframe", + "ppMapWaitTxq", + "ppFillAMPDUBar", + "ppCheckTxConnTrafficIdle", + "ppCalTkipMic", + "phy_force_wifi", + "phy_unforce_wifi", + "write_wifi_chan", + "wifi_track_pll", + ], "crypto_math": [ "ecp_", "bignum_", @@ -231,13 +285,36 @@ SYMBOL_PATTERNS = { "p_256_init_curve", "shift_sub_rows", "rshift", + "rijndaelEncrypt", # AES Rijndael encryption + ], + # System and Arduino core functions must come before libc + "esp_system": [ + "system_", # ESP system functions + "postmortem_", # Postmortem reporting + ], + "arduino_core": [ + "pinMode", + "resetPins", + "millis", + "micros", + "delay(", # More specific - Arduino delay function with parenthesis + "delayMicroseconds", + "digitalWrite", + "digitalRead", + ], + "sntp": ["sntp_", "sntp_recv"], + "scheduler": [ + "run_scheduled_", + "compute_scheduled_", + "event_TaskQueue", ], "hw_crypto": ["esp_aes", "esp_sha", "esp_rsa", "esp_bignum", "esp_mpi"], "libc": [ "printf", "scanf", "malloc", - "free", + "_free", # More specific than "free" to match _free, __free_r, etc. but not arbitrary "free" substring + "umm_free", # UMM malloc free function "memcpy", "memset", "strcpy", @@ -259,7 +336,7 @@ SYMBOL_PATTERNS = { "_setenv_r", "_tzset_unlocked_r", "__tzcalc_limits", - "select", + "_select", # More specific than "select" to avoid matching "dhcp_select", etc. "scalbnf", "strtof", "strtof_l", @@ -316,8 +393,24 @@ SYMBOL_PATTERNS = { "CSWTCH$", "dst$", "sulp", + "_strtol_l", # String to long with locale + "__cvt", # Convert + "__utoa", # Unsigned to ASCII + "__global_locale", # Global locale + "_ctype_", # Character type + "impure_data", # Impure data + ], + "string_ops": [ + "strcmp", + "strncmp", + "strchr", + "strstr", + "strtok", + "strdup", + "strncasecmp_P", # String compare (case insensitive, from program memory) + "strnlen_P", # String length (from program memory) + "strncat_P", # String concatenate (from program memory) ], - "string_ops": ["strcmp", "strncmp", "strchr", "strstr", "strtok", "strdup"], "memory_alloc": ["malloc", "calloc", "realloc", "free", "_sbrk"], "file_io": [ "fread", @@ -338,10 +431,26 @@ SYMBOL_PATTERNS = { "vsscanf", ], "cpp_anonymous": ["_GLOBAL__N_", "n$"], - "cpp_runtime": ["__cxx", "_ZN", "_ZL", "_ZSt", "__gxx_personality", "_Z16"], - "exception_handling": ["__cxa_", "_Unwind_", "__gcc_personality", "uw_frame_state"], + # Plain C patterns only - C++ symbols will be categorized via DEMANGLED_PATTERNS + "nvs": ["nvs_"], # Plain C NVS functions + "ota": ["ota_", "OTA", "esp_ota", "app_desc"], + # cpp_runtime: Removed _ZN, _ZL to let DEMANGLED_PATTERNS categorize C++ symbols properly + # Only keep patterns that are truly runtime-specific and not categorizable by namespace + "cpp_runtime": ["__cxx", "_ZSt", "__gxx_personality", "_Z16"], + "exception_handling": [ + "__cxa_", + "_Unwind_", + "__gcc_personality", + "uw_frame_state", + "search_object", # Search for exception handling object + "get_cie_encoding", # Get CIE encoding + "add_fdes", # Add frame description entries + "fde_unencoded_compare", # Compare FDEs + "fde_mixed_encoding_compare", # Compare mixed encoding FDEs + "frame_downheap", # Frame heap operations + "frame_heapsort", # Frame heap sorting + ], "static_init": ["_GLOBAL__sub_I_"], - "mdns_lib": ["mdns"], "phy_radio": [ "phy_", "rf_", @@ -394,10 +503,47 @@ SYMBOL_PATTERNS = { "txcal_debuge_mode", "ant_wifitx_cfg", "reg_init_begin", + "tx_cap_init", # TX capacitance init + "ram_set_txcap", # RAM TX capacitance setting + "tx_atten_", # TX attenuation + "txiq_", # TX I/Q calibration + "ram_cal_", # RAM calibration + "ram_rxiq_", # RAM RX I/Q + "readvdd33", # Read VDD33 + "test_tout", # Test timeout + "tsen_meas", # Temperature sensor measurement + "bbpll_cal", # Baseband PLL calibration + "set_cal_", # Set calibration + "set_rfanagain_", # Set RF analog gain + "set_txdc_", # Set TX DC + "get_vdd33_", # Get VDD33 + "gen_rx_gain_table", # Generate RX gain table + "ram_ana_inf_gating_en", # RAM analog interface gating enable + "tx_cont_en", # TX continuous enable + "tx_delay_cfg", # TX delay configuration + "tx_gain_table_set", # TX gain table set + "check_and_reset_hw_deadlock", # Hardware deadlock check + "s_config", # System/hardware config + "chan14_mic_cfg", # Channel 14 MIC config + ], + "wifi_phy_pp": [ + "pp_", + "ppT", + "ppR", + "ppP", + "ppInstall", + "ppCalTxAMPDULength", + "ppCheckTx", # Packet processor TX check + "ppCal", # Packet processor calibration + "HdlAllBuffedEb", # Handle buffered EB ], - "wifi_phy_pp": ["pp_", "ppT", "ppR", "ppP", "ppInstall", "ppCalTxAMPDULength"], "wifi_lmac": ["lmac"], - "wifi_device": ["wdev", "wDev_"], + "wifi_device": [ + "wdev", + "wDev_", + "ic_set_sta", # Set station mode + "ic_set_vif", # Set virtual interface + ], "power_mgmt": [ "pm_", "sleep", @@ -406,15 +552,7 @@ SYMBOL_PATTERNS = { "deep_sleep", "power_down", "g_pm", - ], - "memory_mgmt": [ - "mem_", - "memory_", - "tlsf_", - "memp_", - "pbuf_", - "pbuf_alloc", - "pbuf_copy_partial_pbuf", + "pmc", # Power Management Controller ], "hal_layer": ["hal_"], "clock_mgmt": [ @@ -439,7 +577,6 @@ SYMBOL_PATTERNS = { "error_handling": ["panic", "abort", "assert", "error_", "fault"], "authentication": ["auth"], "ppp_protocol": ["ppp", "ipcp_", "lcp_", "chap_", "LcpEchoCheck"], - "dhcp": ["dhcp", "handle_dhcp"], "ethernet_phy": [ "emac_", "eth_phy_", @@ -618,7 +755,15 @@ SYMBOL_PATTERNS = { "ampdu_dispatch_upto", ], "ieee802_11": ["ieee802_11_", "ieee802_11_parse_elems"], - "rate_control": ["rssi_margin", "rcGetSched", "get_rate_fcc_index"], + "rate_control": [ + "rssi_margin", + "rcGetSched", + "get_rate_fcc_index", + "rcGetRate", # Get rate + "rc_get_", # Rate control getters + "rc_set_", # Rate control setters + "rc_enable_", # Rate control enable functions + ], "nan": ["nan_dp_", "nan_dp_post_tx", "nan_dp_delete_peer"], "channel_mgmt": ["chm_init", "chm_set_current_channel"], "trace": ["trc_init", "trc_onAmpduOp"], @@ -799,31 +944,18 @@ SYMBOL_PATTERNS = { "supports_interlaced_inquiry_scan", "supports_reading_remote_extended_features", ], - "bluetooth_ll": [ - "lld_pdu_", - "ld_acl_", - "lld_stop_ind_handler", - "lld_evt_winsize_change", - "config_lld_evt_funcs_reset", - "config_lld_funcs_reset", - "config_llm_funcs_reset", - "llm_set_long_adv_data", - "lld_retry_tx_prog", - "llc_link_sup_to_ind_handler", - "config_llc_funcs_reset", - "lld_evt_rxwin_compute", - "config_btdm_funcs_reset", - "config_ea_funcs_reset", - "llc_defalut_state_tab_reset", - "config_rwip_funcs_reset", - "ke_lmp_rx_flooding_detect", - ], } # Demangled patterns: patterns found in demangled C++ names DEMANGLED_PATTERNS = { "gpio_driver": ["GPIO"], "uart_driver": ["UART"], + # mdns_lib must come before network_stack to avoid "udp" matching "_udpReadBuffer" in MDNSResponder + "mdns_lib": [ + "MDNSResponder", + "MDNSImplementation", + "MDNS", + ], "network_stack": [ "lwip", "tcp", @@ -836,6 +968,24 @@ DEMANGLED_PATTERNS = { "ethernet", "ppp", "slip", + "UdpContext", # UDP context class + "DhcpServer", # DHCP server class + ], + "arduino_core": [ + "String::", # Arduino String class + "Print::", # Arduino Print class + "HardwareSerial::", # Serial class + "IPAddress::", # IP address class + "EspClass::", # ESP class + "experimental::_SPI", # Experimental SPI + ], + "ota": [ + "UpdaterClass", + "Updater::", + ], + "wifi": [ + "ESP8266WiFi", + "WiFi::", ], "wifi_stack": ["NetworkInterface"], "nimble_bt": [ @@ -854,7 +1004,6 @@ DEMANGLED_PATTERNS = { "rtti": ["__type_info", "__class_type_info"], "web_server_lib": ["AsyncWebServer", "AsyncWebHandler", "WebServer"], "async_tcp": ["AsyncClient", "AsyncServer"], - "mdns_lib": ["mdns"], "json_lib": [ "ArduinoJson", "JsonDocument", From 0b2f5fcd7eec47493a3df7162fabab64080a960f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 20:39:21 -1000 Subject: [PATCH 203/526] Add additional sensor filter tests (#11438) --- tests/components/sensor/common.yaml | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/components/sensor/common.yaml b/tests/components/sensor/common.yaml index 3f81f3f9ef..2180f66da8 100644 --- a/tests/components/sensor/common.yaml +++ b/tests/components/sensor/common.yaml @@ -173,3 +173,66 @@ sensor: timeout: 1000ms value: [42.0] - multiply: 2.0 + + # CalibrateLinearFilter - piecewise linear calibration + - platform: copy + source_id: source_sensor + name: "Calibrate Linear Two Points" + filters: + - calibrate_linear: + - 0.0 -> 0.0 + - 100.0 -> 100.0 + + - platform: copy + source_id: source_sensor + name: "Calibrate Linear Multiple Segments" + filters: + - calibrate_linear: + - 0.0 -> 0.0 + - 50.0 -> 55.0 + - 100.0 -> 102.5 + + - platform: copy + source_id: source_sensor + name: "Calibrate Linear Least Squares" + filters: + - calibrate_linear: + method: least_squares + datapoints: + - 0.0 -> 0.0 + - 50.0 -> 55.0 + - 100.0 -> 102.5 + + # CalibratePolynomialFilter - polynomial calibration + - platform: copy + source_id: source_sensor + name: "Calibrate Polynomial Degree 2" + filters: + - calibrate_polynomial: + degree: 2 + datapoints: + - 0.0 -> 0.0 + - 50.0 -> 55.0 + - 100.0 -> 102.5 + + - platform: copy + source_id: source_sensor + name: "Calibrate Polynomial Degree 3" + filters: + - calibrate_polynomial: + degree: 3 + datapoints: + - 0.0 -> 0.0 + - 25.0 -> 26.0 + - 50.0 -> 55.0 + - 100.0 -> 102.5 + + # OrFilter - filter branching + - platform: copy + source_id: source_sensor + name: "Or Filter with Multiple Branches" + filters: + - or: + - multiply: 2.0 + - offset: 10.0 + - lambda: return x * 3.0; From 0ae9009e414517506d983425f2801f2731839f1b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 20:39:50 -1000 Subject: [PATCH 204/526] [ci] Fix clang-tidy split mode for core file changes (#11434) --- script/determine-jobs.py | 44 ++++-- script/helpers.py | 233 +++++++++++++++++++++++++++- script/list-components.py | 180 ++------------------- tests/script/test_determine_jobs.py | 137 ++++++++++------ 4 files changed, 358 insertions(+), 236 deletions(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 0d77177e28..9721fd9756 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -43,7 +43,6 @@ from enum import StrEnum from functools import cache import json import os -from pathlib import Path import subprocess import sys from typing import Any @@ -53,10 +52,13 @@ from helpers import ( CPP_FILE_EXTENSIONS, PYTHON_FILE_EXTENSIONS, changed_files, + filter_component_files, get_all_dependencies, + get_changed_components, get_component_from_path, get_component_test_files, get_components_from_integration_fixtures, + get_components_with_dependencies, git_ls_files, parse_test_filename, root_path, @@ -561,16 +563,29 @@ def main() -> None: run_python_linters = should_run_python_linters(args.branch) changed_cpp_file_count = count_changed_cpp_files(args.branch) - # Get both directly changed and all changed components (with dependencies) in one call - script_path = Path(__file__).parent / "list-components.py" - cmd = [sys.executable, str(script_path), "--changed-with-deps"] - if args.branch: - cmd.extend(["-b", args.branch]) + # Get changed components + # get_changed_components() returns: + # None: Core files changed (need full scan) + # []: No components changed + # [list]: Changed components (already includes dependencies) + changed_components_result = get_changed_components() - result = subprocess.run(cmd, capture_output=True, text=True, check=True) - component_data = json.loads(result.stdout) - directly_changed_components = component_data["directly_changed"] - changed_components = component_data["all_changed"] + if changed_components_result is None: + # Core files changed - will trigger full clang-tidy scan + # No specific components to test + changed_components = [] + directly_changed_components = [] + is_core_change = True + else: + # Get both directly changed and all changed (with dependencies) + changed = changed_files(args.branch) + component_files = [f for f in changed if filter_component_files(f)] + + directly_changed_components = get_components_with_dependencies( + component_files, False + ) + changed_components = get_components_with_dependencies(component_files, True) + is_core_change = False # Filter to only components that have test files # Components without tests shouldn't generate CI test jobs @@ -581,11 +596,11 @@ def main() -> None: # Get directly changed components with tests (for isolated testing) # These will be tested WITHOUT --testing-mode in CI to enable full validation # (pin conflicts, etc.) since they contain the actual changes being reviewed - directly_changed_with_tests = [ + directly_changed_with_tests = { component for component in directly_changed_components if _component_has_tests(component) - ] + } # Get dependency-only components (for grouped testing) dependency_only_components = [ @@ -599,7 +614,8 @@ def main() -> None: # Determine clang-tidy mode based on actual files that will be checked if run_clang_tidy: - is_full_scan = _is_clang_tidy_full_scan() + # Full scan needed if: hash changed OR core files changed + is_full_scan = _is_clang_tidy_full_scan() or is_core_change if is_full_scan: # Full scan checks all files - always use split mode for efficiency @@ -638,7 +654,7 @@ def main() -> None: "python_linters": run_python_linters, "changed_components": changed_components, "changed_components_with_tests": changed_components_with_tests, - "directly_changed_components_with_tests": directly_changed_with_tests, + "directly_changed_components_with_tests": list(directly_changed_with_tests), "dependency_only_components_with_tests": dependency_only_components, "component_test_count": len(changed_components_with_tests), "directly_changed_count": len(directly_changed_with_tests), diff --git a/script/helpers.py b/script/helpers.py index edde3d78af..6b2bb2daef 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -1,5 +1,6 @@ from __future__ import annotations +from collections.abc import Callable from functools import cache import json import os @@ -7,6 +8,7 @@ import os.path from pathlib import Path import re import subprocess +import sys import time from typing import Any @@ -304,7 +306,10 @@ def get_changed_components() -> list[str] | None: for f in changed ) if core_cpp_changed: - print("Core C++/header files changed - will run full clang-tidy scan") + print( + "Core C++/header files changed - will run full clang-tidy scan", + file=sys.stderr, + ) return None # Use list-components.py to get changed components @@ -318,7 +323,10 @@ def get_changed_components() -> list[str] | None: return parse_list_components_output(result.stdout) except subprocess.CalledProcessError: # If the script fails, fall back to full scan - print("Could not determine changed components - will run full clang-tidy scan") + print( + "Could not determine changed components - will run full clang-tidy scan", + file=sys.stderr, + ) return None @@ -370,14 +378,14 @@ def _filter_changed_ci(files: list[str]) -> list[str]: if f in changed and not f.startswith(ESPHOME_COMPONENTS_PATH) ] if not files: - print("No files changed") + print("No files changed", file=sys.stderr) return files # Scenario 3: Specific components changed # Action: Check ALL files in each changed component # Convert component list to set for O(1) lookups component_set = set(components) - print(f"Changed components: {', '.join(sorted(components))}") + print(f"Changed components: {', '.join(sorted(components))}", file=sys.stderr) # The 'files' parameter contains ALL files in the codebase that clang-tidy would check. # We filter this down to only files in the changed components. @@ -648,3 +656,220 @@ def get_components_from_integration_fixtures() -> set[str]: components.add(item["platform"]) return components + + +def filter_component_files(file_path: str) -> bool: + """Check if a file path is a component file. + + Args: + file_path: Path to check + + Returns: + True if the file is in a component directory + """ + return file_path.startswith("esphome/components/") or file_path.startswith( + "tests/components/" + ) + + +def extract_component_names_from_files(files: list[str]) -> list[str]: + """Extract unique component names from a list of file paths. + + Args: + files: List of file paths + + Returns: + List of unique component names (preserves order) + """ + return list( + dict.fromkeys(comp for file in files if (comp := get_component_from_path(file))) + ) + + +def add_item_to_components_graph( + components_graph: dict[str, list[str]], parent: str, child: str +) -> None: + """Add a dependency relationship to the components graph. + + Args: + components_graph: Graph mapping parent components to their children + parent: Parent component name + child: Child component name (dependent) + """ + if not parent.startswith("__") and parent != child: + if parent not in components_graph: + components_graph[parent] = [] + if child not in components_graph[parent]: + components_graph[parent].append(child) + + +def resolve_auto_load( + auto_load: list[str] | Callable[[], list[str]] | Callable[[dict | None], list[str]], + config: dict | None = None, +) -> list[str]: + """Resolve AUTO_LOAD to a list, handling callables with or without config parameter. + + Args: + auto_load: The AUTO_LOAD value (list or callable) + config: Optional config to pass to callable AUTO_LOAD functions + + Returns: + List of component names to auto-load + """ + if not callable(auto_load): + return auto_load + + import inspect + + if inspect.signature(auto_load).parameters: + return auto_load(config) + return auto_load() + + +def create_components_graph() -> dict[str, list[str]]: + """Create a graph of component dependencies. + + Returns: + Dictionary mapping parent components to their children (dependencies) + """ + from pathlib import Path + + from esphome import const + from esphome.core import CORE + from esphome.loader import ComponentManifest, get_component, get_platform + + # The root directory of the repo + root = Path(__file__).parent.parent + components_dir = root / "esphome" / "components" + # Fake some directory so that get_component works + CORE.config_path = root + # Various configuration to capture different outcomes used by `AUTO_LOAD` function. + KEY_CORE = const.KEY_CORE + KEY_TARGET_FRAMEWORK = const.KEY_TARGET_FRAMEWORK + KEY_TARGET_PLATFORM = const.KEY_TARGET_PLATFORM + PLATFORM_ESP32 = const.PLATFORM_ESP32 + PLATFORM_ESP8266 = const.PLATFORM_ESP8266 + + TARGET_CONFIGURATIONS = [ + {KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: None}, + {KEY_TARGET_FRAMEWORK: "arduino", KEY_TARGET_PLATFORM: None}, + {KEY_TARGET_FRAMEWORK: "esp-idf", KEY_TARGET_PLATFORM: None}, + {KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: PLATFORM_ESP32}, + {KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: PLATFORM_ESP8266}, + ] + CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] + + components_graph = {} + platforms = [] + components: list[tuple[ComponentManifest, str, Path]] = [] + + for path in components_dir.iterdir(): + if not path.is_dir(): + continue + if not (path / "__init__.py").is_file(): + continue + name = path.name + comp = get_component(name) + if comp is None: + raise RuntimeError( + f"Cannot find component {name}. Make sure current path is pip installed ESPHome" + ) + + components.append((comp, name, path)) + if comp.is_platform_component: + platforms.append(name) + + platforms = set(platforms) + + for comp, name, path in components: + for dependency in comp.dependencies: + add_item_to_components_graph( + components_graph, dependency.split(".")[0], name + ) + + for target_config in TARGET_CONFIGURATIONS: + CORE.data[KEY_CORE] = target_config + for item in resolve_auto_load(comp.auto_load, config=None): + add_item_to_components_graph(components_graph, item, name) + # restore config + CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] + + for platform_path in path.iterdir(): + platform_name = platform_path.stem + if platform_name == name or platform_name not in platforms: + continue + platform = get_platform(platform_name, name) + if platform is None: + continue + + add_item_to_components_graph(components_graph, platform_name, name) + + for dependency in platform.dependencies: + add_item_to_components_graph( + components_graph, dependency.split(".")[0], name + ) + + for target_config in TARGET_CONFIGURATIONS: + CORE.data[KEY_CORE] = target_config + for item in resolve_auto_load(platform.auto_load, config={}): + add_item_to_components_graph(components_graph, item, name) + # restore config + CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] + + return components_graph + + +def find_children_of_component( + components_graph: dict[str, list[str]], component_name: str, depth: int = 0 +) -> list[str]: + """Find all components that depend on the given component (recursively). + + Args: + components_graph: Graph mapping parent components to their children + component_name: Component name to find children for + depth: Current recursion depth (max 10) + + Returns: + List of all dependent component names (may contain duplicates removed at end) + """ + if component_name not in components_graph: + return [] + + children = [] + + for child in components_graph[component_name]: + children.append(child) + if depth < 10: + children.extend( + find_children_of_component(components_graph, child, depth + 1) + ) + # Remove duplicate values + return list(set(children)) + + +def get_components_with_dependencies( + files: list[str], get_dependencies: bool = False +) -> list[str]: + """Get component names from files, optionally including their dependencies. + + Args: + files: List of file paths + get_dependencies: If True, include all dependent components + + Returns: + Sorted list of component names + """ + components = extract_component_names_from_files(files) + + if get_dependencies: + components_graph = create_components_graph() + + all_components = components.copy() + for c in components: + all_components.extend(find_children_of_component(components_graph, c)) + # Remove duplicate values + all_changed_components = list(set(all_components)) + + return sorted(all_changed_components) + + return sorted(components) diff --git a/script/list-components.py b/script/list-components.py index 11533ceb30..d768256c71 100755 --- a/script/list-components.py +++ b/script/list-components.py @@ -1,24 +1,12 @@ #!/usr/bin/env python3 import argparse -from collections.abc import Callable -from pathlib import Path -import sys -from helpers import changed_files, get_component_from_path, git_ls_files - -from esphome.const import ( - KEY_CORE, - KEY_TARGET_FRAMEWORK, - KEY_TARGET_PLATFORM, - PLATFORM_ESP32, - PLATFORM_ESP8266, +from helpers import ( + changed_files, + filter_component_files, + get_components_with_dependencies, + git_ls_files, ) -from esphome.core import CORE -from esphome.loader import ComponentManifest, get_component, get_platform - - -def filter_component_files(str): - return str.startswith("esphome/components/") | str.startswith("tests/components/") def get_all_component_files() -> list[str]: @@ -27,156 +15,6 @@ def get_all_component_files() -> list[str]: return list(filter(filter_component_files, files)) -def extract_component_names_array_from_files_array(files): - components = [] - for file in files: - component_name = get_component_from_path(file) - if component_name and component_name not in components: - components.append(component_name) - return components - - -def add_item_to_components_graph(components_graph, parent, child): - if not parent.startswith("__") and parent != child: - if parent not in components_graph: - components_graph[parent] = [] - if child not in components_graph[parent]: - components_graph[parent].append(child) - - -def resolve_auto_load( - auto_load: list[str] | Callable[[], list[str]] | Callable[[dict | None], list[str]], - config: dict | None = None, -) -> list[str]: - """Resolve AUTO_LOAD to a list, handling callables with or without config parameter. - - Args: - auto_load: The AUTO_LOAD value (list or callable) - config: Optional config to pass to callable AUTO_LOAD functions - - Returns: - List of component names to auto-load - """ - if not callable(auto_load): - return auto_load - - import inspect - - if inspect.signature(auto_load).parameters: - return auto_load(config) - return auto_load() - - -def create_components_graph(): - # The root directory of the repo - root = Path(__file__).parent.parent - components_dir = root / "esphome" / "components" - # Fake some directory so that get_component works - CORE.config_path = root - # Various configuration to capture different outcomes used by `AUTO_LOAD` function. - TARGET_CONFIGURATIONS = [ - {KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: None}, - {KEY_TARGET_FRAMEWORK: "arduino", KEY_TARGET_PLATFORM: None}, - {KEY_TARGET_FRAMEWORK: "esp-idf", KEY_TARGET_PLATFORM: None}, - {KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: PLATFORM_ESP32}, - {KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: PLATFORM_ESP8266}, - ] - CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] - - components_graph = {} - platforms = [] - components: list[tuple[ComponentManifest, str, Path]] = [] - - for path in components_dir.iterdir(): - if not path.is_dir(): - continue - if not (path / "__init__.py").is_file(): - continue - name = path.name - comp = get_component(name) - if comp is None: - print( - f"Cannot find component {name}. Make sure current path is pip installed ESPHome" - ) - sys.exit(1) - - components.append((comp, name, path)) - if comp.is_platform_component: - platforms.append(name) - - platforms = set(platforms) - - for comp, name, path in components: - for dependency in comp.dependencies: - add_item_to_components_graph( - components_graph, dependency.split(".")[0], name - ) - - for target_config in TARGET_CONFIGURATIONS: - CORE.data[KEY_CORE] = target_config - for item in resolve_auto_load(comp.auto_load, config=None): - add_item_to_components_graph(components_graph, item, name) - # restore config - CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] - - for platform_path in path.iterdir(): - platform_name = platform_path.stem - if platform_name == name or platform_name not in platforms: - continue - platform = get_platform(platform_name, name) - if platform is None: - continue - - add_item_to_components_graph(components_graph, platform_name, name) - - for dependency in platform.dependencies: - add_item_to_components_graph( - components_graph, dependency.split(".")[0], name - ) - - for target_config in TARGET_CONFIGURATIONS: - CORE.data[KEY_CORE] = target_config - for item in resolve_auto_load(platform.auto_load, config={}): - add_item_to_components_graph(components_graph, item, name) - # restore config - CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] - - return components_graph - - -def find_children_of_component(components_graph, component_name, depth=0): - if component_name not in components_graph: - return [] - - children = [] - - for child in components_graph[component_name]: - children.append(child) - if depth < 10: - children.extend( - find_children_of_component(components_graph, child, depth + 1) - ) - # Remove duplicate values - return list(set(children)) - - -def get_components(files: list[str], get_dependencies: bool = False): - components = extract_component_names_array_from_files_array(files) - - if get_dependencies: - components_graph = create_components_graph() - - all_components = components.copy() - for c in components: - all_components.extend(find_children_of_component(components_graph, c)) - # Remove duplicate values - all_changed_components = list(set(all_components)) - - return sorted(all_changed_components) - - return sorted(components) - - def main(): parser = argparse.ArgumentParser() parser.add_argument( @@ -251,8 +89,8 @@ def main(): # Return JSON with both directly changed and all changed components import json - directly_changed = get_components(files, False) - all_changed = get_components(files, True) + directly_changed = get_components_with_dependencies(files, False) + all_changed = get_components_with_dependencies(files, True) output = { "directly_changed": directly_changed, "all_changed": all_changed, @@ -260,11 +98,11 @@ def main(): print(json.dumps(output)) elif args.changed_direct: # Return only directly changed components (without dependencies) - for c in get_components(files, False): + for c in get_components_with_dependencies(files, False): print(c) else: # Return all changed components (with dependencies) - default behavior - for c in get_components(files, args.changed): + for c in get_components_with_dependencies(files, args.changed): print(c) diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 44aea73990..35652e0efc 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -96,17 +96,34 @@ def test_main_all_tests_should_run( mock_should_run_clang_format.return_value = True mock_should_run_python_linters.return_value = True - # Mock list-components.py output (now returns JSON with --changed-with-deps) - mock_result = Mock() - mock_result.stdout = json.dumps( - {"directly_changed": ["wifi", "api"], "all_changed": ["wifi", "api", "sensor"]} - ) - mock_subprocess_run.return_value = mock_result + # Mock changed_files to return non-component files (to avoid memory impact) + # Memory impact only runs when component C++ files change + mock_changed_files.return_value = [ + "esphome/config.py", + "esphome/helpers.py", + ] # Run main function with mocked argv with ( patch("sys.argv", ["determine-jobs.py"]), patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=False), + patch.object( + determine_jobs, + "get_changed_components", + return_value=["wifi", "api", "sensor"], + ), + patch.object( + determine_jobs, + "filter_component_files", + side_effect=lambda f: f.startswith("esphome/components/"), + ), + patch.object( + determine_jobs, + "get_components_with_dependencies", + side_effect=lambda files, deps: ["wifi", "api"] + if not deps + else ["wifi", "api", "sensor"], + ), ): determine_jobs.main() @@ -130,9 +147,9 @@ def test_main_all_tests_should_run( # changed_cpp_file_count should be present assert "changed_cpp_file_count" in output assert isinstance(output["changed_cpp_file_count"], int) - # memory_impact should be present + # memory_impact should be false (no component C++ files changed) assert "memory_impact" in output - assert output["memory_impact"]["should_run"] == "false" # No files changed + assert output["memory_impact"]["should_run"] == "false" def test_main_no_tests_should_run( @@ -154,13 +171,18 @@ def test_main_no_tests_should_run( mock_should_run_clang_format.return_value = False mock_should_run_python_linters.return_value = False - # Mock empty list-components.py output - mock_result = Mock() - mock_result.stdout = json.dumps({"directly_changed": [], "all_changed": []}) - mock_subprocess_run.return_value = mock_result + # Mock changed_files to return no component files + mock_changed_files.return_value = [] # Run main function with mocked argv - with patch("sys.argv", ["determine-jobs.py"]): + with ( + patch("sys.argv", ["determine-jobs.py"]), + patch.object(determine_jobs, "get_changed_components", return_value=[]), + patch.object(determine_jobs, "filter_component_files", return_value=False), + patch.object( + determine_jobs, "get_components_with_dependencies", return_value=[] + ), + ): determine_jobs.main() # Check output @@ -226,16 +248,22 @@ def test_main_with_branch_argument( mock_should_run_clang_format.return_value = False mock_should_run_python_linters.return_value = True - # Mock list-components.py output - mock_result = Mock() - mock_result.stdout = json.dumps( - {"directly_changed": ["mqtt"], "all_changed": ["mqtt"]} - ) - mock_subprocess_run.return_value = mock_result + # Mock changed_files to return non-component files (to avoid memory impact) + # Memory impact only runs when component C++ files change + mock_changed_files.return_value = ["esphome/config.py"] with ( patch("sys.argv", ["script.py", "-b", "main"]), patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=False), + patch.object(determine_jobs, "get_changed_components", return_value=["mqtt"]), + patch.object( + determine_jobs, + "filter_component_files", + side_effect=lambda f: f.startswith("esphome/components/"), + ), + patch.object( + determine_jobs, "get_components_with_dependencies", return_value=["mqtt"] + ), ): determine_jobs.main() @@ -245,13 +273,6 @@ def test_main_with_branch_argument( mock_should_run_clang_format.assert_called_once_with("main") mock_should_run_python_linters.assert_called_once_with("main") - # Check that list-components.py was called with branch - mock_subprocess_run.assert_called_once() - call_args = mock_subprocess_run.call_args[0][0] - assert "--changed-with-deps" in call_args - assert "-b" in call_args - assert "main" in call_args - # Check output captured = capsys.readouterr() output = json.loads(captured.out) @@ -272,7 +293,7 @@ def test_main_with_branch_argument( # changed_cpp_file_count should be present assert "changed_cpp_file_count" in output assert isinstance(output["changed_cpp_file_count"], int) - # memory_impact should be present + # memory_impact should be false (no component C++ files changed) assert "memory_impact" in output assert output["memory_impact"]["should_run"] == "false" @@ -500,16 +521,11 @@ def test_main_filters_components_without_tests( mock_should_run_clang_format.return_value = False mock_should_run_python_linters.return_value = False - # Mock list-components.py output with 3 components - # wifi: has tests, sensor: has tests, airthings_ble: no tests - mock_result = Mock() - mock_result.stdout = json.dumps( - { - "directly_changed": ["wifi", "sensor"], - "all_changed": ["wifi", "sensor", "airthings_ble"], - } - ) - mock_subprocess_run.return_value = mock_result + # Mock changed_files to return component files + mock_changed_files.return_value = [ + "esphome/components/wifi/wifi.cpp", + "esphome/components/sensor/sensor.h", + ] # Create test directory structure tests_dir = tmp_path / "tests" / "components" @@ -533,6 +549,23 @@ def test_main_filters_components_without_tests( patch.object(determine_jobs, "root_path", str(tmp_path)), patch.object(helpers, "root_path", str(tmp_path)), patch("sys.argv", ["determine-jobs.py"]), + patch.object( + determine_jobs, + "get_changed_components", + return_value=["wifi", "sensor", "airthings_ble"], + ), + patch.object( + determine_jobs, + "filter_component_files", + side_effect=lambda f: f.startswith("esphome/components/"), + ), + patch.object( + determine_jobs, + "get_components_with_dependencies", + side_effect=lambda files, deps: ["wifi", "sensor"] + if not deps + else ["wifi", "sensor", "airthings_ble"], + ), ): # Clear the cache since we're mocking root_path determine_jobs._component_has_tests.cache_clear() @@ -788,15 +821,18 @@ def test_clang_tidy_mode_full_scan( mock_should_run_clang_format.return_value = False mock_should_run_python_linters.return_value = False - # Mock list-components.py output - mock_result = Mock() - mock_result.stdout = json.dumps({"directly_changed": [], "all_changed": []}) - mock_subprocess_run.return_value = mock_result + # Mock changed_files to return no component files + mock_changed_files.return_value = [] # Mock full scan (hash changed) with ( patch("sys.argv", ["determine-jobs.py"]), patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=True), + patch.object(determine_jobs, "get_changed_components", return_value=[]), + patch.object(determine_jobs, "filter_component_files", return_value=False), + patch.object( + determine_jobs, "get_components_with_dependencies", return_value=[] + ), ): determine_jobs.main() @@ -853,12 +889,10 @@ def test_clang_tidy_mode_targeted_scan( # Create component names components = [f"comp{i}" for i in range(component_count)] - # Mock list-components.py output - mock_result = Mock() - mock_result.stdout = json.dumps( - {"directly_changed": components, "all_changed": components} - ) - mock_subprocess_run.return_value = mock_result + # Mock changed_files to return component files + mock_changed_files.return_value = [ + f"esphome/components/{comp}/file.cpp" for comp in components + ] # Mock git_ls_files to return files for each component cpp_files = { @@ -875,6 +909,15 @@ def test_clang_tidy_mode_targeted_scan( patch("sys.argv", ["determine-jobs.py"]), patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=False), patch.object(determine_jobs, "git_ls_files", side_effect=mock_git_ls_files), + patch.object(determine_jobs, "get_changed_components", return_value=components), + patch.object( + determine_jobs, + "filter_component_files", + side_effect=lambda f: f.startswith("esphome/components/"), + ), + patch.object( + determine_jobs, "get_components_with_dependencies", return_value=components + ), ): determine_jobs.main() From 66afe4a9be8fa379c8c33b2be7ffcfab6992eec9 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 21 Oct 2025 02:26:18 -0500 Subject: [PATCH 205/526] [climate] Add some integration tests (#11439) --- .../host_mode_climate_basic_state.yaml | 112 ++++++++++++++++++ .../fixtures/host_mode_climate_control.yaml | 108 +++++++++++++++++ .../fixtures/host_mode_many_entities.yaml | 36 +++++- .../test_host_mode_climate_basic_state.py | 49 ++++++++ .../test_host_mode_climate_control.py | 76 ++++++++++++ .../test_host_mode_many_entities.py | 43 +++++++ 6 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 tests/integration/fixtures/host_mode_climate_basic_state.yaml create mode 100644 tests/integration/fixtures/host_mode_climate_control.yaml create mode 100644 tests/integration/test_host_mode_climate_basic_state.py create mode 100644 tests/integration/test_host_mode_climate_control.py diff --git a/tests/integration/fixtures/host_mode_climate_basic_state.yaml b/tests/integration/fixtures/host_mode_climate_basic_state.yaml new file mode 100644 index 0000000000..f79d684fc6 --- /dev/null +++ b/tests/integration/fixtures/host_mode_climate_basic_state.yaml @@ -0,0 +1,112 @@ +esphome: + name: host-climate-test +host: +api: +logger: + +climate: + - platform: thermostat + id: dual_mode_thermostat + name: Dual-mode Thermostat + sensor: host_thermostat_temperature_sensor + humidity_sensor: host_thermostat_humidity_sensor + humidity_hysteresis: 1.0 + min_cooling_off_time: 20s + min_cooling_run_time: 20s + max_cooling_run_time: 30s + supplemental_cooling_delta: 3.0 + min_heating_off_time: 20s + min_heating_run_time: 20s + max_heating_run_time: 30s + supplemental_heating_delta: 3.0 + min_fanning_off_time: 20s + min_fanning_run_time: 20s + min_idle_time: 10s + visual: + min_humidity: 20% + max_humidity: 70% + min_temperature: 15.0 + max_temperature: 32.0 + temperature_step: 0.1 + default_preset: home + preset: + - name: "away" + default_target_temperature_low: 18.0 + default_target_temperature_high: 24.0 + - name: "home" + default_target_temperature_low: 18.0 + default_target_temperature_high: 24.0 + auto_mode: + - logger.log: "AUTO mode set" + heat_cool_mode: + - logger.log: "HEAT_COOL mode set" + cool_action: + - switch.turn_on: air_cond + supplemental_cooling_action: + - switch.turn_on: air_cond_2 + heat_action: + - switch.turn_on: heater + supplemental_heating_action: + - switch.turn_on: heater_2 + dry_action: + - switch.turn_on: air_cond + fan_only_action: + - switch.turn_on: fan_only + idle_action: + - switch.turn_off: air_cond + - switch.turn_off: air_cond_2 + - switch.turn_off: heater + - switch.turn_off: heater_2 + - switch.turn_off: fan_only + humidity_control_humidify_action: + - switch.turn_on: humidifier + humidity_control_off_action: + - switch.turn_off: humidifier + +sensor: + - platform: template + id: host_thermostat_humidity_sensor + unit_of_measurement: °C + accuracy_decimals: 2 + state_class: measurement + force_update: true + lambda: return 42.0; + update_interval: 0.1s + - platform: template + id: host_thermostat_temperature_sensor + unit_of_measurement: °C + accuracy_decimals: 2 + state_class: measurement + force_update: true + lambda: return 22.0; + update_interval: 0.1s + +switch: + - platform: template + id: air_cond + name: Air Conditioner + optimistic: true + - platform: template + id: air_cond_2 + name: Air Conditioner 2 + optimistic: true + - platform: template + id: fan_only + name: Fan + optimistic: true + - platform: template + id: heater + name: Heater + optimistic: true + - platform: template + id: heater_2 + name: Heater 2 + optimistic: true + - platform: template + id: dehumidifier + name: Dehumidifier + optimistic: true + - platform: template + id: humidifier + name: Humidifier + optimistic: true diff --git a/tests/integration/fixtures/host_mode_climate_control.yaml b/tests/integration/fixtures/host_mode_climate_control.yaml new file mode 100644 index 0000000000..c60e0597a2 --- /dev/null +++ b/tests/integration/fixtures/host_mode_climate_control.yaml @@ -0,0 +1,108 @@ +esphome: + name: host-climate-test +host: +api: +logger: + +climate: + - platform: thermostat + id: dual_mode_thermostat + name: Dual-mode Thermostat + sensor: host_thermostat_temperature_sensor + humidity_sensor: host_thermostat_humidity_sensor + humidity_hysteresis: 1.0 + min_cooling_off_time: 20s + min_cooling_run_time: 20s + max_cooling_run_time: 30s + supplemental_cooling_delta: 3.0 + min_heating_off_time: 20s + min_heating_run_time: 20s + max_heating_run_time: 30s + supplemental_heating_delta: 3.0 + min_fanning_off_time: 20s + min_fanning_run_time: 20s + min_idle_time: 10s + visual: + min_humidity: 20% + max_humidity: 70% + min_temperature: 15.0 + max_temperature: 32.0 + temperature_step: 0.1 + default_preset: home + preset: + - name: "away" + default_target_temperature_low: 18.0 + default_target_temperature_high: 24.0 + - name: "home" + default_target_temperature_low: 18.0 + default_target_temperature_high: 24.0 + auto_mode: + - logger.log: "AUTO mode set" + heat_cool_mode: + - logger.log: "HEAT_COOL mode set" + cool_action: + - switch.turn_on: air_cond + supplemental_cooling_action: + - switch.turn_on: air_cond_2 + heat_action: + - switch.turn_on: heater + supplemental_heating_action: + - switch.turn_on: heater_2 + dry_action: + - switch.turn_on: air_cond + fan_only_action: + - switch.turn_on: fan_only + idle_action: + - switch.turn_off: air_cond + - switch.turn_off: air_cond_2 + - switch.turn_off: heater + - switch.turn_off: heater_2 + - switch.turn_off: fan_only + humidity_control_humidify_action: + - switch.turn_on: humidifier + humidity_control_off_action: + - switch.turn_off: humidifier + +sensor: + - platform: template + id: host_thermostat_humidity_sensor + unit_of_measurement: °C + accuracy_decimals: 2 + state_class: measurement + force_update: true + lambda: return 42.0; + update_interval: 0.1s + - platform: template + id: host_thermostat_temperature_sensor + unit_of_measurement: °C + accuracy_decimals: 2 + state_class: measurement + force_update: true + lambda: return 22.0; + update_interval: 0.1s + +switch: + - platform: template + id: air_cond + name: Air Conditioner + optimistic: true + - platform: template + id: air_cond_2 + name: Air Conditioner 2 + optimistic: true + - platform: template + id: fan_only + name: Fan + optimistic: true + - platform: template + id: heater + name: Heater + optimistic: true + - platform: template + id: heater_2 + name: Heater 2 + optimistic: true + - platform: template + id: humidifier + name: Humidifier + optimistic: true diff --git a/tests/integration/fixtures/host_mode_many_entities.yaml b/tests/integration/fixtures/host_mode_many_entities.yaml index 612186507c..acb03f235b 100644 --- a/tests/integration/fixtures/host_mode_many_entities.yaml +++ b/tests/integration/fixtures/host_mode_many_entities.yaml @@ -210,7 +210,15 @@ sensor: name: "Test Sensor 50" lambda: return 50.0; update_interval: 0.1s - # Temperature sensor for the thermostat + # Sensors for the thermostat + - platform: template + name: "Humidity Sensor" + id: humidity_sensor + lambda: return 35.0; + unit_of_measurement: "%" + device_class: humidity + state_class: measurement + update_interval: 5s - platform: template name: "Temperature Sensor" id: temp_sensor @@ -295,6 +303,11 @@ valve: - logger.log: "Valve stopping" output: + - platform: template + id: humidifier_output + type: binary + write_action: + - logger.log: "Humidifier output changed" - platform: template id: heater_output type: binary @@ -305,18 +318,31 @@ output: type: binary write_action: - logger.log: "Cooler output changed" + - platform: template + id: fan_output + type: binary + write_action: + - logger.log: "Fan output changed" climate: - platform: thermostat name: "Test Thermostat" sensor: temp_sensor + humidity_sensor: humidity_sensor default_preset: Home on_boot_restore_from: default_preset min_heating_off_time: 1s min_heating_run_time: 1s min_cooling_off_time: 1s min_cooling_run_time: 1s + min_fan_mode_switching_time: 1s min_idle_time: 1s + visual: + min_humidity: 20% + max_humidity: 70% + min_temperature: 15.0 + max_temperature: 32.0 + temperature_step: 0.1 heat_action: - output.turn_on: heater_output cool_action: @@ -324,6 +350,14 @@ climate: idle_action: - output.turn_off: heater_output - output.turn_off: cooler_output + humidity_control_humidify_action: + - output.turn_on: humidifier_output + humidity_control_off_action: + - output.turn_off: humidifier_output + fan_mode_auto_action: + - output.turn_off: fan_output + fan_mode_on_action: + - output.turn_on: fan_output preset: - name: Home default_target_temperature_low: 20 diff --git a/tests/integration/test_host_mode_climate_basic_state.py b/tests/integration/test_host_mode_climate_basic_state.py new file mode 100644 index 0000000000..4697342a99 --- /dev/null +++ b/tests/integration/test_host_mode_climate_basic_state.py @@ -0,0 +1,49 @@ +"""Integration test for Host mode with climate.""" + +from __future__ import annotations + +import asyncio + +import aioesphomeapi +from aioesphomeapi import ClimateAction, ClimateMode, ClimatePreset, EntityState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_climate_basic_state( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test basic climate state reporting.""" + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + states: dict[int, EntityState] = {} + climate_future: asyncio.Future[EntityState] = loop.create_future() + + def on_state(state: EntityState) -> None: + states[state.key] = state + if ( + isinstance(state, aioesphomeapi.ClimateState) + and not climate_future.done() + ): + climate_future.set_result(state) + + client.subscribe_states(on_state) + + try: + climate_state = await asyncio.wait_for(climate_future, timeout=5.0) + except TimeoutError: + pytest.fail("Climate state not received within 5 seconds") + + assert isinstance(climate_state, aioesphomeapi.ClimateState) + assert climate_state.mode == ClimateMode.OFF + assert climate_state.action == ClimateAction.OFF + assert climate_state.current_temperature == 22.0 + assert climate_state.target_temperature_low == 18.0 + assert climate_state.target_temperature_high == 24.0 + assert climate_state.preset == ClimatePreset.HOME + assert climate_state.current_humidity == 42.0 + assert climate_state.target_humidity == 20.0 diff --git a/tests/integration/test_host_mode_climate_control.py b/tests/integration/test_host_mode_climate_control.py new file mode 100644 index 0000000000..96d15dfae0 --- /dev/null +++ b/tests/integration/test_host_mode_climate_control.py @@ -0,0 +1,76 @@ +"""Integration test for Host mode with climate.""" + +from __future__ import annotations + +import asyncio + +import aioesphomeapi +from aioesphomeapi import ClimateInfo, ClimateMode, EntityState +import pytest + +from .state_utils import InitialStateHelper +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_climate_control( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test climate mode control.""" + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + states: dict[int, EntityState] = {} + climate_future: asyncio.Future[EntityState] = loop.create_future() + + def on_state(state: EntityState) -> None: + states[state.key] = state + if ( + isinstance(state, aioesphomeapi.ClimateState) + and state.mode == ClimateMode.HEAT + and state.target_temperature_low == 21.5 + and state.target_temperature_high == 26.5 + and not climate_future.done() + ): + climate_future.set_result(state) + + # Get entities and set up state synchronization + entities, services = await client.list_entities_services() + initial_state_helper = InitialStateHelper(entities) + climate_infos = [e for e in entities if isinstance(e, ClimateInfo)] + assert len(climate_infos) >= 1, "Expected at least 1 climate entity" + + # Subscribe with the wrapper that filters initial states + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + + # Wait for all initial states to be broadcast + try: + await initial_state_helper.wait_for_initial_states() + except TimeoutError: + pytest.fail("Timeout waiting for initial states") + + test_climate = next( + (c for c in climate_infos if c.name == "Dual-mode Thermostat"), None + ) + assert test_climate is not None, ( + "Dual-mode Thermostat thermostat climate not found" + ) + + # Adjust setpoints + client.climate_command( + test_climate.key, + mode=ClimateMode.HEAT, + target_temperature_low=21.5, + target_temperature_high=26.5, + ) + + try: + climate_state = await asyncio.wait_for(climate_future, timeout=5.0) + except TimeoutError: + pytest.fail("Climate state not received within 5 seconds") + + assert isinstance(climate_state, aioesphomeapi.ClimateState) + assert climate_state.mode == ClimateMode.HEAT + assert climate_state.target_temperature_low == 21.5 + assert climate_state.target_temperature_high == 26.5 diff --git a/tests/integration/test_host_mode_many_entities.py b/tests/integration/test_host_mode_many_entities.py index fbe3dc25c8..299644d496 100644 --- a/tests/integration/test_host_mode_many_entities.py +++ b/tests/integration/test_host_mode_many_entities.py @@ -5,7 +5,10 @@ from __future__ import annotations import asyncio from aioesphomeapi import ( + ClimateFanMode, + ClimateFeature, ClimateInfo, + ClimateMode, DateInfo, DateState, DateTimeInfo, @@ -121,6 +124,46 @@ async def test_host_mode_many_entities( assert len(climate_infos) >= 1, "Expected at least 1 climate entity" climate_info = climate_infos[0] + + # Verify feature flags set as expected + assert climate_info.feature_flags == ( + ClimateFeature.SUPPORTS_ACTION + | ClimateFeature.SUPPORTS_CURRENT_HUMIDITY + | ClimateFeature.SUPPORTS_CURRENT_TEMPERATURE + | ClimateFeature.SUPPORTS_TWO_POINT_TARGET_TEMPERATURE + | ClimateFeature.SUPPORTS_TARGET_HUMIDITY + ) + + # Verify modes + assert climate_info.supported_modes == [ + ClimateMode.OFF, + ClimateMode.COOL, + ClimateMode.HEAT, + ], f"Expected modes [OFF, COOL, HEAT], got {climate_info.supported_modes}" + + # Verify visual parameters + assert climate_info.visual_min_temperature == 15.0, ( + f"Expected min_temperature=15.0, got {climate_info.visual_min_temperature}" + ) + assert climate_info.visual_max_temperature == 32.0, ( + f"Expected max_temperature=32.0, got {climate_info.visual_max_temperature}" + ) + assert climate_info.visual_target_temperature_step == 0.1, ( + f"Expected temperature_step=0.1, got {climate_info.visual_target_temperature_step}" + ) + assert climate_info.visual_min_humidity == 20.0, ( + f"Expected min_humidity=20.0, got {climate_info.visual_min_humidity}" + ) + assert climate_info.visual_max_humidity == 70.0, ( + f"Expected max_humidity=70.0, got {climate_info.visual_max_humidity}" + ) + + # Verify fan modes + assert climate_info.supported_fan_modes == [ + ClimateFanMode.ON, + ClimateFanMode.AUTO, + ], f"Expected fan modes [ON, AUTO], got {climate_info.supported_fan_modes}" + # Verify the thermostat has presets assert len(climate_info.supported_presets) > 0, ( "Expected climate to have presets" From a5542e0d2bf7e10813bd43ca386fbd3c680ef358 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 21:38:05 -1000 Subject: [PATCH 206/526] [sensor] Optimize calibration and Or filters with FixedVector (#11437) --- esphome/components/sensor/filter.cpp | 16 +++++++++++----- esphome/components/sensor/filter.h | 13 ++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index 0d57c792db..e8d04d161b 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -313,7 +313,7 @@ optional DeltaFilter::new_value(float value) { } // OrFilter -OrFilter::OrFilter(std::vector filters) : filters_(std::move(filters)), phi_(this) {} +OrFilter::OrFilter(std::initializer_list filters) : filters_(filters), phi_(this) {} OrFilter::PhiNode::PhiNode(OrFilter *or_parent) : or_parent_(or_parent) {} optional OrFilter::PhiNode::new_value(float value) { @@ -326,14 +326,14 @@ optional OrFilter::PhiNode::new_value(float value) { } optional OrFilter::new_value(float value) { this->has_value_ = false; - for (Filter *filter : this->filters_) + for (auto *filter : this->filters_) filter->input(value); return {}; } void OrFilter::initialize(Sensor *parent, Filter *next) { Filter::initialize(parent, next); - for (Filter *filter : this->filters_) { + for (auto *filter : this->filters_) { filter->initialize(parent, &this->phi_); } this->phi_.initialize(parent, nullptr); @@ -386,18 +386,24 @@ void HeartbeatFilter::setup() { } float HeartbeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; } +CalibrateLinearFilter::CalibrateLinearFilter(std::initializer_list> linear_functions) + : linear_functions_(linear_functions) {} + optional CalibrateLinearFilter::new_value(float value) { - for (std::array f : this->linear_functions_) { + for (const auto &f : this->linear_functions_) { if (!std::isfinite(f[2]) || value < f[2]) return (value * f[0]) + f[1]; } return NAN; } +CalibratePolynomialFilter::CalibratePolynomialFilter(std::initializer_list coefficients) + : coefficients_(coefficients) {} + optional CalibratePolynomialFilter::new_value(float value) { float res = 0.0f; float x = 1.0f; - for (float coefficient : this->coefficients_) { + for (const auto &coefficient : this->coefficients_) { res += x * coefficient; x *= value; } diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index e09c66afcb..03a1e0f24c 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -422,7 +422,7 @@ class DeltaFilter : public Filter { class OrFilter : public Filter { public: - explicit OrFilter(std::vector filters); + explicit OrFilter(std::initializer_list filters); void initialize(Sensor *parent, Filter *next) override; @@ -438,28 +438,27 @@ class OrFilter : public Filter { OrFilter *or_parent_; }; - std::vector filters_; + FixedVector filters_; PhiNode phi_; bool has_value_{false}; }; class CalibrateLinearFilter : public Filter { public: - CalibrateLinearFilter(std::vector> linear_functions) - : linear_functions_(std::move(linear_functions)) {} + explicit CalibrateLinearFilter(std::initializer_list> linear_functions); optional new_value(float value) override; protected: - std::vector> linear_functions_; + FixedVector> linear_functions_; }; class CalibratePolynomialFilter : public Filter { public: - CalibratePolynomialFilter(std::vector coefficients) : coefficients_(std::move(coefficients)) {} + explicit CalibratePolynomialFilter(std::initializer_list coefficients); optional new_value(float value) override; protected: - std::vector coefficients_; + FixedVector coefficients_; }; class ClampFilter : public Filter { From 7f2cc47ed6c4fa7ef531da3d80066a4829e37430 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 23:25:59 -1000 Subject: [PATCH 207/526] [binary_sensor] Add compile test for auto repeat (#11443) --- tests/components/binary_sensor/common.yaml | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/components/binary_sensor/common.yaml b/tests/components/binary_sensor/common.yaml index ed6322768f..6965c1feeb 100644 --- a/tests/components/binary_sensor/common.yaml +++ b/tests/components/binary_sensor/common.yaml @@ -37,3 +37,36 @@ binary_sensor: format: "New state is %s" args: ['x.has_value() ? ONOFF(x) : "Unknown"'] - binary_sensor.invalidate_state: some_binary_sensor + + # Test autorepeat with default configuration (no timings) + - platform: template + id: autorepeat_default + name: "Autorepeat Default" + filters: + - autorepeat: + + # Test autorepeat with single timing entry + - platform: template + id: autorepeat_single + name: "Autorepeat Single" + filters: + - autorepeat: + - delay: 2s + time_off: 200ms + time_on: 800ms + + # Test autorepeat with three timing entries + - platform: template + id: autorepeat_multiple + name: "Autorepeat Multiple" + filters: + - autorepeat: + - delay: 500ms + time_off: 50ms + time_on: 950ms + - delay: 2s + time_off: 100ms + time_on: 900ms + - delay: 10s + time_off: 200ms + time_on: 800ms From 3aedfe8be34d7fd7758d96fdc6e6ee95065fc705 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 23:30:13 -1000 Subject: [PATCH 208/526] [binary_sensor] Optimize AutorepeatFilter with FixedVector (#11444) --- esphome/components/binary_sensor/__init__.py | 35 +++++++++++++------- esphome/components/binary_sensor/filter.cpp | 3 +- esphome/components/binary_sensor/filter.h | 11 ++---- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 6aa97d6e05..26e784a0b8 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -264,20 +264,31 @@ async def delayed_off_filter_to_code(config, filter_id): ), ) async def autorepeat_filter_to_code(config, filter_id): - timings = [] if len(config) > 0: - timings.extend( - (conf[CONF_DELAY], conf[CONF_TIME_OFF], conf[CONF_TIME_ON]) - for conf in config - ) - else: - timings.append( - ( - cv.time_period_str_unit(DEFAULT_DELAY).total_milliseconds, - cv.time_period_str_unit(DEFAULT_TIME_OFF).total_milliseconds, - cv.time_period_str_unit(DEFAULT_TIME_ON).total_milliseconds, + timings = [ + cg.StructInitializer( + cg.MockObj("AutorepeatFilterTiming", "esphome::binary_sensor::"), + ("delay", conf[CONF_DELAY]), + ("time_off", conf[CONF_TIME_OFF]), + ("time_on", conf[CONF_TIME_ON]), ) - ) + for conf in config + ] + else: + timings = [ + cg.StructInitializer( + cg.MockObj("AutorepeatFilterTiming", "esphome::binary_sensor::"), + ("delay", cv.time_period_str_unit(DEFAULT_DELAY).total_milliseconds), + ( + "time_off", + cv.time_period_str_unit(DEFAULT_TIME_OFF).total_milliseconds, + ), + ( + "time_on", + cv.time_period_str_unit(DEFAULT_TIME_ON).total_milliseconds, + ), + ) + ] var = cg.new_Pvariable(filter_id, timings) await cg.register_component(var, {}) return var diff --git a/esphome/components/binary_sensor/filter.cpp b/esphome/components/binary_sensor/filter.cpp index 3567e9c72b..8f31cf6fc2 100644 --- a/esphome/components/binary_sensor/filter.cpp +++ b/esphome/components/binary_sensor/filter.cpp @@ -1,7 +1,6 @@ #include "filter.h" #include "binary_sensor.h" -#include namespace esphome { @@ -68,7 +67,7 @@ float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARD optional InvertFilter::new_value(bool value) { return !value; } -AutorepeatFilter::AutorepeatFilter(std::vector timings) : timings_(std::move(timings)) {} +AutorepeatFilter::AutorepeatFilter(std::initializer_list timings) : timings_(timings) {} optional AutorepeatFilter::new_value(bool value) { if (value) { diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index 16f44aa5fe..a7eb080feb 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -4,8 +4,6 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include - namespace esphome { namespace binary_sensor { @@ -82,11 +80,6 @@ class InvertFilter : public Filter { }; struct AutorepeatFilterTiming { - AutorepeatFilterTiming(uint32_t delay, uint32_t off, uint32_t on) { - this->delay = delay; - this->time_off = off; - this->time_on = on; - } uint32_t delay; uint32_t time_off; uint32_t time_on; @@ -94,7 +87,7 @@ struct AutorepeatFilterTiming { class AutorepeatFilter : public Filter, public Component { public: - explicit AutorepeatFilter(std::vector timings); + explicit AutorepeatFilter(std::initializer_list timings); optional new_value(bool value) override; @@ -104,7 +97,7 @@ class AutorepeatFilter : public Filter, public Component { void next_timing_(); void next_value_(bool val); - std::vector timings_; + FixedVector timings_; uint8_t active_timing_{0}; }; From 87e9a7a1bd8923b2ab0c36b0f8fb1cbc6ae01308 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 20 Oct 2025 23:35:18 -1000 Subject: [PATCH 209/526] [climate] Remove unnecessary vector allocations in state save/restore (#11445) --- esphome/components/climate/climate.cpp | 44 ++++++++++++++++++-------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 24a3fe6d5a..87d03f78c5 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -385,12 +385,14 @@ void Climate::save_state_() { if (!traits.get_supported_custom_fan_modes().empty() && custom_fan_mode.has_value()) { state.uses_custom_fan_mode = true; const auto &supported = traits.get_supported_custom_fan_modes(); - std::vector vec{supported.begin(), supported.end()}; - for (size_t i = 0; i < vec.size(); i++) { - if (vec[i] == custom_fan_mode) { + // std::set has consistent order (lexicographic for strings) + size_t i = 0; + for (const auto &mode : supported) { + if (mode == custom_fan_mode) { state.custom_fan_mode = i; break; } + i++; } } if (traits.get_supports_presets() && preset.has_value()) { @@ -400,12 +402,14 @@ void Climate::save_state_() { if (!traits.get_supported_custom_presets().empty() && custom_preset.has_value()) { state.uses_custom_preset = true; const auto &supported = traits.get_supported_custom_presets(); - std::vector vec{supported.begin(), supported.end()}; - for (size_t i = 0; i < vec.size(); i++) { - if (vec[i] == custom_preset) { + // std::set has consistent order (lexicographic for strings) + size_t i = 0; + for (const auto &preset : supported) { + if (preset == custom_preset) { state.custom_preset = i; break; } + i++; } } if (traits.get_supports_swing_modes()) { @@ -549,22 +553,34 @@ void ClimateDeviceRestoreState::apply(Climate *climate) { climate->fan_mode = this->fan_mode; } if (!traits.get_supported_custom_fan_modes().empty() && this->uses_custom_fan_mode) { - // std::set has consistent order (lexicographic for strings), so this is ok + // std::set has consistent order (lexicographic for strings) const auto &modes = traits.get_supported_custom_fan_modes(); - std::vector modes_vec{modes.begin(), modes.end()}; - if (custom_fan_mode < modes_vec.size()) { - climate->custom_fan_mode = modes_vec[this->custom_fan_mode]; + if (custom_fan_mode < modes.size()) { + size_t i = 0; + for (const auto &mode : modes) { + if (i == this->custom_fan_mode) { + climate->custom_fan_mode = mode; + break; + } + i++; + } } } if (traits.get_supports_presets() && !this->uses_custom_preset) { climate->preset = this->preset; } if (!traits.get_supported_custom_presets().empty() && uses_custom_preset) { - // std::set has consistent order (lexicographic for strings), so this is ok + // std::set has consistent order (lexicographic for strings) const auto &presets = traits.get_supported_custom_presets(); - std::vector presets_vec{presets.begin(), presets.end()}; - if (custom_preset < presets_vec.size()) { - climate->custom_preset = presets_vec[this->custom_preset]; + if (custom_preset < presets.size()) { + size_t i = 0; + for (const auto &preset : presets) { + if (i == this->custom_preset) { + climate->custom_preset = preset; + break; + } + i++; + } } } if (traits.get_supports_swing_modes()) { From 80265a6bd2ad9208b3fa0b0ee8137495b4aeb7f0 Mon Sep 17 00:00:00 2001 From: Petr Kejval Date: Tue, 21 Oct 2025 15:17:07 +0200 Subject: [PATCH 210/526] [sensor] Add optimistic option to heartbeat filter (#10993) Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Co-authored-by: J. Nick Koston --- esphome/components/sensor/__init__.py | 23 +++++++++++++++++++++- esphome/components/sensor/filter.cpp | 5 +++++ esphome/components/sensor/filter.h | 5 +++-- tests/components/template/common-base.yaml | 3 +++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index e603896f6d..7e91bb83c4 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -28,6 +28,8 @@ from esphome.const import ( CONF_ON_RAW_VALUE, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, + CONF_OPTIMISTIC, + CONF_PERIOD, CONF_QUANTILE, CONF_SEND_EVERY, CONF_SEND_FIRST_AT, @@ -644,10 +646,29 @@ async def throttle_with_priority_filter_to_code(config, filter_id): return cg.new_Pvariable(filter_id, config[CONF_TIMEOUT], template_) +HEARTBEAT_SCHEMA = cv.Schema( + { + cv.Required(CONF_PERIOD): cv.positive_time_period_milliseconds, + cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, + } +) + + @FILTER_REGISTRY.register( - "heartbeat", HeartbeatFilter, cv.positive_time_period_milliseconds + "heartbeat", + HeartbeatFilter, + cv.Any( + cv.positive_time_period_milliseconds, + HEARTBEAT_SCHEMA, + ), ) async def heartbeat_filter_to_code(config, filter_id): + if isinstance(config, dict): + var = cg.new_Pvariable(filter_id, config[CONF_PERIOD]) + await cg.register_component(var, {}) + cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) + return var + var = cg.new_Pvariable(filter_id, config) await cg.register_component(var, {}) return var diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index e8d04d161b..65d8dea31c 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -372,8 +372,12 @@ optional HeartbeatFilter::new_value(float value) { this->last_input_ = value; this->has_value_ = true; + if (this->optimistic_) { + return value; + } return {}; } + void HeartbeatFilter::setup() { this->set_interval("heartbeat", this->time_period_, [this]() { ESP_LOGVV(TAG, "HeartbeatFilter(%p)::interval(has_value=%s, last_input=%f)", this, YESNO(this->has_value_), @@ -384,6 +388,7 @@ void HeartbeatFilter::setup() { this->output(this->last_input_); }); } + float HeartbeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; } CalibrateLinearFilter::CalibrateLinearFilter(std::initializer_list> linear_functions) diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 03a1e0f24c..ecd55308d1 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -396,15 +396,16 @@ class HeartbeatFilter : public Filter, public Component { explicit HeartbeatFilter(uint32_t time_period); void setup() override; - optional new_value(float value) override; - float get_setup_priority() const override; + void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } + protected: uint32_t time_period_; float last_input_; bool has_value_{false}; + bool optimistic_{false}; }; class DeltaFilter : public Filter { diff --git a/tests/components/template/common-base.yaml b/tests/components/template/common-base.yaml index ea812532d4..b873af5207 100644 --- a/tests/components/template/common-base.yaml +++ b/tests/components/template/common-base.yaml @@ -101,6 +101,9 @@ sensor: - filter_out: 10 - filter_out: !lambda return NAN; - heartbeat: 5s + - heartbeat: + period: 5s + optimistic: true - lambda: return x * (9.0/5.0) + 32.0; - max: window_size: 10 From 8e8a2bde95e306fd029e3c40abc2547c11a34a5b Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 21 Oct 2025 13:37:29 -0700 Subject: [PATCH 211/526] [light] Decouple AddressableLight and Light transition classes (#11166) Co-authored-by: J. Nick Koston --- esphome/components/light/addressable_light.cpp | 2 +- esphome/components/light/addressable_light.h | 4 ++-- esphome/components/light/light_transformer.h | 4 ++++ esphome/components/light/transformers.h | 6 +----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/esphome/components/light/addressable_light.cpp b/esphome/components/light/addressable_light.cpp index a8e0c7b762..cd83015ecb 100644 --- a/esphome/components/light/addressable_light.cpp +++ b/esphome/components/light/addressable_light.cpp @@ -62,7 +62,7 @@ void AddressableLightTransformer::start() { } optional AddressableLightTransformer::apply() { - float smoothed_progress = LightTransitionTransformer::smoothed_progress(this->get_progress_()); + float smoothed_progress = LightTransformer::smoothed_progress(this->get_progress_()); // When running an output-buffer modifying effect, don't try to transition individual LEDs, but instead just fade the // LightColorValues. write_state() then picks up the change in brightness, and the color change is picked up by the diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index 3e94a39745..c8ed4897fa 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -8,7 +8,7 @@ #include "esphome/core/defines.h" #include "light_output.h" #include "light_state.h" -#include "transformers.h" +#include "light_transformer.h" #ifdef USE_POWER_SUPPLY #include "esphome/components/power_supply/power_supply.h" @@ -103,7 +103,7 @@ class AddressableLight : public LightOutput, public Component { bool effect_active_{false}; }; -class AddressableLightTransformer : public LightTransitionTransformer { +class AddressableLightTransformer : public LightTransformer { public: AddressableLightTransformer(AddressableLight &light) : light_(light) {} diff --git a/esphome/components/light/light_transformer.h b/esphome/components/light/light_transformer.h index fb9b709187..a84183c03c 100644 --- a/esphome/components/light/light_transformer.h +++ b/esphome/components/light/light_transformer.h @@ -38,6 +38,10 @@ class LightTransformer { const LightColorValues &get_target_values() const { return this->target_values_; } protected: + // This looks crazy, but it reduces to 6x^5 - 15x^4 + 10x^3 which is just a smooth sigmoid-like + // transition from 0 to 1 on x = [0, 1] + static float smoothed_progress(float x) { return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); } + /// The progress of this transition, on a scale of 0 to 1. float get_progress_() { uint32_t now = esphome::millis(); diff --git a/esphome/components/light/transformers.h b/esphome/components/light/transformers.h index 8d49acff97..71d41a66d3 100644 --- a/esphome/components/light/transformers.h +++ b/esphome/components/light/transformers.h @@ -50,15 +50,11 @@ class LightTransitionTransformer : public LightTransformer { if (this->changing_color_mode_) p = p < 0.5f ? p * 2 : (p - 0.5) * 2; - float v = LightTransitionTransformer::smoothed_progress(p); + float v = LightTransformer::smoothed_progress(p); return LightColorValues::lerp(start, end, v); } protected: - // This looks crazy, but it reduces to 6x^5 - 15x^4 + 10x^3 which is just a smooth sigmoid-like - // transition from 0 to 1 on x = [0, 1] - static float smoothed_progress(float x) { return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); } - LightColorValues end_values_{}; LightColorValues intermediate_values_{}; bool changing_color_mode_{false}; From a05c5ea24016d4f7680f5ba632c00a1140d30b43 Mon Sep 17 00:00:00 2001 From: Anton Sergunov Date: Wed, 22 Oct 2025 03:10:25 +0600 Subject: [PATCH 212/526] [uart] Make rx pin respect pullup and pulldown settings (#9248) --- esphome/components/uart/uart_component_esp8266.cpp | 7 +++++++ esphome/components/uart/uart_component_esp_idf.cpp | 10 ++++++++++ esphome/components/uart/uart_component_libretiny.cpp | 7 +++++++ esphome/components/uart/uart_component_rp2040.cpp | 7 +++++++ 4 files changed, 31 insertions(+) diff --git a/esphome/components/uart/uart_component_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp index b2bf2bacf1..7a453dbb50 100644 --- a/esphome/components/uart/uart_component_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -56,6 +56,13 @@ uint32_t ESP8266UartComponent::get_config() { } void ESP8266UartComponent::setup() { + if (this->rx_pin_) { + this->rx_pin_->setup(); + } + if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) { + this->tx_pin_->setup(); + } + // Use Arduino HardwareSerial UARTs if all used pins match the ones // preconfigured by the platform. For example if RX disabled but TX pin // is 1 we still want to use Serial. diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 7530856b1e..cffa3308eb 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -6,6 +6,9 @@ #include "esphome/core/defines.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/gpio.h" +#include "driver/gpio.h" +#include "soc/gpio_num.h" #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" @@ -104,6 +107,13 @@ void IDFUARTComponent::load_settings(bool dump_config) { return; } + if (this->rx_pin_) { + this->rx_pin_->setup(); + } + if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) { + this->tx_pin_->setup(); + } + int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1; int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1; int8_t flow_control = this->flow_control_pin_ != nullptr ? this->flow_control_pin_->get_pin() : -1; diff --git a/esphome/components/uart/uart_component_libretiny.cpp b/esphome/components/uart/uart_component_libretiny.cpp index 8a7a301cfe..9c065fe5df 100644 --- a/esphome/components/uart/uart_component_libretiny.cpp +++ b/esphome/components/uart/uart_component_libretiny.cpp @@ -46,6 +46,13 @@ uint16_t LibreTinyUARTComponent::get_config() { } void LibreTinyUARTComponent::setup() { + if (this->rx_pin_) { + this->rx_pin_->setup(); + } + if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) { + this->tx_pin_->setup(); + } + int8_t tx_pin = tx_pin_ == nullptr ? -1 : tx_pin_->get_pin(); int8_t rx_pin = rx_pin_ == nullptr ? -1 : rx_pin_->get_pin(); bool tx_inverted = tx_pin_ != nullptr && tx_pin_->is_inverted(); diff --git a/esphome/components/uart/uart_component_rp2040.cpp b/esphome/components/uart/uart_component_rp2040.cpp index ae3042fb77..c78691653d 100644 --- a/esphome/components/uart/uart_component_rp2040.cpp +++ b/esphome/components/uart/uart_component_rp2040.cpp @@ -52,6 +52,13 @@ uint16_t RP2040UartComponent::get_config() { } void RP2040UartComponent::setup() { + if (this->rx_pin_) { + this->rx_pin_->setup(); + } + if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) { + this->tx_pin_->setup(); + } + uint16_t config = get_config(); constexpr uint32_t valid_tx_uart_0 = __bitset({0, 12, 16, 28}); From 548913b471a7cdd4ca412ff29807fd4c012308d9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 11:12:32 -1000 Subject: [PATCH 213/526] Add gpio switch interlock compile tests (#11449) --- tests/components/gpio/common.yaml | 17 +++++++++++++++++ tests/components/gpio/test.esp32-c3-idf.yaml | 3 +++ tests/components/gpio/test.esp32-idf.yaml | 3 +++ tests/components/gpio/test.esp8266-ard.yaml | 3 +++ tests/components/gpio/test.nrf52-adafruit.yaml | 17 +++++++++++++++++ tests/components/gpio/test.nrf52-mcumgr.yaml | 17 +++++++++++++++++ tests/components/gpio/test.rp2040-ard.yaml | 3 +++ 7 files changed, 63 insertions(+) diff --git a/tests/components/gpio/common.yaml b/tests/components/gpio/common.yaml index 4e237349d9..b8e8fa81e4 100644 --- a/tests/components/gpio/common.yaml +++ b/tests/components/gpio/common.yaml @@ -12,3 +12,20 @@ switch: - platform: gpio pin: ${switch_pin} id: gpio_switch + + - platform: gpio + pin: ${switch_pin_2} + id: gpio_switch_interlock_1 + interlock: [gpio_switch_interlock_2, gpio_switch_interlock_3] + interlock_wait_time: 100ms + + - platform: gpio + pin: ${switch_pin_3} + id: gpio_switch_interlock_2 + interlock: [gpio_switch_interlock_1, gpio_switch_interlock_3] + + - platform: gpio + pin: ${switch_pin_4} + id: gpio_switch_interlock_3 + interlock: [gpio_switch_interlock_1, gpio_switch_interlock_2] + interlock_wait_time: 50ms diff --git a/tests/components/gpio/test.esp32-c3-idf.yaml b/tests/components/gpio/test.esp32-c3-idf.yaml index fc7c9942d0..e9071b4356 100644 --- a/tests/components/gpio/test.esp32-c3-idf.yaml +++ b/tests/components/gpio/test.esp32-c3-idf.yaml @@ -2,5 +2,8 @@ substitutions: binary_sensor_pin: GPIO2 output_pin: GPIO3 switch_pin: GPIO4 + switch_pin_2: GPIO5 + switch_pin_3: GPIO6 + switch_pin_4: GPIO7 <<: !include common.yaml diff --git a/tests/components/gpio/test.esp32-idf.yaml b/tests/components/gpio/test.esp32-idf.yaml index 09f41abb79..862aa533ea 100644 --- a/tests/components/gpio/test.esp32-idf.yaml +++ b/tests/components/gpio/test.esp32-idf.yaml @@ -2,5 +2,8 @@ substitutions: binary_sensor_pin: GPIO12 output_pin: GPIO13 switch_pin: GPIO14 + switch_pin_2: GPIO15 + switch_pin_3: GPIO16 + switch_pin_4: GPIO17 <<: !include common.yaml diff --git a/tests/components/gpio/test.esp8266-ard.yaml b/tests/components/gpio/test.esp8266-ard.yaml index e1660ec47c..e13b4520d1 100644 --- a/tests/components/gpio/test.esp8266-ard.yaml +++ b/tests/components/gpio/test.esp8266-ard.yaml @@ -2,5 +2,8 @@ substitutions: binary_sensor_pin: GPIO0 output_pin: GPIO2 switch_pin: GPIO15 + switch_pin_2: GPIO12 + switch_pin_3: GPIO13 + switch_pin_4: GPIO14 <<: !include common.yaml diff --git a/tests/components/gpio/test.nrf52-adafruit.yaml b/tests/components/gpio/test.nrf52-adafruit.yaml index 912b9537c4..fb3f368e03 100644 --- a/tests/components/gpio/test.nrf52-adafruit.yaml +++ b/tests/components/gpio/test.nrf52-adafruit.yaml @@ -12,3 +12,20 @@ switch: - platform: gpio pin: P1.2 id: gpio_switch + + - platform: gpio + pin: P1.3 + id: gpio_switch_interlock_1 + interlock: [gpio_switch_interlock_2, gpio_switch_interlock_3] + interlock_wait_time: 100ms + + - platform: gpio + pin: P1.4 + id: gpio_switch_interlock_2 + interlock: [gpio_switch_interlock_1, gpio_switch_interlock_3] + + - platform: gpio + pin: P1.5 + id: gpio_switch_interlock_3 + interlock: [gpio_switch_interlock_1, gpio_switch_interlock_2] + interlock_wait_time: 50ms diff --git a/tests/components/gpio/test.nrf52-mcumgr.yaml b/tests/components/gpio/test.nrf52-mcumgr.yaml index 912b9537c4..fb3f368e03 100644 --- a/tests/components/gpio/test.nrf52-mcumgr.yaml +++ b/tests/components/gpio/test.nrf52-mcumgr.yaml @@ -12,3 +12,20 @@ switch: - platform: gpio pin: P1.2 id: gpio_switch + + - platform: gpio + pin: P1.3 + id: gpio_switch_interlock_1 + interlock: [gpio_switch_interlock_2, gpio_switch_interlock_3] + interlock_wait_time: 100ms + + - platform: gpio + pin: P1.4 + id: gpio_switch_interlock_2 + interlock: [gpio_switch_interlock_1, gpio_switch_interlock_3] + + - platform: gpio + pin: P1.5 + id: gpio_switch_interlock_3 + interlock: [gpio_switch_interlock_1, gpio_switch_interlock_2] + interlock_wait_time: 50ms diff --git a/tests/components/gpio/test.rp2040-ard.yaml b/tests/components/gpio/test.rp2040-ard.yaml index fc7c9942d0..e9071b4356 100644 --- a/tests/components/gpio/test.rp2040-ard.yaml +++ b/tests/components/gpio/test.rp2040-ard.yaml @@ -2,5 +2,8 @@ substitutions: binary_sensor_pin: GPIO2 output_pin: GPIO3 switch_pin: GPIO4 + switch_pin_2: GPIO5 + switch_pin_3: GPIO6 + switch_pin_4: GPIO7 <<: !include common.yaml From 742eca92d892c4001c9ccd16148c36482a0df182 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 22 Oct 2025 10:22:56 +1300 Subject: [PATCH 214/526] [CI] Add auto label for chained PRs (#11457) --- .github/workflows/auto-label-pr.yml | 7 +++++-- .github/workflows/status-check-labels.yml | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-label-pr.yml b/.github/workflows/auto-label-pr.yml index 1670bd1821..4e2f086f47 100644 --- a/.github/workflows/auto-label-pr.yml +++ b/.github/workflows/auto-label-pr.yml @@ -53,6 +53,7 @@ jobs: 'new-target-platform', 'merging-to-release', 'merging-to-beta', + 'chained-pr', 'core', 'small-pr', 'dashboard', @@ -140,6 +141,8 @@ jobs: labels.add('merging-to-release'); } else if (baseRef === 'beta') { labels.add('merging-to-beta'); + } else if (baseRef !== 'dev') { + labels.add('chained-pr'); } return labels; @@ -528,8 +531,8 @@ jobs: const apiData = await fetchApiData(); const baseRef = context.payload.pull_request.base.ref; - // Early exit for non-dev branches - if (baseRef !== 'dev') { + // Early exit for release and beta branches only + if (baseRef === 'release' || baseRef === 'beta') { const branchLabels = await detectMergeBranch(); const finalLabels = Array.from(branchLabels); diff --git a/.github/workflows/status-check-labels.yml b/.github/workflows/status-check-labels.yml index e44fd18132..cca70815b9 100644 --- a/.github/workflows/status-check-labels.yml +++ b/.github/workflows/status-check-labels.yml @@ -14,6 +14,7 @@ jobs: label: - needs-docs - merge-after-release + - chained-pr steps: - name: Check for ${{ matrix.label }} label uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 From f2469077d9562c491520b0b732aba3c338ee2382 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 11:31:18 -1000 Subject: [PATCH 215/526] [light] Add tests for AddressableColorWipeEffectColor/StrobeLightEffectColor (#11456) --- tests/components/light/common.yaml | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/components/light/common.yaml b/tests/components/light/common.yaml index d4f64dcdea..f807014065 100644 --- a/tests/components/light/common.yaml +++ b/tests/components/light/common.yaml @@ -123,3 +123,43 @@ light: red: 100% green: 50% blue: 50% + # Test StrobeLightEffect with multiple colors + - platform: monochromatic + id: test_strobe_multiple + name: Strobe Multiple Colors + output: test_ledc_1 + effects: + - strobe: + name: Strobe Multi + colors: + - state: true + brightness: 100% + duration: 500ms + - state: false + duration: 250ms + - state: true + brightness: 50% + duration: 500ms + # Test StrobeLightEffect with transition + - platform: rgb + id: test_strobe_transition + name: Strobe With Transition + red: test_ledc_1 + green: test_ledc_2 + blue: test_ledc_3 + effects: + - strobe: + name: Strobe Transition + colors: + - state: true + red: 100% + green: 0% + blue: 0% + duration: 1s + transition_length: 500ms + - state: true + red: 0% + green: 100% + blue: 0% + duration: 1s + transition_length: 500ms From 9922c6591223821fe622cf71530544393c03bdb0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 11:32:48 -1000 Subject: [PATCH 216/526] Add compile tests for binary_sensor MultiClickTrigger (#11454) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- tests/components/binary_sensor/common.yaml | 66 ++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/components/binary_sensor/common.yaml b/tests/components/binary_sensor/common.yaml index 6965c1feeb..e3fd159b08 100644 --- a/tests/components/binary_sensor/common.yaml +++ b/tests/components/binary_sensor/common.yaml @@ -70,3 +70,69 @@ binary_sensor: - delay: 10s time_off: 200ms time_on: 800ms + + # Test on_multi_click with single click + - platform: template + id: multi_click_single + name: "Multi Click Single" + on_multi_click: + - timing: + - state: true + min_length: 50ms + max_length: 350ms + then: + - logger.log: "Single click detected" + + # Test on_multi_click with double click + - platform: template + id: multi_click_double + name: "Multi Click Double" + on_multi_click: + - timing: + - state: true + min_length: 50ms + max_length: 350ms + - state: false + min_length: 50ms + max_length: 350ms + - state: true + min_length: 50ms + max_length: 350ms + then: + - logger.log: "Double click detected" + + # Test on_multi_click with complex pattern (5 events) + - platform: template + id: multi_click_complex + name: "Multi Click Complex" + on_multi_click: + - timing: + - state: true + min_length: 50ms + max_length: 350ms + - state: false + min_length: 50ms + max_length: 350ms + - state: true + min_length: 50ms + max_length: 350ms + - state: false + min_length: 50ms + max_length: 350ms + - state: true + min_length: 50ms + then: + - logger.log: "Complex pattern detected" + + # Test on_multi_click with custom invalid_cooldown + - platform: template + id: multi_click_cooldown + name: "Multi Click Cooldown" + on_multi_click: + - timing: + - state: true + min_length: 100ms + max_length: 500ms + invalid_cooldown: 2s + then: + - logger.log: "Click with custom cooldown" From 6f7db2f5f77a7fb32e938588f52ec910c16b50d8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 11:35:34 -1000 Subject: [PATCH 217/526] [gpio] Optimize switch interlock with FixedVector (#11448) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/gpio/switch/gpio_switch.cpp | 2 +- esphome/components/gpio/switch/gpio_switch.h | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/esphome/components/gpio/switch/gpio_switch.cpp b/esphome/components/gpio/switch/gpio_switch.cpp index b67af5e95d..9043a6a493 100644 --- a/esphome/components/gpio/switch/gpio_switch.cpp +++ b/esphome/components/gpio/switch/gpio_switch.cpp @@ -67,7 +67,7 @@ void GPIOSwitch::write_state(bool state) { this->pin_->digital_write(state); this->publish_state(state); } -void GPIOSwitch::set_interlock(const std::vector &interlock) { this->interlock_ = interlock; } +void GPIOSwitch::set_interlock(const std::initializer_list &interlock) { this->interlock_ = interlock; } } // namespace gpio } // namespace esphome diff --git a/esphome/components/gpio/switch/gpio_switch.h b/esphome/components/gpio/switch/gpio_switch.h index 94d49745b5..080decac08 100644 --- a/esphome/components/gpio/switch/gpio_switch.h +++ b/esphome/components/gpio/switch/gpio_switch.h @@ -2,10 +2,9 @@ #include "esphome/core/component.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "esphome/components/switch/switch.h" -#include - namespace esphome { namespace gpio { @@ -19,14 +18,14 @@ class GPIOSwitch : public switch_::Switch, public Component { void setup() override; void dump_config() override; - void set_interlock(const std::vector &interlock); + void set_interlock(const std::initializer_list &interlock); void set_interlock_wait_time(uint32_t interlock_wait_time) { interlock_wait_time_ = interlock_wait_time; } protected: void write_state(bool state) override; GPIOPin *pin_; - std::vector interlock_; + FixedVector interlock_; uint32_t interlock_wait_time_{0}; }; From 8500323d397cefa73dca7951418ff41e2f3857e5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 11:47:31 -1000 Subject: [PATCH 218/526] [esp32] Add advanced options to disable unused VFS features (saves ~8.7 KB flash) (#11441) --- esphome/components/esp32/__init__.py | 70 +++++++++++++++++++++++ esphome/components/openthread/__init__.py | 10 ++++ 2 files changed, 80 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 99a87e06f9..cb6354cc74 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -550,6 +550,32 @@ CONF_ENABLE_LWIP_BRIDGE_INTERFACE = "enable_lwip_bridge_interface" CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING = "enable_lwip_tcpip_core_locking" CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY = "enable_lwip_check_thread_safety" CONF_DISABLE_LIBC_LOCKS_IN_IRAM = "disable_libc_locks_in_iram" +CONF_DISABLE_VFS_SUPPORT_TERMIOS = "disable_vfs_support_termios" +CONF_DISABLE_VFS_SUPPORT_SELECT = "disable_vfs_support_select" +CONF_DISABLE_VFS_SUPPORT_DIR = "disable_vfs_support_dir" + +# VFS requirement tracking +# Components that need VFS features can call require_vfs_select() or require_vfs_dir() +KEY_VFS_SELECT_REQUIRED = "vfs_select_required" +KEY_VFS_DIR_REQUIRED = "vfs_dir_required" + + +def require_vfs_select() -> None: + """Mark that VFS select support is required by a component. + + Call this from components that use esp_vfs_eventfd or other VFS select features. + This prevents CONFIG_VFS_SUPPORT_SELECT from being disabled. + """ + CORE.data[KEY_VFS_SELECT_REQUIRED] = True + + +def require_vfs_dir() -> None: + """Mark that VFS directory support is required by a component. + + Call this from components that use directory functions (opendir, readdir, mkdir, etc.). + This prevents CONFIG_VFS_SUPPORT_DIR from being disabled. + """ + CORE.data[KEY_VFS_DIR_REQUIRED] = True def _validate_idf_component(config: ConfigType) -> ConfigType: @@ -615,6 +641,13 @@ FRAMEWORK_SCHEMA = cv.All( cv.Optional( CONF_DISABLE_LIBC_LOCKS_IN_IRAM, default=True ): cv.boolean, + cv.Optional( + CONF_DISABLE_VFS_SUPPORT_TERMIOS, default=True + ): cv.boolean, + cv.Optional( + CONF_DISABLE_VFS_SUPPORT_SELECT, default=True + ): cv.boolean, + cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean, cv.Optional(CONF_EXECUTE_FROM_PSRAM): cv.boolean, } ), @@ -962,6 +995,43 @@ async def to_code(config): if advanced.get(CONF_DISABLE_LIBC_LOCKS_IN_IRAM, True): add_idf_sdkconfig_option("CONFIG_LIBC_LOCKS_PLACE_IN_IRAM", False) + # Disable VFS support for termios (terminal I/O functions) + # ESPHome doesn't use termios functions on ESP32 (only used in host UART driver). + # Saves approximately 1.8KB of flash when disabled (default). + add_idf_sdkconfig_option( + "CONFIG_VFS_SUPPORT_TERMIOS", + not advanced.get(CONF_DISABLE_VFS_SUPPORT_TERMIOS, True), + ) + + # Disable VFS support for select() with file descriptors + # ESPHome only uses select() with sockets via lwip_select(), which still works. + # VFS select is only needed for UART/eventfd file descriptors. + # Components that need it (e.g., openthread) call require_vfs_select(). + # Saves approximately 2.7KB of flash when disabled (default). + if CORE.data.get(KEY_VFS_SELECT_REQUIRED, False): + # Component requires VFS select - force enable regardless of user setting + add_idf_sdkconfig_option("CONFIG_VFS_SUPPORT_SELECT", True) + else: + # No component needs it - allow user to control (default: disabled) + add_idf_sdkconfig_option( + "CONFIG_VFS_SUPPORT_SELECT", + not advanced.get(CONF_DISABLE_VFS_SUPPORT_SELECT, True), + ) + + # Disable VFS support for directory functions (opendir, readdir, mkdir, etc.) + # ESPHome doesn't use directory functions on ESP32. + # Components that need it (e.g., storage components) call require_vfs_dir(). + # Saves approximately 0.5KB+ of flash when disabled (default). + if CORE.data.get(KEY_VFS_DIR_REQUIRED, False): + # Component requires VFS directory support - force enable regardless of user setting + add_idf_sdkconfig_option("CONFIG_VFS_SUPPORT_DIR", True) + else: + # No component needs it - allow user to control (default: disabled) + add_idf_sdkconfig_option( + "CONFIG_VFS_SUPPORT_DIR", + not advanced.get(CONF_DISABLE_VFS_SUPPORT_DIR, True), + ) + cg.add_platformio_option("board_build.partitions", "partitions.csv") if CONF_PARTITIONS in config: add_extra_build_file( diff --git a/esphome/components/openthread/__init__.py b/esphome/components/openthread/__init__.py index 3fac497c3d..4865399d02 100644 --- a/esphome/components/openthread/__init__.py +++ b/esphome/components/openthread/__init__.py @@ -4,6 +4,7 @@ from esphome.components.esp32 import ( VARIANT_ESP32H2, add_idf_sdkconfig_option, only_on_variant, + require_vfs_select, ) from esphome.components.mdns import MDNSComponent, enable_mdns_storage import esphome.config_validation as cv @@ -106,6 +107,14 @@ _CONNECTION_SCHEMA = cv.Schema( } ) + +def _require_vfs_select(config): + """Register VFS select requirement during config validation.""" + # OpenThread uses esp_vfs_eventfd which requires VFS select support + require_vfs_select() + return config + + CONFIG_SCHEMA = cv.All( cv.Schema( { @@ -122,6 +131,7 @@ CONFIG_SCHEMA = cv.All( cv.has_exactly_one_key(CONF_NETWORK_KEY, CONF_TLV), cv.only_with_esp_idf, only_on_variant(supported=[VARIANT_ESP32C6, VARIANT_ESP32H2]), + _require_vfs_select, ) From 1ea80594c66bd7fb3af2fb326fe853e6ad85f853 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 21 Oct 2025 15:11:11 -0700 Subject: [PATCH 219/526] [light] Improve gamma correction precision (#11141) Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston --- esphome/components/light/esp_color_correction.h | 8 ++++---- esphome/core/color.h | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/esphome/components/light/esp_color_correction.h b/esphome/components/light/esp_color_correction.h index 979a1acb07..14c065058c 100644 --- a/esphome/components/light/esp_color_correction.h +++ b/esphome/components/light/esp_color_correction.h @@ -17,19 +17,19 @@ class ESPColorCorrection { this->color_correct_blue(color.blue), this->color_correct_white(color.white)); } inline uint8_t color_correct_red(uint8_t red) const ESPHOME_ALWAYS_INLINE { - uint8_t res = esp_scale8(esp_scale8(red, this->max_brightness_.red), this->local_brightness_); + uint8_t res = esp_scale8_twice(red, this->max_brightness_.red, this->local_brightness_); return this->gamma_table_[res]; } inline uint8_t color_correct_green(uint8_t green) const ESPHOME_ALWAYS_INLINE { - uint8_t res = esp_scale8(esp_scale8(green, this->max_brightness_.green), this->local_brightness_); + uint8_t res = esp_scale8_twice(green, this->max_brightness_.green, this->local_brightness_); return this->gamma_table_[res]; } inline uint8_t color_correct_blue(uint8_t blue) const ESPHOME_ALWAYS_INLINE { - uint8_t res = esp_scale8(esp_scale8(blue, this->max_brightness_.blue), this->local_brightness_); + uint8_t res = esp_scale8_twice(blue, this->max_brightness_.blue, this->local_brightness_); return this->gamma_table_[res]; } inline uint8_t color_correct_white(uint8_t white) const ESPHOME_ALWAYS_INLINE { - uint8_t res = esp_scale8(esp_scale8(white, this->max_brightness_.white), this->local_brightness_); + uint8_t res = esp_scale8_twice(white, this->max_brightness_.white, this->local_brightness_); return this->gamma_table_[res]; } inline Color color_uncorrect(Color color) const ESPHOME_ALWAYS_INLINE { diff --git a/esphome/core/color.h b/esphome/core/color.h index 5dce58a485..4b0ae5b57a 100644 --- a/esphome/core/color.h +++ b/esphome/core/color.h @@ -14,6 +14,15 @@ inline static constexpr uint8_t esp_scale8(uint8_t i, uint8_t scale) { return (uint16_t(i) * (1 + uint16_t(scale))) / 256; } +/// Scale an 8-bit value by two 8-bit scale factors with improved precision. +/// This is more accurate than calling esp_scale8() twice because it delays +/// truncation until after both multiplications, preserving intermediate precision. +/// For example: esp_scale8_twice(value, max_brightness, local_brightness) +/// gives better results than esp_scale8(esp_scale8(value, max_brightness), local_brightness) +inline static constexpr uint8_t esp_scale8_twice(uint8_t i, uint8_t scale1, uint8_t scale2) { + return (uint32_t(i) * (1 + uint32_t(scale1)) * (1 + uint32_t(scale2))) >> 16; +} + struct Color { union { struct { From ae50a09b4e26c8acee06242ca7171a128beae242 Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Wed, 22 Oct 2025 00:21:22 +0200 Subject: [PATCH 220/526] C++ components unit test framework (#9284) Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston --- .clang-tidy.hash | 2 +- .github/workflows/ci.yml | 31 ++++ platformio.ini | 4 + script/cpp_unit_test.py | 172 +++++++++++++++++++++++ script/determine-jobs.py | 53 ++++++- script/extract_automations.py | 11 +- script/helpers.py | 114 ++++++++++++++- script/list-components.py | 43 ++++-- tests/components/.gitignore | 5 + tests/components/README.md | 32 +++++ tests/components/main.cpp | 26 ++++ tests/components/uart/common.h | 37 +++++ tests/components/uart/uart_component.cpp | 73 ++++++++++ tests/components/uart/uart_device.cpp | 108 ++++++++++++++ tests/script/test_determine_jobs.py | 79 +++++------ 15 files changed, 710 insertions(+), 80 deletions(-) create mode 100755 script/cpp_unit_test.py create mode 100644 tests/components/.gitignore create mode 100644 tests/components/README.md create mode 100644 tests/components/main.cpp create mode 100644 tests/components/uart/common.h create mode 100644 tests/components/uart/uart_component.cpp create mode 100644 tests/components/uart/uart_device.cpp diff --git a/.clang-tidy.hash b/.clang-tidy.hash index 2cd4319325..3ade00f0cd 100644 --- a/.clang-tidy.hash +++ b/.clang-tidy.hash @@ -1 +1 @@ -d7693a1e996cacd4a3d1c9a16336799c2a8cc3db02e4e74084151ce964581248 +3d46b63015d761c85ca9cb77ab79a389509e5776701fb22aed16e7b79d432c0c diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f085aedcc0..cb04f6bf8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -178,6 +178,8 @@ jobs: component-test-count: ${{ steps.determine.outputs.component-test-count }} changed-cpp-file-count: ${{ steps.determine.outputs.changed-cpp-file-count }} memory_impact: ${{ steps.determine.outputs.memory-impact }} + cpp-unit-tests-run-all: ${{ steps.determine.outputs.cpp-unit-tests-run-all }} + cpp-unit-tests-components: ${{ steps.determine.outputs.cpp-unit-tests-components }} steps: - name: Check out code from GitHub uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -210,6 +212,8 @@ jobs: echo "component-test-count=$(echo "$output" | jq -r '.component_test_count')" >> $GITHUB_OUTPUT echo "changed-cpp-file-count=$(echo "$output" | jq -r '.changed_cpp_file_count')" >> $GITHUB_OUTPUT echo "memory-impact=$(echo "$output" | jq -c '.memory_impact')" >> $GITHUB_OUTPUT + echo "cpp-unit-tests-run-all=$(echo "$output" | jq -r '.cpp_unit_tests_run_all')" >> $GITHUB_OUTPUT + echo "cpp-unit-tests-components=$(echo "$output" | jq -c '.cpp_unit_tests_components')" >> $GITHUB_OUTPUT integration-tests: name: Run integration tests @@ -247,6 +251,33 @@ jobs: . venv/bin/activate pytest -vv --no-cov --tb=native -n auto tests/integration/ + cpp-unit-tests: + name: Run C++ unit tests + runs-on: ubuntu-24.04 + needs: + - common + - determine-jobs + if: github.event_name == 'pull_request' && (needs.determine-jobs.outputs.cpp-unit-tests-run-all == 'true' || needs.determine-jobs.outputs.cpp-unit-tests-components != '[]') + steps: + - name: Check out code from GitHub + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: Restore Python + uses: ./.github/actions/restore-python + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache-key: ${{ needs.common.outputs.cache-key }} + + - name: Run cpp_unit_test.py + run: | + . venv/bin/activate + if [ "${{ needs.determine-jobs.outputs.cpp-unit-tests-run-all }}" = "true" ]; then + script/cpp_unit_test.py --all + else + ARGS=$(echo '${{ needs.determine-jobs.outputs.cpp-unit-tests-components }}' | jq -r '.[] | @sh' | xargs) + script/cpp_unit_test.py $ARGS + fi + clang-tidy-single: name: ${{ matrix.name }} runs-on: ubuntu-24.04 diff --git a/platformio.ini b/platformio.ini index 6b2a8657bb..94f58f84ab 100644 --- a/platformio.ini +++ b/platformio.ini @@ -46,6 +46,10 @@ lib_deps = ; This is using the repository until a new release is published to PlatformIO https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library lvgl/lvgl@8.4.0 ; lvgl + ; This dependency is used only in unit tests. + ; Must coincide with PLATFORMIO_GOOGLE_TEST_LIB in scripts/cpp_unit_test.py + ; See scripts/cpp_unit_test.py and tests/components/README.md + google/googletest@^1.15.2 build_flags = -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE -std=gnu++20 diff --git a/script/cpp_unit_test.py b/script/cpp_unit_test.py new file mode 100755 index 0000000000..e97b5bd7b0 --- /dev/null +++ b/script/cpp_unit_test.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import os +from pathlib import Path +import subprocess +import sys + +from helpers import get_all_components, get_all_dependencies, root_path + +from esphome.__main__ import command_compile, parse_args +from esphome.config import validate_config +from esphome.core import CORE +from esphome.platformio_api import get_idedata + +# This must coincide with the version in /platformio.ini +PLATFORMIO_GOOGLE_TEST_LIB = "google/googletest@^1.15.2" + +# Path to /tests/components +COMPONENTS_TESTS_DIR: Path = Path(root_path) / "tests" / "components" + + +def hash_components(components: list[str]) -> str: + key = ",".join(components) + return hashlib.sha256(key.encode()).hexdigest()[:16] + + +def filter_components_without_tests(components: list[str]) -> list[str]: + """Filter out components that do not have a corresponding test file. + + This is done by checking if the component's directory contains at + least a .cpp file. + """ + filtered_components: list[str] = [] + for component in components: + test_dir = COMPONENTS_TESTS_DIR / component + if test_dir.is_dir() and any(test_dir.glob("*.cpp")): + filtered_components.append(component) + else: + print( + f"WARNING: No tests found for component '{component}', skipping.", + file=sys.stderr, + ) + return filtered_components + + +def create_test_config(config_name: str, includes: list[str]) -> dict: + """Create ESPHome test configuration for C++ unit tests. + + Args: + config_name: Unique name for this test configuration + includes: List of include folders for the test build + + Returns: + Configuration dict for ESPHome + """ + return { + "esphome": { + "name": config_name, + "friendly_name": "CPP Unit Tests", + "libraries": PLATFORMIO_GOOGLE_TEST_LIB, + "platformio_options": { + "build_type": "debug", + "build_unflags": [ + "-Os", # remove size-opt flag + ], + "build_flags": [ + "-Og", # optimize for debug + ], + "debug_build_flags": [ # only for debug builds + "-g3", # max debug info + "-ggdb3", + ], + }, + "includes": includes, + }, + "host": {}, + "logger": {"level": "DEBUG"}, + } + + +def run_tests(selected_components: list[str]) -> int: + # Skip tests on Windows + if os.name == "nt": + print("Skipping esphome tests on Windows", file=sys.stderr) + return 1 + + # Remove components that do not have tests + components = filter_components_without_tests(selected_components) + + if len(components) == 0: + print( + "No components specified or no tests found for the specified components.", + file=sys.stderr, + ) + return 0 + + components = sorted(components) + + # Obtain possible dependencies for the requested components: + components_with_dependencies = sorted(get_all_dependencies(set(components))) + + # Build a list of include folders, one folder per component containing tests. + # A special replacement main.cpp is located in /tests/components/main.cpp + includes: list[str] = ["main.cpp"] + components + + # Create a unique name for this config based on the actual components being tested + # to maximize cache during testing + config_name: str = "cpptests-" + hash_components(components) + + config = create_test_config(config_name, includes) + + CORE.config_path = COMPONENTS_TESTS_DIR / "dummy.yaml" + CORE.dashboard = None + + # Validate config will expand the above with defaults: + config = validate_config(config, {}) + + # Add all components and dependencies to the base configuration after validation, so their files + # are added to the build. + config.update({key: {} for key in components_with_dependencies}) + + print(f"Testing components: {', '.join(components)}") + CORE.config = config + args = parse_args(["program", "compile", str(CORE.config_path)]) + try: + exit_code: int = command_compile(args, config) + + if exit_code != 0: + print(f"Error compiling unit tests for {', '.join(components)}") + return exit_code + except Exception as e: + print( + f"Error compiling unit tests for {', '.join(components)}. Check path. : {e}" + ) + return 2 + + # After a successful compilation, locate the executable and run it: + idedata = get_idedata(config) + if idedata is None: + print("Cannot find executable") + return 1 + + program_path: str = idedata.raw["prog_path"] + run_cmd: list[str] = [program_path] + run_proc = subprocess.run(run_cmd, check=False) + return run_proc.returncode + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Run C++ unit tests for ESPHome components." + ) + parser.add_argument( + "components", + nargs="*", + help="List of components to test. Use --all to test all known components.", + ) + parser.add_argument("--all", action="store_true", help="Test all known components.") + + args = parser.parse_args() + + if args.all: + components: list[str] = get_all_components() + else: + components: list[str] = args.components + + sys.exit(run_tests(components)) + + +if __name__ == "__main__": + main() diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 9721fd9756..6651553ce7 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -52,13 +52,16 @@ from helpers import ( CPP_FILE_EXTENSIONS, PYTHON_FILE_EXTENSIONS, changed_files, - filter_component_files, + core_changed, + filter_component_and_test_cpp_files, + filter_component_and_test_files, get_all_dependencies, get_changed_components, get_component_from_path, get_component_test_files, get_components_from_integration_fixtures, get_components_with_dependencies, + get_cpp_changed_components, git_ls_files, parse_test_filename, root_path, @@ -143,10 +146,9 @@ def should_run_integration_tests(branch: str | None = None) -> bool: """ files = changed_files(branch) - # Check if any core files changed (esphome/core/*) - for file in files: - if file.startswith("esphome/core/"): - return True + if core_changed(files): + # If any core files changed, run integration tests + return True # Check if any integration test files changed if any("tests/integration" in file for file in files): @@ -283,6 +285,40 @@ def should_run_python_linters(branch: str | None = None) -> bool: return _any_changed_file_endswith(branch, PYTHON_FILE_EXTENSIONS) +def determine_cpp_unit_tests( + branch: str | None = None, +) -> tuple[bool, list[str]]: + """Determine if C++ unit tests should run based on changed files. + + This function is used by the CI workflow to skip C++ unit tests when + no relevant files have changed, saving CI time and resources. + + C++ unit tests will run when any of the following conditions are met: + + 1. Any C++ core source files changed (esphome/core/*), in which case + all cpp unit tests run. + 2. A test file for a component changed, which triggers tests for that + component. + 3. The code for a component changed, which triggers tests for that + component and all components that depend on it. + + Args: + branch: Branch to compare against. If None, uses default. + + Returns: + Tuple of (run_all, components) where: + - run_all: True if all tests should run, False otherwise + - components: List of specific components to test (empty if run_all) + """ + files = changed_files(branch) + if core_changed(files): + return (True, []) + + # Filter to only C++ files + cpp_files = list(filter(filter_component_and_test_cpp_files, files)) + return (False, get_cpp_changed_components(cpp_files)) + + def _any_changed_file_endswith(branch: str | None, extensions: tuple[str, ...]) -> bool: """Check if a changed file ends with any of the specified extensions.""" return any(file.endswith(extensions) for file in changed_files(branch)) @@ -579,7 +615,7 @@ def main() -> None: else: # Get both directly changed and all changed (with dependencies) changed = changed_files(args.branch) - component_files = [f for f in changed if filter_component_files(f)] + component_files = [f for f in changed if filter_component_and_test_files(f)] directly_changed_components = get_components_with_dependencies( component_files, False @@ -646,6 +682,9 @@ def main() -> None: files_to_check_count = 0 # Build output + # Determine which C++ unit tests to run + cpp_run_all, cpp_components = determine_cpp_unit_tests(args.branch) + output: dict[str, Any] = { "integration_tests": run_integration, "clang_tidy": run_clang_tidy, @@ -661,6 +700,8 @@ def main() -> None: "dependency_only_count": len(dependency_only_components), "changed_cpp_file_count": changed_cpp_file_count, "memory_impact": memory_impact, + "cpp_unit_tests_run_all": cpp_run_all, + "cpp_unit_tests_components": cpp_components, } # Output as JSON diff --git a/script/extract_automations.py b/script/extract_automations.py index 943eb7110a..4e650ce25f 100755 --- a/script/extract_automations.py +++ b/script/extract_automations.py @@ -2,19 +2,14 @@ import json -from helpers import git_ls_files +from helpers import get_all_component_files, get_components_with_dependencies from esphome.automation import ACTION_REGISTRY, CONDITION_REGISTRY from esphome.pins import PIN_SCHEMA_REGISTRY -list_components = __import__("list-components") - - if __name__ == "__main__": - files = git_ls_files() - files = filter(list_components.filter_component_files, files) - - components = list_components.get_components(files, True) + files = get_all_component_files() + components = get_components_with_dependencies(files, True) dump = { "actions": sorted(list(ACTION_REGISTRY.keys())), diff --git a/script/helpers.py b/script/helpers.py index 6b2bb2daef..78c11b427e 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -25,12 +25,21 @@ CPP_FILE_EXTENSIONS = (".cpp", ".h", ".hpp", ".cc", ".cxx", ".c", ".tcc") # Python file extensions PYTHON_FILE_EXTENSIONS = (".py", ".pyi") +# Combined C++ and Python file extensions for convenience +CPP_AND_PYTHON_FILE_EXTENSIONS = (*CPP_FILE_EXTENSIONS, *PYTHON_FILE_EXTENSIONS) + # YAML file extensions YAML_FILE_EXTENSIONS = (".yaml", ".yml") # Component path prefix ESPHOME_COMPONENTS_PATH = "esphome/components/" +# Test components path prefix +ESPHOME_TESTS_COMPONENTS_PATH = "tests/components/" + +# Tuple of component and test paths for efficient startswith checks +COMPONENT_AND_TESTS_PATHS = (ESPHOME_COMPONENTS_PATH, ESPHOME_TESTS_COMPONENTS_PATH) + # Base bus components - these ARE the bus implementations and should not # be flagged as needing migration since they are the platform/base components BASE_BUS_COMPONENTS = { @@ -658,17 +667,32 @@ def get_components_from_integration_fixtures() -> set[str]: return components -def filter_component_files(file_path: str) -> bool: - """Check if a file path is a component file. +def filter_component_and_test_files(file_path: str) -> bool: + """Check if a file path is a component or test file. Args: file_path: Path to check Returns: - True if the file is in a component directory + True if the file is in a component or test directory """ - return file_path.startswith("esphome/components/") or file_path.startswith( - "tests/components/" + return file_path.startswith(COMPONENT_AND_TESTS_PATHS) or ( + file_path.startswith(ESPHOME_TESTS_COMPONENTS_PATH) + and file_path.endswith(YAML_FILE_EXTENSIONS) + ) + + +def filter_component_and_test_cpp_files(file_path: str) -> bool: + """Check if a file is a C++ source file in component or test directories. + + Args: + file_path: Path to check + + Returns: + True if the file is a C++ source/header file in component or test directories + """ + return file_path.endswith(CPP_FILE_EXTENSIONS) and file_path.startswith( + COMPONENT_AND_TESTS_PATHS ) @@ -740,7 +764,7 @@ def create_components_graph() -> dict[str, list[str]]: # The root directory of the repo root = Path(__file__).parent.parent - components_dir = root / "esphome" / "components" + components_dir = root / ESPHOME_COMPONENTS_PATH # Fake some directory so that get_component works CORE.config_path = root # Various configuration to capture different outcomes used by `AUTO_LOAD` function. @@ -873,3 +897,81 @@ def get_components_with_dependencies( return sorted(all_changed_components) return sorted(components) + + +def get_all_component_files() -> list[str]: + """Get all component and test files from git. + + Returns: + List of all component and test file paths + """ + files = git_ls_files() + return list(filter(filter_component_and_test_files, files)) + + +def get_all_components() -> list[str]: + """Get all component names. + + This function uses git to find all component files and extracts the component names. + It returns the same list as calling list-components.py without arguments. + + Returns: + List of all component names + """ + return get_components_with_dependencies(get_all_component_files(), False) + + +def core_changed(files: list[str]) -> bool: + """Check if any core C++ or Python files have changed. + + Args: + files: List of file paths to check + + Returns: + True if any core C++ or Python files have changed + """ + return any( + f.startswith("esphome/core/") and f.endswith(CPP_AND_PYTHON_FILE_EXTENSIONS) + for f in files + ) + + +def get_cpp_changed_components(files: list[str]) -> list[str]: + """Get components that have changed C++ files or tests. + + This function analyzes a list of changed files and determines which components + are affected. It handles two scenarios: + + 1. Test files changed (tests/components//*.cpp): + - Adds the component to the affected list + - Only that component needs to be tested + + 2. Component C++ files changed (esphome/components//*): + - Adds the component to the affected list + - Also adds all components that depend on this component (recursively) + - This ensures that changes propagate to dependent components + + Args: + files: List of file paths to analyze (should be C++ files) + + Returns: + Sorted list of component names that need C++ unit tests run + """ + components_graph = create_components_graph() + affected: set[str] = set() + for file in files: + if not file.endswith(CPP_FILE_EXTENSIONS): + continue + if file.startswith(ESPHOME_TESTS_COMPONENTS_PATH): + parts = file.split("/") + if len(parts) >= 4: + component_dir = Path(ESPHOME_TESTS_COMPONENTS_PATH) / parts[2] + if component_dir.is_dir(): + affected.add(parts[2]) + elif file.startswith(ESPHOME_COMPONENTS_PATH): + parts = file.split("/") + if len(parts) >= 4: + component = parts[2] + affected.update(find_children_of_component(components_graph, component)) + affected.add(component) + return sorted(affected) diff --git a/script/list-components.py b/script/list-components.py index d768256c71..31a1609f88 100755 --- a/script/list-components.py +++ b/script/list-components.py @@ -3,18 +3,14 @@ import argparse from helpers import ( changed_files, - filter_component_files, + filter_component_and_test_cpp_files, + filter_component_and_test_files, + get_all_component_files, get_components_with_dependencies, - git_ls_files, + get_cpp_changed_components, ) -def get_all_component_files() -> list[str]: - """Get all component files from git.""" - files = git_ls_files() - return list(filter(filter_component_files, files)) - - def main(): parser = argparse.ArgumentParser() parser.add_argument( @@ -39,16 +35,29 @@ def main(): parser.add_argument( "-b", "--branch", help="Branch to compare changed files against" ) + parser.add_argument( + "--cpp-changed", + action="store_true", + help="List components with changed C++ files", + ) args = parser.parse_args() if args.branch and not ( - args.changed or args.changed_direct or args.changed_with_deps + args.changed + or args.changed_direct + or args.changed_with_deps + or args.cpp_changed ): parser.error( - "--branch requires --changed, --changed-direct, or --changed-with-deps" + "--branch requires --changed, --changed-direct, --changed-with-deps, or --cpp-changed" ) - if args.changed or args.changed_direct or args.changed_with_deps: + if ( + args.changed + or args.changed_direct + or args.changed_with_deps + or args.cpp_changed + ): # When --changed* is passed, only get the changed files changed = changed_files(args.branch) @@ -68,6 +77,11 @@ def main(): # - --changed-with-deps: Used by CI test determination (script/determine-jobs.py) # Returns: Components with code changes + their dependencies (not infrastructure) # Reason: CI needs to test changed components and their dependents + # + # - --cpp-changed: Used by CI to determine if any C++ files changed (script/determine-jobs.py) + # Returns: Only components with changed C++ files + # Reason: Only components with C++ changes need C++ testing + base_test_changed = any( "tests/test_build_components" in file for file in changed ) @@ -80,7 +94,7 @@ def main(): # Only look at changed component files (ignore infrastructure changes) # For --changed-direct: only actual component code changes matter (for isolation) # For --changed-with-deps: only actual component code changes matter (for testing) - files = [f for f in changed if filter_component_files(f)] + files = [f for f in changed if filter_component_and_test_files(f)] else: # Get all component files files = get_all_component_files() @@ -100,6 +114,11 @@ def main(): # Return only directly changed components (without dependencies) for c in get_components_with_dependencies(files, False): print(c) + elif args.cpp_changed: + # Only look at changed cpp files + files = list(filter(filter_component_and_test_cpp_files, changed)) + for c in get_cpp_changed_components(files): + print(c) else: # Return all changed components (with dependencies) - default behavior for c in get_components_with_dependencies(files, args.changed): diff --git a/tests/components/.gitignore b/tests/components/.gitignore new file mode 100644 index 0000000000..d8b4157aef --- /dev/null +++ b/tests/components/.gitignore @@ -0,0 +1,5 @@ +# Gitignore settings for ESPHome +# This is an example and may include too much for your use-case. +# You can modify this file to suit your needs. +/.esphome/ +/secrets.yaml diff --git a/tests/components/README.md b/tests/components/README.md new file mode 100644 index 0000000000..0901f2ef17 --- /dev/null +++ b/tests/components/README.md @@ -0,0 +1,32 @@ +# How to write C++ ESPHome unit tests + +1. Locate the folder with your component or create a new one with the same name as the component. +2. Write the tests. You can add as many `.cpp` and `.h` files as you need to organize your tests. + +**IMPORTANT**: wrap all your testing code in a unique namespace to avoid linker collisions when compiling +testing binaries that combine many components. By convention, this unique namespace is `esphome::component::testing` +(where "component" is the component under test), for example: `esphome::uart::testing`. + + +## Running component unit tests + +(from the repository root) +```bash +./script/cpp_unit_test.py component1 component2 ... +``` + +The above will compile and run the provided components and their tests. + +To run all tests, you can invoke `cpp_unit_test.py` with the special `--all` flag: + +```bash +./script/cpp_unit_test.py --all +``` + +To run a specific test suite, you can provide a Google Test filter: + +```bash +GTEST_FILTER='UART*' ./script/cpp_unit_test.py uart modbus +``` + +The process will return `0` for success or nonzero for failure. In case of failure, the errors will be printed out to the console. diff --git a/tests/components/main.cpp b/tests/components/main.cpp new file mode 100644 index 0000000000..928f0e6059 --- /dev/null +++ b/tests/components/main.cpp @@ -0,0 +1,26 @@ +#include + +/* +This special main.cpp replaces the default one. +It will run all the Google Tests found in all compiled cpp files and then exit with the result +See README.md for more information +*/ + +// Auto generated code by esphome +// ========== AUTO GENERATED INCLUDE BLOCK BEGIN =========== +// ========== AUTO GENERATED INCLUDE BLOCK END ===========" + +void original_setup() { + // This function won't be run. + + // ========== AUTO GENERATED CODE BEGIN =========== + // =========== AUTO GENERATED CODE END ============ +} + +void setup() { + ::testing::InitGoogleTest(); + int exit_code = RUN_ALL_TESTS(); + exit(exit_code); +} + +void loop() {} diff --git a/tests/components/uart/common.h b/tests/components/uart/common.h new file mode 100644 index 0000000000..5597b86410 --- /dev/null +++ b/tests/components/uart/common.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include +#include +#include +#include "esphome/components/uart/uart_component.h" + +namespace esphome::uart::testing { + +using ::testing::_; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::SetArgPointee; + +// Derive a mock from UARTComponent to test the wrapper implementations. +class MockUARTComponent : public UARTComponent { + public: + using UARTComponent::write_array; + using UARTComponent::write_byte; + + // NOTE: std::vector is used here for test convenience. For production code, + // consider using StaticVector or FixedVector from esphome/core/helpers.h instead. + std::vector written_data; + + void write_array(const uint8_t *data, size_t len) override { written_data.assign(data, data + len); } + + MOCK_METHOD(bool, read_array, (uint8_t * data, size_t len), (override)); + MOCK_METHOD(bool, peek_byte, (uint8_t * data), (override)); + MOCK_METHOD(int, available, (), (override)); + MOCK_METHOD(void, flush, (), (override)); + MOCK_METHOD(void, check_logger_conflict, (), (override)); +}; + +} // namespace esphome::uart::testing diff --git a/tests/components/uart/uart_component.cpp b/tests/components/uart/uart_component.cpp new file mode 100644 index 0000000000..2cab1f62ad --- /dev/null +++ b/tests/components/uart/uart_component.cpp @@ -0,0 +1,73 @@ +#include "common.h" + +namespace esphome::uart::testing { + +TEST(UARTComponentTest, SetGetBaudRate) { + MockUARTComponent mock; + mock.set_baud_rate(38400); + EXPECT_EQ(mock.get_baud_rate(), 38400); +} + +TEST(UARTComponentTest, SetGetStopBits) { + MockUARTComponent mock; + mock.set_stop_bits(2); + EXPECT_EQ(mock.get_stop_bits(), 2); +} + +TEST(UARTComponentTest, SetGetDataBits) { + MockUARTComponent mock; + mock.set_data_bits(7); + EXPECT_EQ(mock.get_data_bits(), 7); +} + +TEST(UARTComponentTest, SetGetParity) { + MockUARTComponent mock; + mock.set_parity(UARTParityOptions::UART_CONFIG_PARITY_EVEN); + EXPECT_EQ(mock.get_parity(), UARTParityOptions::UART_CONFIG_PARITY_EVEN); +} + +TEST(UARTComponentTest, SetGetRxBufferSize) { + MockUARTComponent mock; + mock.set_rx_buffer_size(128); + EXPECT_EQ(mock.get_rx_buffer_size(), 128); +} + +TEST(UARTComponentTest, WriteArrayVector) { + MockUARTComponent mock; + std::vector data = {10, 20, 30}; + mock.write_array(data); + EXPECT_EQ(mock.written_data, data); +} +TEST(UARTComponentTest, WriteByte) { + MockUARTComponent mock; + uint8_t byte = 0x79; + mock.write_byte(byte); + EXPECT_EQ(mock.written_data.size(), 1); + EXPECT_EQ(mock.written_data[0], byte); +} + +TEST(UARTComponentTest, WriteStr) { + MockUARTComponent mock; + const char *str = "Hello"; + std::vector captured; + mock.write_str(str); + EXPECT_EQ(mock.written_data.size(), strlen(str)); + EXPECT_EQ(0, strncmp(str, (const char *) mock.written_data.data(), mock.written_data.size())); +} + +// Tests for wrapper methods forwarding to pure virtual read_array +TEST(UARTComponentTest, ReadByteSuccess) { + MockUARTComponent mock; + uint8_t value = 0; + EXPECT_CALL(mock, read_array(&value, 1)).WillOnce(Return(true)); + EXPECT_TRUE(mock.read_byte(&value)); +} + +TEST(UARTComponentTest, ReadByteFailure) { + MockUARTComponent mock; + uint8_t value = 0xFF; + EXPECT_CALL(mock, read_array(&value, 1)).WillOnce(Return(false)); + EXPECT_FALSE(mock.read_byte(&value)); +} + +} // namespace esphome::uart::testing diff --git a/tests/components/uart/uart_device.cpp b/tests/components/uart/uart_device.cpp new file mode 100644 index 0000000000..c3f1d9078b --- /dev/null +++ b/tests/components/uart/uart_device.cpp @@ -0,0 +1,108 @@ +#include "common.h" +#include "esphome/components/uart/uart.h" + +namespace esphome::uart::testing { + +TEST(UARTDeviceTest, ReadByteSuccess) { + MockUARTComponent mock; + UARTDevice dev(&mock); + uint8_t value = 0; + EXPECT_CALL(mock, read_array(_, 1)).WillOnce(DoAll(SetArgPointee<0>(0x5A), Return(true))); + bool result = dev.read_byte(&value); + EXPECT_TRUE(result); + EXPECT_EQ(value, 0x5A); +} + +TEST(UARTDeviceTest, ReadByteFailure) { + MockUARTComponent mock; + UARTDevice dev(&mock); + uint8_t value = 0xFF; + EXPECT_CALL(mock, read_array(_, 1)).WillOnce(Return(false)); + bool result = dev.read_byte(&value); + EXPECT_FALSE(result); +} + +TEST(UARTDeviceTest, PeekByteSuccess) { + MockUARTComponent mock; + UARTDevice dev(&mock); + uint8_t value = 0; + EXPECT_CALL(mock, peek_byte(_)).WillOnce(DoAll(SetArgPointee<0>(0xA5), Return(true))); + bool result = dev.peek_byte(&value); + EXPECT_TRUE(result); + EXPECT_EQ(value, 0xA5); +} + +TEST(UARTDeviceTest, PeekByteFailure) { + MockUARTComponent mock; + UARTDevice dev(&mock); + uint8_t value = 0; + EXPECT_CALL(mock, peek_byte(_)).WillOnce(Return(false)); + bool result = dev.peek_byte(&value); + EXPECT_FALSE(result); +} + +TEST(UARTDeviceTest, Available) { + MockUARTComponent mock; + UARTDevice dev(&mock); + EXPECT_CALL(mock, available()).WillOnce(Return(5)); + EXPECT_EQ(dev.available(), 5); +} + +TEST(UARTDeviceTest, FlushCallsParent) { + MockUARTComponent mock; + UARTDevice dev(&mock); + EXPECT_CALL(mock, flush()).Times(1); + dev.flush(); +} + +TEST(UARTDeviceTest, WriteByteForwardsToWriteArray) { + MockUARTComponent mock; + UARTDevice dev(&mock); + dev.write_byte(0xAB); + EXPECT_EQ(mock.written_data.size(), 1); + EXPECT_EQ(mock.written_data[0], 0xAB); +} +TEST(UARTDeviceTest, WriteArrayPointer) { + MockUARTComponent mock; + UARTDevice dev(&mock); + uint8_t data[3] = {1, 2, 3}; + dev.write_array(data, 3); + EXPECT_EQ(mock.written_data.size(), 3); + EXPECT_EQ(mock.written_data, std::vector(data, data + 3)); +} + +TEST(UARTDeviceTest, WriteArrayVector) { + MockUARTComponent mock; + UARTDevice dev(&mock); + std::vector data = {4, 5, 6}; + dev.write_array(data); + EXPECT_EQ(mock.written_data, data); +} + +TEST(UARTDeviceTest, WriteArrayStdArray) { + MockUARTComponent mock; + UARTDevice dev(&mock); + std::array data = {7, 8, 9, 10}; + dev.write_array(data); + EXPECT_EQ(mock.written_data.size(), data.size()); + EXPECT_EQ(mock.written_data, std::vector(data.begin(), data.end())); +} + +TEST(UARTDeviceTest, WriteStrForwardsToWriteArray) { + MockUARTComponent mock; + UARTDevice dev(&mock); + const char *str = "ESPHome"; + dev.write_str(str); + EXPECT_EQ(mock.written_data.size(), strlen(str)); + EXPECT_EQ(0, strncmp(str, (const char *) mock.written_data.data(), mock.written_data.size())); +} + +TEST(UARTDeviceTest, WriteStrEmptyString) { + MockUARTComponent mock; + UARTDevice dev(&mock); + const char *str = ""; + dev.write_str(str); + EXPECT_EQ(mock.written_data.size(), 0); +} + +} // namespace esphome::uart::testing diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 35652e0efc..a859b3c24d 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -5,7 +5,6 @@ import importlib.util import json import os from pathlib import Path -import subprocess import sys from unittest.mock import Mock, call, patch @@ -56,9 +55,9 @@ def mock_should_run_python_linters() -> Generator[Mock, None, None]: @pytest.fixture -def mock_subprocess_run() -> Generator[Mock, None, None]: - """Mock subprocess.run for list-components.py calls.""" - with patch.object(determine_jobs.subprocess, "run") as mock: +def mock_determine_cpp_unit_tests() -> Generator[Mock, None, None]: + """Mock determine_cpp_unit_tests from helpers.""" + with patch.object(determine_jobs, "determine_cpp_unit_tests") as mock: yield mock @@ -82,8 +81,8 @@ def test_main_all_tests_should_run( mock_should_run_clang_tidy: Mock, mock_should_run_clang_format: Mock, mock_should_run_python_linters: Mock, - mock_subprocess_run: Mock, mock_changed_files: Mock, + mock_determine_cpp_unit_tests: Mock, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -95,6 +94,7 @@ def test_main_all_tests_should_run( mock_should_run_clang_tidy.return_value = True mock_should_run_clang_format.return_value = True mock_should_run_python_linters.return_value = True + mock_determine_cpp_unit_tests.return_value = (False, ["wifi", "api", "sensor"]) # Mock changed_files to return non-component files (to avoid memory impact) # Memory impact only runs when component C++ files change @@ -114,15 +114,15 @@ def test_main_all_tests_should_run( ), patch.object( determine_jobs, - "filter_component_files", + "filter_component_and_test_files", side_effect=lambda f: f.startswith("esphome/components/"), ), patch.object( determine_jobs, "get_components_with_dependencies", - side_effect=lambda files, deps: ["wifi", "api"] - if not deps - else ["wifi", "api", "sensor"], + side_effect=lambda files, deps: ( + ["wifi", "api"] if not deps else ["wifi", "api", "sensor"] + ), ), ): determine_jobs.main() @@ -150,6 +150,8 @@ def test_main_all_tests_should_run( # memory_impact should be false (no component C++ files changed) assert "memory_impact" in output assert output["memory_impact"]["should_run"] == "false" + assert output["cpp_unit_tests_run_all"] is False + assert output["cpp_unit_tests_components"] == ["wifi", "api", "sensor"] def test_main_no_tests_should_run( @@ -157,8 +159,8 @@ def test_main_no_tests_should_run( mock_should_run_clang_tidy: Mock, mock_should_run_clang_format: Mock, mock_should_run_python_linters: Mock, - mock_subprocess_run: Mock, mock_changed_files: Mock, + mock_determine_cpp_unit_tests: Mock, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -170,6 +172,7 @@ def test_main_no_tests_should_run( mock_should_run_clang_tidy.return_value = False mock_should_run_clang_format.return_value = False mock_should_run_python_linters.return_value = False + mock_determine_cpp_unit_tests.return_value = (False, []) # Mock changed_files to return no component files mock_changed_files.return_value = [] @@ -178,7 +181,9 @@ def test_main_no_tests_should_run( with ( patch("sys.argv", ["determine-jobs.py"]), patch.object(determine_jobs, "get_changed_components", return_value=[]), - patch.object(determine_jobs, "filter_component_files", return_value=False), + patch.object( + determine_jobs, "filter_component_and_test_files", return_value=False + ), patch.object( determine_jobs, "get_components_with_dependencies", return_value=[] ), @@ -202,31 +207,8 @@ def test_main_no_tests_should_run( # memory_impact should be present assert "memory_impact" in output assert output["memory_impact"]["should_run"] == "false" - - -def test_main_list_components_fails( - mock_should_run_integration_tests: Mock, - mock_should_run_clang_tidy: Mock, - mock_should_run_clang_format: Mock, - mock_should_run_python_linters: Mock, - mock_subprocess_run: Mock, - capsys: pytest.CaptureFixture[str], -) -> None: - """Test when list-components.py fails.""" - mock_should_run_integration_tests.return_value = True - mock_should_run_clang_tidy.return_value = True - mock_should_run_clang_format.return_value = True - mock_should_run_python_linters.return_value = True - - # Mock list-components.py failure - mock_subprocess_run.side_effect = subprocess.CalledProcessError(1, "cmd") - - # Run main function with mocked argv - should raise - with ( - patch("sys.argv", ["determine-jobs.py"]), - pytest.raises(subprocess.CalledProcessError), - ): - determine_jobs.main() + assert output["cpp_unit_tests_run_all"] is False + assert output["cpp_unit_tests_components"] == [] def test_main_with_branch_argument( @@ -234,8 +216,8 @@ def test_main_with_branch_argument( mock_should_run_clang_tidy: Mock, mock_should_run_clang_format: Mock, mock_should_run_python_linters: Mock, - mock_subprocess_run: Mock, mock_changed_files: Mock, + mock_determine_cpp_unit_tests: Mock, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -247,6 +229,7 @@ def test_main_with_branch_argument( mock_should_run_clang_tidy.return_value = True mock_should_run_clang_format.return_value = False mock_should_run_python_linters.return_value = True + mock_determine_cpp_unit_tests.return_value = (False, ["mqtt"]) # Mock changed_files to return non-component files (to avoid memory impact) # Memory impact only runs when component C++ files change @@ -258,7 +241,7 @@ def test_main_with_branch_argument( patch.object(determine_jobs, "get_changed_components", return_value=["mqtt"]), patch.object( determine_jobs, - "filter_component_files", + "filter_component_and_test_files", side_effect=lambda f: f.startswith("esphome/components/"), ), patch.object( @@ -296,6 +279,8 @@ def test_main_with_branch_argument( # memory_impact should be false (no component C++ files changed) assert "memory_impact" in output assert output["memory_impact"]["should_run"] == "false" + assert output["cpp_unit_tests_run_all"] is False + assert output["cpp_unit_tests_components"] == ["mqtt"] def test_should_run_integration_tests( @@ -506,7 +491,6 @@ def test_main_filters_components_without_tests( mock_should_run_clang_tidy: Mock, mock_should_run_clang_format: Mock, mock_should_run_python_linters: Mock, - mock_subprocess_run: Mock, mock_changed_files: Mock, capsys: pytest.CaptureFixture[str], tmp_path: Path, @@ -556,16 +540,17 @@ def test_main_filters_components_without_tests( ), patch.object( determine_jobs, - "filter_component_files", + "filter_component_and_test_files", side_effect=lambda f: f.startswith("esphome/components/"), ), patch.object( determine_jobs, "get_components_with_dependencies", - side_effect=lambda files, deps: ["wifi", "sensor"] - if not deps - else ["wifi", "sensor", "airthings_ble"], + side_effect=lambda files, deps: ( + ["wifi", "sensor"] if not deps else ["wifi", "sensor", "airthings_ble"] + ), ), + patch.object(determine_jobs, "changed_files", return_value=[]), ): # Clear the cache since we're mocking root_path determine_jobs._component_has_tests.cache_clear() @@ -808,7 +793,6 @@ def test_clang_tidy_mode_full_scan( mock_should_run_clang_tidy: Mock, mock_should_run_clang_format: Mock, mock_should_run_python_linters: Mock, - mock_subprocess_run: Mock, mock_changed_files: Mock, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch, @@ -829,7 +813,9 @@ def test_clang_tidy_mode_full_scan( patch("sys.argv", ["determine-jobs.py"]), patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=True), patch.object(determine_jobs, "get_changed_components", return_value=[]), - patch.object(determine_jobs, "filter_component_files", return_value=False), + patch.object( + determine_jobs, "filter_component_and_test_files", return_value=False + ), patch.object( determine_jobs, "get_components_with_dependencies", return_value=[] ), @@ -873,7 +859,6 @@ def test_clang_tidy_mode_targeted_scan( mock_should_run_clang_tidy: Mock, mock_should_run_clang_format: Mock, mock_should_run_python_linters: Mock, - mock_subprocess_run: Mock, mock_changed_files: Mock, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch, @@ -912,7 +897,7 @@ def test_clang_tidy_mode_targeted_scan( patch.object(determine_jobs, "get_changed_components", return_value=components), patch.object( determine_jobs, - "filter_component_files", + "filter_component_and_test_files", side_effect=lambda f: f.startswith("esphome/components/"), ), patch.object( From 9c712744bea9c495bfc7c62275c4f8c03cf9493a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 12:40:19 -1000 Subject: [PATCH 221/526] [light] Replace std::vector with FixedVector in strobe and color_wipe effects (#11455) --- esphome/components/light/addressable_light_effect.h | 6 +++--- esphome/components/light/base_light_effects.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/light/addressable_light_effect.h b/esphome/components/light/addressable_light_effect.h index fcf76b3cb0..9caccad634 100644 --- a/esphome/components/light/addressable_light_effect.h +++ b/esphome/components/light/addressable_light_effect.h @@ -1,9 +1,9 @@ #pragma once #include -#include #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/light/light_state.h" #include "esphome/components/light/addressable_light.h" @@ -113,7 +113,7 @@ struct AddressableColorWipeEffectColor { class AddressableColorWipeEffect : public AddressableLightEffect { public: explicit AddressableColorWipeEffect(const std::string &name) : AddressableLightEffect(name) {} - void set_colors(const std::vector &colors) { this->colors_ = colors; } + void set_colors(const std::initializer_list &colors) { this->colors_ = colors; } void set_add_led_interval(uint32_t add_led_interval) { this->add_led_interval_ = add_led_interval; } void set_reverse(bool reverse) { this->reverse_ = reverse; } void apply(AddressableLight &it, const Color ¤t_color) override { @@ -155,7 +155,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect { } protected: - std::vector colors_; + FixedVector colors_; size_t at_color_{0}; uint32_t last_add_{0}; uint32_t add_led_interval_{}; diff --git a/esphome/components/light/base_light_effects.h b/esphome/components/light/base_light_effects.h index ff6cd1ccfe..c74d19fe14 100644 --- a/esphome/components/light/base_light_effects.h +++ b/esphome/components/light/base_light_effects.h @@ -1,9 +1,9 @@ #pragma once #include -#include #include "esphome/core/automation.h" +#include "esphome/core/helpers.h" #include "light_effect.h" namespace esphome { @@ -188,10 +188,10 @@ class StrobeLightEffect : public LightEffect { this->last_switch_ = now; } - void set_colors(const std::vector &colors) { this->colors_ = colors; } + void set_colors(const std::initializer_list &colors) { this->colors_ = colors; } protected: - std::vector colors_; + FixedVector colors_; uint32_t last_switch_{0}; size_t at_color_{0}; }; From c6ae1a5909c0a0f2ec87c479da43a5602109eeb5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:00:27 +1300 Subject: [PATCH 222/526] [core] Stop clang-format "fixing" a single line (#11462) --- esphome/core/defines.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 4e9fb078a0..39698c1004 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -243,8 +243,10 @@ // Dummy firmware payload for shelly_dimmer #define USE_SHD_FIRMWARE_MAJOR_VERSION 56 #define USE_SHD_FIRMWARE_MINOR_VERSION 5 +// clang-format off #define USE_SHD_FIRMWARE_DATA \ {} +// clang-format on #define USE_WEBSERVER #define USE_WEBSERVER_AUTH From 2c1927fd123ed96f1cbab482ef533e81d5b4d402 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:24:56 +1300 Subject: [PATCH 223/526] [api] Allow clearing noise psk if dynamically set (#11429) --- esphome/components/api/api_connection.cpp | 8 ++- esphome/components/api/api_server.cpp | 61 ++++++++++++------- esphome/components/api/api_server.h | 5 ++ ...noise_encryption_key_clear_protection.yaml | 10 +++ .../test_noise_encryption_key_protection.py | 39 ++++++++++++ 5 files changed, 101 insertions(+), 22 deletions(-) create mode 100644 tests/integration/fixtures/noise_encryption_key_clear_protection.yaml diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 6334815678..7c135946f8 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1572,7 +1572,13 @@ bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryption resp.success = false; psk_t psk{}; - if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) { + if (msg.key.empty()) { + if (this->parent_->clear_noise_psk(true)) { + resp.success = true; + } else { + ESP_LOGW(TAG, "Failed to clear encryption key"); + } + } else if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) { ESP_LOGW(TAG, "Invalid encryption key length"); } else if (!this->parent_->save_noise_psk(psk, true)) { ESP_LOGW(TAG, "Failed to save encryption key"); diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 778d9389ef..e618610a75 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -468,6 +468,31 @@ uint16_t APIServer::get_port() const { return this->port_; } void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; } #ifdef USE_API_NOISE +bool APIServer::update_noise_psk_(const SavedNoisePsk &new_psk, const LogString *save_log_msg, + const LogString *fail_log_msg, const psk_t &active_psk, bool make_active) { + if (!this->noise_pref_.save(&new_psk)) { + ESP_LOGW(TAG, "%s", LOG_STR_ARG(fail_log_msg)); + return false; + } + // ensure it's written immediately + if (!global_preferences->sync()) { + ESP_LOGW(TAG, "Failed to sync preferences"); + return false; + } + ESP_LOGD(TAG, "%s", LOG_STR_ARG(save_log_msg)); + if (make_active) { + this->set_timeout(100, [this, active_psk]() { + ESP_LOGW(TAG, "Disconnecting all clients to reset PSK"); + this->set_noise_psk(active_psk); + for (auto &c : this->clients_) { + DisconnectRequest req; + c->send_message(req, DisconnectRequest::MESSAGE_TYPE); + } + }); + } + return true; +} + bool APIServer::save_noise_psk(psk_t psk, bool make_active) { #ifdef USE_API_NOISE_PSK_FROM_YAML // When PSK is set from YAML, this function should never be called @@ -482,27 +507,21 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) { } SavedNoisePsk new_saved_psk{psk}; - if (!this->noise_pref_.save(&new_saved_psk)) { - ESP_LOGW(TAG, "Failed to save Noise PSK"); - return false; - } - // ensure it's written immediately - if (!global_preferences->sync()) { - ESP_LOGW(TAG, "Failed to sync preferences"); - return false; - } - ESP_LOGD(TAG, "Noise PSK saved"); - if (make_active) { - this->set_timeout(100, [this, psk]() { - ESP_LOGW(TAG, "Disconnecting all clients to reset PSK"); - this->set_noise_psk(psk); - for (auto &c : this->clients_) { - DisconnectRequest req; - c->send_message(req, DisconnectRequest::MESSAGE_TYPE); - } - }); - } - return true; + return this->update_noise_psk_(new_saved_psk, LOG_STR("Noise PSK saved"), LOG_STR("Failed to save Noise PSK"), psk, + make_active); +#endif +} +bool APIServer::clear_noise_psk(bool make_active) { +#ifdef USE_API_NOISE_PSK_FROM_YAML + // When PSK is set from YAML, this function should never be called + // but if it is, reject the change + ESP_LOGW(TAG, "Key set in YAML"); + return false; +#else + SavedNoisePsk empty_psk{}; + psk_t empty{}; + return this->update_noise_psk_(empty_psk, LOG_STR("Noise PSK cleared"), LOG_STR("Failed to clear Noise PSK"), empty, + make_active); #endif } #endif diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 5d038e5ddd..e0e23301d0 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -53,6 +53,7 @@ class APIServer : public Component, public Controller { #ifdef USE_API_NOISE bool save_noise_psk(psk_t psk, bool make_active = true); + bool clear_noise_psk(bool make_active = true); void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); } std::shared_ptr get_noise_ctx() { return noise_ctx_; } #endif // USE_API_NOISE @@ -174,6 +175,10 @@ class APIServer : public Component, public Controller { protected: void schedule_reboot_timeout_(); +#ifdef USE_API_NOISE + bool update_noise_psk_(const SavedNoisePsk &new_psk, const LogString *save_log_msg, const LogString *fail_log_msg, + const psk_t &active_psk, bool make_active); +#endif // USE_API_NOISE // Pointers and pointer-like types first (4 bytes each) std::unique_ptr socket_ = nullptr; #ifdef USE_API_CLIENT_CONNECTED_TRIGGER diff --git a/tests/integration/fixtures/noise_encryption_key_clear_protection.yaml b/tests/integration/fixtures/noise_encryption_key_clear_protection.yaml new file mode 100644 index 0000000000..3ce84cd373 --- /dev/null +++ b/tests/integration/fixtures/noise_encryption_key_clear_protection.yaml @@ -0,0 +1,10 @@ +esphome: + name: noise-key-test + +host: + +api: + encryption: + key: "zX9/JHxMKwpP0jUGsF0iESCm1wRvNgR6NkKVOhn7kSs=" + +logger: diff --git a/tests/integration/test_noise_encryption_key_protection.py b/tests/integration/test_noise_encryption_key_protection.py index 03c43ca8d3..37d32ce2b4 100644 --- a/tests/integration/test_noise_encryption_key_protection.py +++ b/tests/integration/test_noise_encryption_key_protection.py @@ -49,3 +49,42 @@ async def test_noise_encryption_key_protection( with pytest.raises(InvalidEncryptionKeyAPIError): async with api_client_connected(noise_psk=wrong_key) as client: await client.device_info() + + +@pytest.mark.asyncio +async def test_noise_encryption_key_clear_protection( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that noise encryption key set in YAML cannot be changed via API.""" + # The key that's set in the YAML fixture + noise_psk = "zX9/JHxMKwpP0jUGsF0iESCm1wRvNgR6NkKVOhn7kSs=" + + # Keep ESPHome process running throughout all tests + async with run_compiled(yaml_config): + # First connection - test key change attempt + async with api_client_connected(noise_psk=noise_psk) as client: + # Verify connection is established + device_info = await client.device_info() + assert device_info is not None + + # Try to set a new encryption key via API + new_key = b"" # Empty key to attempt to clear + + # This should fail since key was set in YAML + success = await client.noise_encryption_set_key(new_key) + assert success is False + + # Reconnect with the original key to verify it still works + async with api_client_connected(noise_psk=noise_psk) as client: + # Verify connection is still successful with original key + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "noise-key-test" + + # Verify that connecting with a wrong key fails + wrong_key = base64.b64encode(b"y" * 32).decode() # Different key + with pytest.raises(InvalidEncryptionKeyAPIError): + async with api_client_connected(noise_psk=wrong_key) as client: + await client.device_info() From 78ffeb30fb9f3cb578fd17d8ed852c139696f191 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 17:55:13 -1000 Subject: [PATCH 224/526] [binary_sensor] Optimize MultiClickTrigger with FixedVector (#11453) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/binary_sensor/automation.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/binary_sensor/automation.h b/esphome/components/binary_sensor/automation.h index b46436dc41..0bc7b9acb3 100644 --- a/esphome/components/binary_sensor/automation.h +++ b/esphome/components/binary_sensor/automation.h @@ -2,11 +2,11 @@ #include #include -#include #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { @@ -92,8 +92,8 @@ class DoubleClickTrigger : public Trigger<> { class MultiClickTrigger : public Trigger<>, public Component { public: - explicit MultiClickTrigger(BinarySensor *parent, std::vector timing) - : parent_(parent), timing_(std::move(timing)) {} + explicit MultiClickTrigger(BinarySensor *parent, std::initializer_list timing) + : parent_(parent), timing_(timing) {} void setup() override { this->last_state_ = this->parent_->get_state_default(false); @@ -115,7 +115,7 @@ class MultiClickTrigger : public Trigger<>, public Component { void trigger_(); BinarySensor *parent_; - std::vector timing_; + FixedVector timing_; uint32_t invalid_cooldown_{1000}; optional at_index_{}; bool last_state_{false}; From e3aaf6a1440d066429b8b0465538f6f10b873a33 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 17:55:46 -1000 Subject: [PATCH 225/526] [wifi] Test multiple stas in wifi compile tests (#11460) --- tests/components/wifi/common.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/components/wifi/common.yaml b/tests/components/wifi/common.yaml index 343d44b177..af27f85092 100644 --- a/tests/components/wifi/common.yaml +++ b/tests/components/wifi/common.yaml @@ -12,5 +12,8 @@ esphome: - logger.log: "Failed to connect to WiFi!" wifi: - ssid: MySSID - password: password1 + networks: + - ssid: MySSID + password: password1 + - ssid: MySSID2 + password: password2 From 0de79ba29144a38ba4de245990dc033cc0cddd1c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 17:57:18 -1000 Subject: [PATCH 226/526] [event] Replace std::set with FixedVector for event type storage (#11463) --- esphome/components/event/event.cpp | 13 ++++++++++--- esphome/components/event/event.h | 7 +++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/esphome/components/event/event.cpp b/esphome/components/event/event.cpp index d27b3b378e..20549ad0a5 100644 --- a/esphome/components/event/event.cpp +++ b/esphome/components/event/event.cpp @@ -8,12 +8,19 @@ namespace event { static const char *const TAG = "event"; void Event::trigger(const std::string &event_type) { - auto found = types_.find(event_type); - if (found == types_.end()) { + // Linear search - faster than std::set for small datasets (1-5 items typical) + const std::string *found = nullptr; + for (const auto &type : this->types_) { + if (type == event_type) { + found = &type; + break; + } + } + if (found == nullptr) { ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str()); return; } - last_event_type = &(*found); + last_event_type = found; ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), last_event_type->c_str()); this->event_callback_.call(event_type); } diff --git a/esphome/components/event/event.h b/esphome/components/event/event.h index a90c8ebe05..2f6267a200 100644 --- a/esphome/components/event/event.h +++ b/esphome/components/event/event.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include "esphome/core/component.h" @@ -26,13 +25,13 @@ class Event : public EntityBase, public EntityBase_DeviceClass { const std::string *last_event_type; void trigger(const std::string &event_type); - void set_event_types(const std::set &event_types) { this->types_ = event_types; } - std::set get_event_types() const { return this->types_; } + void set_event_types(const std::initializer_list &event_types) { this->types_ = event_types; } + const FixedVector &get_event_types() const { return this->types_; } void add_on_event_callback(std::function &&callback); protected: CallbackManager event_callback_; - std::set types_; + FixedVector types_; }; } // namespace event From 5b15827009a1269df71754b850295580ad90e0eb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 17:58:40 -1000 Subject: [PATCH 227/526] [CI] Fix component detection when core files change in determine-jobs (#11461) --- script/determine-jobs.py | 24 ++++++------ tests/script/test_determine_jobs.py | 57 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 6651553ce7..7cdec959c7 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -606,21 +606,23 @@ def main() -> None: # [list]: Changed components (already includes dependencies) changed_components_result = get_changed_components() + # Always analyze component files, even if core files changed + # This is needed for component testing and memory impact analysis + changed = changed_files(args.branch) + component_files = [f for f in changed if filter_component_and_test_files(f)] + + directly_changed_components = get_components_with_dependencies( + component_files, False + ) + if changed_components_result is None: # Core files changed - will trigger full clang-tidy scan - # No specific components to test - changed_components = [] - directly_changed_components = [] + # But we still need to track changed components for testing and memory analysis + changed_components = get_components_with_dependencies(component_files, True) is_core_change = True else: - # Get both directly changed and all changed (with dependencies) - changed = changed_files(args.branch) - component_files = [f for f in changed if filter_component_and_test_files(f)] - - directly_changed_components = get_components_with_dependencies( - component_files, False - ) - changed_components = get_components_with_dependencies(component_files, True) + # Use the result from get_changed_components() which includes dependencies + changed_components = changed_components_result is_core_change = False # Filter to only components that have test files diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index a859b3c24d..6095e86ea7 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -910,3 +910,60 @@ def test_clang_tidy_mode_targeted_scan( output = json.loads(captured.out) assert output["clang_tidy_mode"] == expected_mode + + +def test_main_core_files_changed_still_detects_components( + mock_should_run_integration_tests: Mock, + mock_should_run_clang_tidy: Mock, + mock_should_run_clang_format: Mock, + mock_should_run_python_linters: Mock, + mock_changed_files: Mock, + mock_determine_cpp_unit_tests: Mock, + capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test that component changes are detected even when core files change.""" + monkeypatch.delenv("GITHUB_ACTIONS", raising=False) + + mock_should_run_integration_tests.return_value = True + mock_should_run_clang_tidy.return_value = True + mock_should_run_clang_format.return_value = True + mock_should_run_python_linters.return_value = True + mock_determine_cpp_unit_tests.return_value = (True, []) + + mock_changed_files.return_value = [ + "esphome/core/helpers.h", + "esphome/components/select/select_traits.h", + "esphome/components/select/select_traits.cpp", + "esphome/components/api/api.proto", + ] + + with ( + patch("sys.argv", ["determine-jobs.py"]), + patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=False), + patch.object(determine_jobs, "get_changed_components", return_value=None), + patch.object( + determine_jobs, + "filter_component_and_test_files", + side_effect=lambda f: f.startswith("esphome/components/"), + ), + patch.object( + determine_jobs, + "get_components_with_dependencies", + side_effect=lambda files, deps: ( + ["select", "api"] + if not deps + else ["select", "api", "bluetooth_proxy", "logger"] + ), + ), + ): + determine_jobs.main() + + captured = capsys.readouterr() + output = json.loads(captured.out) + + assert output["clang_tidy"] is True + assert output["clang_tidy_mode"] == "split" + assert "select" in output["changed_components"] + assert "api" in output["changed_components"] + assert len(output["changed_components"]) > 0 From 146b067d629ee0401fee030815ef7eaf89fe8e06 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 17:59:39 -1000 Subject: [PATCH 228/526] [light] Add compile test for addressable lights (#11465) --- tests/components/light/common.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/components/light/common.yaml b/tests/components/light/common.yaml index f807014065..247fc19aba 100644 --- a/tests/components/light/common.yaml +++ b/tests/components/light/common.yaml @@ -17,6 +17,20 @@ esphome: relative_brightness: 5% brightness_limits: max_brightness: 90% + - light.turn_on: + id: test_addressable_transition + brightness: 50% + red: 100% + green: 0% + blue: 0% + transition_length: 500ms + - light.turn_on: + id: test_addressable_transition + brightness: 100% + red: 0% + green: 100% + blue: 0% + transition_length: 1s light: - platform: binary @@ -163,3 +177,9 @@ light: blue: 0% duration: 1s transition_length: 500ms + - platform: partition + id: test_addressable_transition + name: Addressable Transition Test + default_transition_length: 1s + segments: + - single_light_id: test_rgb_light From e1c851cab88a408d9df25cd924caffd8db263f67 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Oct 2025 19:23:10 -1000 Subject: [PATCH 229/526] [wifi] Optimize WiFi network storage with FixedVector (#11458) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/wifi/__init__.py | 19 ++++++++++------- esphome/components/wifi/wifi_component.cpp | 2 ++ esphome/components/wifi/wifi_component.h | 3 ++- esphome/core/helpers.h | 24 ++++++++++++++++------ 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 494470cb48..29d33bfc76 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -378,14 +378,19 @@ async def to_code(config): # Track if any network uses Enterprise authentication has_eap = False - def add_sta(ap, network): - ip_config = network.get(CONF_MANUAL_IP, config.get(CONF_MANUAL_IP)) - cg.add(var.add_sta(wifi_network(network, ap, ip_config))) + # Initialize FixedVector with the count of networks + networks = config.get(CONF_NETWORKS, []) + if networks: + cg.add(var.init_sta(len(networks))) - for network in config.get(CONF_NETWORKS, []): - if CONF_EAP in network: - has_eap = True - cg.with_local_variable(network[CONF_ID], WiFiAP(), add_sta, network) + def add_sta(ap: cg.MockObj, network: dict) -> None: + ip_config = network.get(CONF_MANUAL_IP, config.get(CONF_MANUAL_IP)) + cg.add(var.add_sta(wifi_network(network, ap, ip_config))) + + for network in networks: + if CONF_EAP in network: + has_eap = True + cg.with_local_variable(network[CONF_ID], WiFiAP(), add_sta, network) if CONF_AP in config: conf = config[CONF_AP] diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index c89384d742..b278e5a386 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -330,9 +330,11 @@ float WiFiComponent::get_loop_priority() const { return 10.0f; // before other loop components } +void WiFiComponent::init_sta(size_t count) { this->sta_.init(count); } void WiFiComponent::add_sta(const WiFiAP &ap) { this->sta_.push_back(ap); } void WiFiComponent::set_sta(const WiFiAP &ap) { this->clear_sta(); + this->init_sta(1); this->add_sta(ap); } void WiFiComponent::clear_sta() { this->sta_.clear(); } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 10aa82a065..42f78dbfac 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -219,6 +219,7 @@ class WiFiComponent : public Component { void set_sta(const WiFiAP &ap); WiFiAP get_sta() { return this->selected_ap_; } + void init_sta(size_t count); void add_sta(const WiFiAP &ap); void clear_sta(); @@ -393,7 +394,7 @@ class WiFiComponent : public Component { #endif std::string use_address_; - std::vector sta_; + FixedVector sta_; std::vector sta_priorities_; wifi_scan_vector_t scan_result_; WiFiAP selected_ap_; diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 234d2a7d7d..9b0591c9c5 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -194,12 +194,8 @@ template class FixedVector { size_ = 0; } - public: - FixedVector() = default; - - /// Constructor from initializer list - allocates exact size needed - /// This enables brace initialization: FixedVector v = {1, 2, 3}; - FixedVector(std::initializer_list init_list) { + // Helper to assign from initializer list (shared by constructor and assignment operator) + void assign_from_initializer_list_(std::initializer_list init_list) { init(init_list.size()); size_t idx = 0; for (const auto &item : init_list) { @@ -209,6 +205,13 @@ template class FixedVector { size_ = init_list.size(); } + public: + FixedVector() = default; + + /// Constructor from initializer list - allocates exact size needed + /// This enables brace initialization: FixedVector v = {1, 2, 3}; + FixedVector(std::initializer_list init_list) { assign_from_initializer_list_(init_list); } + ~FixedVector() { cleanup_(); } // Disable copy operations (avoid accidental expensive copies) @@ -234,6 +237,15 @@ template class FixedVector { return *this; } + /// Assignment from initializer list - avoids temporary and move overhead + /// This enables: FixedVector v; v = {1, 2, 3}; + FixedVector &operator=(std::initializer_list init_list) { + cleanup_(); + reset_(); + assign_from_initializer_list_(init_list); + return *this; + } + // Allocate capacity - can be called multiple times to reinit void init(size_t n) { cleanup_(); From e2b3617df32cd4c3696eed1aac4ddfcc38aa42a6 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 22 Oct 2025 01:08:40 -0700 Subject: [PATCH 230/526] [climate] Fix restore state for fan mode, preset, and swing mode (#11126) Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston --- esphome/components/climate/climate.cpp | 60 ++++++++++++-------------- esphome/components/climate/climate.h | 1 + 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 87d03f78c5..19fe241729 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -524,13 +524,23 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) { if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { call.set_target_humidity(this->target_humidity); } - if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) { + if (this->uses_custom_fan_mode) { + if (this->custom_fan_mode < traits.get_supported_custom_fan_modes().size()) { + call.fan_mode_.reset(); + call.custom_fan_mode_ = *std::next(traits.get_supported_custom_fan_modes().cbegin(), this->custom_fan_mode); + } + } else if (traits.supports_fan_mode(this->fan_mode)) { call.set_fan_mode(this->fan_mode); } - if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) { + if (this->uses_custom_preset) { + if (this->custom_preset < traits.get_supported_custom_presets().size()) { + call.preset_.reset(); + call.custom_preset_ = *std::next(traits.get_supported_custom_presets().cbegin(), this->custom_preset); + } + } else if (traits.supports_preset(this->preset)) { call.set_preset(this->preset); } - if (traits.get_supports_swing_modes()) { + if (traits.supports_swing_mode(this->swing_mode)) { call.set_swing_mode(this->swing_mode); } return call; @@ -549,41 +559,25 @@ void ClimateDeviceRestoreState::apply(Climate *climate) { if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { climate->target_humidity = this->target_humidity; } - if (traits.get_supports_fan_modes() && !this->uses_custom_fan_mode) { + if (this->uses_custom_fan_mode) { + if (this->custom_fan_mode < traits.get_supported_custom_fan_modes().size()) { + climate->fan_mode.reset(); + climate->custom_fan_mode = *std::next(traits.get_supported_custom_fan_modes().cbegin(), this->custom_fan_mode); + } + } else if (traits.supports_fan_mode(this->fan_mode)) { climate->fan_mode = this->fan_mode; + climate->custom_fan_mode.reset(); } - if (!traits.get_supported_custom_fan_modes().empty() && this->uses_custom_fan_mode) { - // std::set has consistent order (lexicographic for strings) - const auto &modes = traits.get_supported_custom_fan_modes(); - if (custom_fan_mode < modes.size()) { - size_t i = 0; - for (const auto &mode : modes) { - if (i == this->custom_fan_mode) { - climate->custom_fan_mode = mode; - break; - } - i++; - } + if (this->uses_custom_preset) { + if (this->custom_preset < traits.get_supported_custom_presets().size()) { + climate->preset.reset(); + climate->custom_preset = *std::next(traits.get_supported_custom_presets().cbegin(), this->custom_preset); } - } - if (traits.get_supports_presets() && !this->uses_custom_preset) { + } else if (traits.supports_preset(this->preset)) { climate->preset = this->preset; + climate->custom_preset.reset(); } - if (!traits.get_supported_custom_presets().empty() && uses_custom_preset) { - // std::set has consistent order (lexicographic for strings) - const auto &presets = traits.get_supported_custom_presets(); - if (custom_preset < presets.size()) { - size_t i = 0; - for (const auto &preset : presets) { - if (i == this->custom_preset) { - climate->custom_preset = preset; - break; - } - i++; - } - } - } - if (traits.get_supports_swing_modes()) { + if (traits.supports_swing_mode(this->swing_mode)) { climate->swing_mode = this->swing_mode; } climate->publish_state(); diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index 495464c6a2..0c3e3ebe16 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -33,6 +33,7 @@ class Climate; class ClimateCall { public: explicit ClimateCall(Climate *parent) : parent_(parent) {} + friend struct ClimateDeviceRestoreState; /// Set the mode of the climate device. ClimateCall &set_mode(ClimateMode mode); From d37eb59fd733b3dd06b2e62409249e92bde5b7b8 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 22 Oct 2025 01:22:33 -0700 Subject: [PATCH 231/526] [light] Eliminate dimming undershoot during addressable light transition (#11471) --- .../components/light/addressable_light.cpp | 61 ++++++++++--------- esphome/components/light/addressable_light.h | 1 - 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/esphome/components/light/addressable_light.cpp b/esphome/components/light/addressable_light.cpp index cd83015ecb..5cbdcb0e86 100644 --- a/esphome/components/light/addressable_light.cpp +++ b/esphome/components/light/addressable_light.cpp @@ -61,6 +61,10 @@ void AddressableLightTransformer::start() { this->target_color_ *= to_uint8_scale(end_values.get_brightness() * end_values.get_state()); } +inline constexpr uint8_t subtract_scaled_difference(uint8_t a, uint8_t b, int32_t scale) { + return uint8_t(int32_t(a) - (((int32_t(a) - int32_t(b)) * scale) / 256)); +} + optional AddressableLightTransformer::apply() { float smoothed_progress = LightTransformer::smoothed_progress(this->get_progress_()); @@ -74,38 +78,37 @@ optional AddressableLightTransformer::apply() { // all LEDs, we use the current state of each LED as the start. // We can't use a direct lerp smoothing here though - that would require creating a copy of the original - // state of each LED at the start of the transition. - // Instead, we "fake" the look of the LERP by using an exponential average over time and using - // dynamically-calculated alpha values to match the look. + // state of each LED at the start of the transition. Instead, we "fake" the look of lerp by calculating + // the delta between the current state and the target state, assuming that the delta represents the rest + // of the transition that was to be applied as of the previous transition step, and scaling the delta for + // what should be left after the current transition step. In this manner, the delta decays to zero as the + // transition progresses. + // + // Here's an example of how the algorithm progresses in discrete steps: + // + // At time = 0.00, 0% complete, 100% remaining, 100% will remain after this step, so the scale is 100% / 100% = 100%. + // At time = 0.10, 0% complete, 100% remaining, 90% will remain after this step, so the scale is 90% / 100% = 90%. + // At time = 0.20, 10% complete, 90% remaining, 80% will remain after this step, so the scale is 80% / 90% = 88.9%. + // At time = 0.50, 20% complete, 80% remaining, 50% will remain after this step, so the scale is 50% / 80% = 62.5%. + // At time = 0.90, 50% complete, 50% remaining, 10% will remain after this step, so the scale is 10% / 50% = 20%. + // At time = 0.91, 90% complete, 10% remaining, 9% will remain after this step, so the scale is 9% / 10% = 90%. + // At time = 1.00, 91% complete, 9% remaining, 0% will remain after this step, so the scale is 0% / 9% = 0%. + // + // Because the color values are quantized to 8 bit resolution after each step, the transition may appear + // non-linear when applying small deltas. - float denom = (1.0f - smoothed_progress); - float alpha = denom == 0.0f ? 1.0f : (smoothed_progress - this->last_transition_progress_) / denom; - - // We need to use a low-resolution alpha here which makes the transition set in only after ~half of the length - // We solve this by accumulating the fractional part of the alpha over time. - float alpha255 = alpha * 255.0f; - float alpha255int = floorf(alpha255); - float alpha255remainder = alpha255 - alpha255int; - - this->accumulated_alpha_ += alpha255remainder; - float alpha_add = floorf(this->accumulated_alpha_); - this->accumulated_alpha_ -= alpha_add; - - alpha255 += alpha_add; - alpha255 = clamp(alpha255, 0.0f, 255.0f); - auto alpha8 = static_cast(alpha255); - - if (alpha8 != 0) { - uint8_t inv_alpha8 = 255 - alpha8; - Color add = this->target_color_ * alpha8; - - for (auto led : this->light_) - led.set(add + led.get() * inv_alpha8); + if (smoothed_progress > this->last_transition_progress_ && this->last_transition_progress_ < 1.f) { + int32_t scale = int32_t(256.f * std::max((1.f - smoothed_progress) / (1.f - this->last_transition_progress_), 0.f)); + for (auto led : this->light_) { + led.set_rgbw(subtract_scaled_difference(this->target_color_.red, led.get_red(), scale), + subtract_scaled_difference(this->target_color_.green, led.get_green(), scale), + subtract_scaled_difference(this->target_color_.blue, led.get_blue(), scale), + subtract_scaled_difference(this->target_color_.white, led.get_white(), scale)); + } + this->last_transition_progress_ = smoothed_progress; + this->light_.schedule_show(); } - this->last_transition_progress_ = smoothed_progress; - this->light_.schedule_show(); - return {}; } diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index c8ed4897fa..393cc679bc 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -113,7 +113,6 @@ class AddressableLightTransformer : public LightTransformer { protected: AddressableLight &light_; float last_transition_progress_{0.0f}; - float accumulated_alpha_{0.0f}; Color target_color_{}; }; From 6edbb945295188f94950288a90a8dad45171464c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Oct 2025 00:06:14 -1000 Subject: [PATCH 232/526] [ci] Fix test detection for components with only variant tests (#11474) --- script/determine-jobs.py | 4 +- tests/script/test_determine_jobs.py | 144 ++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 2 deletions(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 7cdec959c7..ac384d74f1 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -336,7 +336,7 @@ def _component_has_tests(component: str) -> bool: Returns: True if the component has test YAML files """ - return bool(get_component_test_files(component)) + return bool(get_component_test_files(component, all_variants=True)) def _select_platform_by_preference( @@ -496,7 +496,7 @@ def detect_memory_impact_config( for component in sorted(changed_component_set): # Look for test files on preferred platforms - test_files = get_component_test_files(component) + test_files = get_component_test_files(component, all_variants=True) if not test_files: continue diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 6095e86ea7..c9ccf53252 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -574,6 +574,105 @@ def test_main_filters_components_without_tests( assert output["memory_impact"]["should_run"] == "false" +def test_main_detects_components_with_variant_tests( + mock_should_run_integration_tests: Mock, + mock_should_run_clang_tidy: Mock, + mock_should_run_clang_format: Mock, + mock_should_run_python_linters: Mock, + mock_changed_files: Mock, + capsys: pytest.CaptureFixture[str], + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test that components with only variant test files (test-*.yaml) are detected. + + This test verifies the fix for components like improv_serial, ethernet, mdns, + improv_base, and safe_mode which only have variant test files (test-*.yaml) + instead of base test files (test.*.yaml). + """ + # Ensure we're not in GITHUB_ACTIONS mode for this test + monkeypatch.delenv("GITHUB_ACTIONS", raising=False) + + mock_should_run_integration_tests.return_value = False + mock_should_run_clang_tidy.return_value = False + mock_should_run_clang_format.return_value = False + mock_should_run_python_linters.return_value = False + + # Mock changed_files to return component files + mock_changed_files.return_value = [ + "esphome/components/improv_serial/improv_serial.cpp", + "esphome/components/ethernet/ethernet.cpp", + "esphome/components/no_tests/component.cpp", + ] + + # Create test directory structure + tests_dir = tmp_path / "tests" / "components" + + # improv_serial has only variant tests (like the real component) + improv_serial_dir = tests_dir / "improv_serial" + improv_serial_dir.mkdir(parents=True) + (improv_serial_dir / "test-uart0.esp32-idf.yaml").write_text("test: config") + (improv_serial_dir / "test-uart0.esp8266-ard.yaml").write_text("test: config") + (improv_serial_dir / "test-usb_cdc.esp32-s2-idf.yaml").write_text("test: config") + + # ethernet also has only variant tests + ethernet_dir = tests_dir / "ethernet" + ethernet_dir.mkdir(parents=True) + (ethernet_dir / "test-manual_ip.esp32-idf.yaml").write_text("test: config") + (ethernet_dir / "test-dhcp.esp32-idf.yaml").write_text("test: config") + + # no_tests component has no test files at all + no_tests_dir = tests_dir / "no_tests" + no_tests_dir.mkdir(parents=True) + + # Mock root_path to use tmp_path (need to patch both determine_jobs and helpers) + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch("sys.argv", ["determine-jobs.py"]), + patch.object( + determine_jobs, + "get_changed_components", + return_value=["improv_serial", "ethernet", "no_tests"], + ), + patch.object( + determine_jobs, + "filter_component_and_test_files", + side_effect=lambda f: f.startswith("esphome/components/"), + ), + patch.object( + determine_jobs, + "get_components_with_dependencies", + side_effect=lambda files, deps: ( + ["improv_serial", "ethernet"] + if not deps + else ["improv_serial", "ethernet", "no_tests"] + ), + ), + patch.object(determine_jobs, "changed_files", return_value=[]), + ): + # Clear the cache since we're mocking root_path + determine_jobs._component_has_tests.cache_clear() + determine_jobs.main() + + # Check output + captured = capsys.readouterr() + output = json.loads(captured.out) + + # changed_components should have all components + assert set(output["changed_components"]) == { + "improv_serial", + "ethernet", + "no_tests", + } + # changed_components_with_tests should include components with variant tests + assert set(output["changed_components_with_tests"]) == {"improv_serial", "ethernet"} + # component_test_count should be 2 (improv_serial and ethernet) + assert output["component_test_count"] == 2 + # no_tests should be excluded since it has no test files + assert "no_tests" not in output["changed_components_with_tests"] + + # Tests for detect_memory_impact_config function @@ -785,6 +884,51 @@ def test_detect_memory_impact_config_skips_base_bus_components(tmp_path: Path) - assert "i2c" not in result["components"] +def test_detect_memory_impact_config_with_variant_tests(tmp_path: Path) -> None: + """Test memory impact detection for components with only variant test files. + + This verifies that memory impact analysis works correctly for components like + improv_serial, ethernet, mdns, etc. which only have variant test files + (test-*.yaml) instead of base test files (test.*.yaml). + """ + # Create test directory structure + tests_dir = tmp_path / "tests" / "components" + + # improv_serial with only variant tests + improv_serial_dir = tests_dir / "improv_serial" + improv_serial_dir.mkdir(parents=True) + (improv_serial_dir / "test-uart0.esp32-idf.yaml").write_text("test: improv") + (improv_serial_dir / "test-uart0.esp8266-ard.yaml").write_text("test: improv") + (improv_serial_dir / "test-usb_cdc.esp32-s2-idf.yaml").write_text("test: improv") + + # ethernet with only variant tests + ethernet_dir = tests_dir / "ethernet" + ethernet_dir.mkdir(parents=True) + (ethernet_dir / "test-manual_ip.esp32-idf.yaml").write_text("test: ethernet") + (ethernet_dir / "test-dhcp.esp32-c3-idf.yaml").write_text("test: ethernet") + + # Mock changed_files to return both components + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + ): + mock_changed_files.return_value = [ + "esphome/components/improv_serial/improv_serial.cpp", + "esphome/components/ethernet/ethernet.cpp", + ] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + # Should detect both components even though they only have variant tests + assert result["should_run"] == "true" + assert set(result["components"]) == {"improv_serial", "ethernet"} + # Both components support esp32-idf + assert result["platform"] == "esp32-idf" + assert result["use_merged_config"] == "true" + + # Tests for clang-tidy split mode logic From f592f79bcece7edb4810f09ebdb6e3f25267b398 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Oct 2025 07:30:27 -1000 Subject: [PATCH 233/526] [ci] Fix component splitter for components with only variant tests (#11476) --- script/split_components_for_ci.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/split_components_for_ci.py b/script/split_components_for_ci.py index 6ba2598eda..c58dfd218f 100755 --- a/script/split_components_for_ci.py +++ b/script/split_components_for_ci.py @@ -49,9 +49,9 @@ def has_test_files(component_name: str, tests_dir: Path) -> bool: tests_dir: Path to tests/components directory (unused, kept for compatibility) Returns: - True if the component has test.*.yaml files + True if the component has test.*.yaml or test-*.yaml files """ - return bool(get_component_test_files(component_name)) + return bool(get_component_test_files(component_name, all_variants=True)) def create_intelligent_batches( From 77141d3e83e1bb11107255423556373ba588d3ca Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:28:18 -0400 Subject: [PATCH 234/526] [esp32] Set the location of the IDF component manager cache (#11467) --- esphome/components/esp32/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index cb6354cc74..48d11f46fa 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -877,6 +877,11 @@ async def to_code(config): for clean_var in ("IDF_PATH", "IDF_TOOLS_PATH"): os.environ.pop(clean_var, None) + # Set the location of the IDF component manager cache + os.environ["IDF_COMPONENT_CACHE_PATH"] = str( + CORE.relative_internal_path(".espressif") + ) + add_extra_script( "post", "post_build.py", From 1c67a619459c58fd855771defec3389ae41603f3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Oct 2025 09:10:24 -1000 Subject: [PATCH 235/526] [ci] Fix WiFi testing mode validation and component splitter for variant-only tests (#11481) --- esphome/components/wifi/__init__.py | 14 +++++++++----- script/split_components_for_ci.py | 9 +++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 29d33bfc76..ba488728b7 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -213,11 +213,15 @@ def _validate(config): if CONF_EAP in config: network[CONF_EAP] = config.pop(CONF_EAP) if CONF_NETWORKS in config: - raise cv.Invalid( - "You cannot use the 'ssid:' option together with 'networks:'. Please " - "copy your network into the 'networks:' key" - ) - config[CONF_NETWORKS] = cv.ensure_list(WIFI_NETWORK_STA)(network) + # In testing mode, merged component tests may have both ssid and networks + # Just use the networks list and ignore the single ssid + if not CORE.testing_mode: + raise cv.Invalid( + "You cannot use the 'ssid:' option together with 'networks:'. Please " + "copy your network into the 'networks:' key" + ) + else: + config[CONF_NETWORKS] = cv.ensure_list(WIFI_NETWORK_STA)(network) if (CONF_NETWORKS not in config) and (CONF_AP not in config): config = config.copy() diff --git a/script/split_components_for_ci.py b/script/split_components_for_ci.py index c58dfd218f..87da540d43 100755 --- a/script/split_components_for_ci.py +++ b/script/split_components_for_ci.py @@ -118,8 +118,13 @@ def create_intelligent_batches( continue # Get signature from any platform (they should all have the same buses) - # Components not in component_buses were filtered out by has_test_files check - comp_platforms = component_buses[component] + # Components not in component_buses may only have variant-specific tests + comp_platforms = component_buses.get(component) + if not comp_platforms: + # Component has tests but no analyzable base config - treat as no buses + signature_groups[(ALL_PLATFORMS, NO_BUSES_SIGNATURE)].append(component) + continue + for platform, buses in comp_platforms.items(): if buses: signature = create_grouping_signature({platform: buses}, platform) From f2de8df556d01b9faba6ab7f732df39bfe3c4ea9 Mon Sep 17 00:00:00 2001 From: Daniel Stiner Date: Wed, 22 Oct 2025 14:07:01 -0700 Subject: [PATCH 236/526] [openthread] Fix OTA by populating CORE.address with device's mDNS address (#11095) Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- esphome/components/network/util.cpp | 6 +++++- esphome/components/openthread/__init__.py | 13 ++++++++++++- esphome/components/openthread/openthread.cpp | 6 ++++++ esphome/components/openthread/openthread.h | 4 ++++ esphome/core/__init__.py | 8 +++----- tests/unit_tests/test_core.py | 6 ++++-- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp index 27ad9448a4..cb8f8569ad 100644 --- a/esphome/components/network/util.cpp +++ b/esphome/components/network/util.cpp @@ -99,7 +99,11 @@ const std::string &get_use_address() { return wifi::global_wifi_component->get_use_address(); #endif -#if !defined(USE_ETHERNET) && !defined(USE_MODEM) && !defined(USE_WIFI) +#ifdef USE_OPENTHREAD + return openthread::global_openthread_component->get_use_address(); +#endif + +#if !defined(USE_ETHERNET) && !defined(USE_MODEM) && !defined(USE_WIFI) && !defined(USE_OPENTHREAD) // Fallback when no network component is defined (e.g., host platform) static const std::string empty; return empty; diff --git a/esphome/components/openthread/__init__.py b/esphome/components/openthread/__init__.py index 4865399d02..572ec144d4 100644 --- a/esphome/components/openthread/__init__.py +++ b/esphome/components/openthread/__init__.py @@ -8,8 +8,10 @@ from esphome.components.esp32 import ( ) from esphome.components.mdns import MDNSComponent, enable_mdns_storage import esphome.config_validation as cv -from esphome.const import CONF_CHANNEL, CONF_ENABLE_IPV6, CONF_ID +from esphome.const import CONF_CHANNEL, CONF_ENABLE_IPV6, CONF_ID, CONF_USE_ADDRESS +from esphome.core import CORE import esphome.final_validate as fv +from esphome.types import ConfigType from .const import ( CONF_DEVICE_TYPE, @@ -108,6 +110,12 @@ _CONNECTION_SCHEMA = cv.Schema( ) +def _validate(config: ConfigType) -> ConfigType: + if CONF_USE_ADDRESS not in config: + config[CONF_USE_ADDRESS] = f"{CORE.name}.local" + return config + + def _require_vfs_select(config): """Register VFS select requirement during config validation.""" # OpenThread uses esp_vfs_eventfd which requires VFS select support @@ -126,11 +134,13 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_FORCE_DATASET): cv.boolean, cv.Optional(CONF_TLV): cv.string_strict, + cv.Optional(CONF_USE_ADDRESS): cv.string_strict, } ).extend(_CONNECTION_SCHEMA), cv.has_exactly_one_key(CONF_NETWORK_KEY, CONF_TLV), cv.only_with_esp_idf, only_on_variant(supported=[VARIANT_ESP32C6, VARIANT_ESP32H2]), + _validate, _require_vfs_select, ) @@ -155,6 +165,7 @@ async def to_code(config): enable_mdns_storage() ot = cg.new_Pvariable(config[CONF_ID]) + cg.add(ot.set_use_address(config[CONF_USE_ADDRESS])) await cg.register_component(ot, config) srp = cg.new_Pvariable(config[CONF_SRP_ID]) diff --git a/esphome/components/openthread/openthread.cpp b/esphome/components/openthread/openthread.cpp index b2c2519c08..db909e6b1f 100644 --- a/esphome/components/openthread/openthread.cpp +++ b/esphome/components/openthread/openthread.cpp @@ -252,6 +252,12 @@ void OpenThreadComponent::on_factory_reset(std::function callback) { ESP_LOGD(TAG, "Waiting on Confirmation Removal SRP Host and Services"); } +// set_use_address() is guaranteed to be called during component setup by Python code generation, +// so use_address_ will always be valid when get_use_address() is called - no fallback needed. +const std::string &OpenThreadComponent::get_use_address() const { return this->use_address_; } + +void OpenThreadComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } + } // namespace openthread } // namespace esphome diff --git a/esphome/components/openthread/openthread.h b/esphome/components/openthread/openthread.h index 5d139c633d..19dbeb4628 100644 --- a/esphome/components/openthread/openthread.h +++ b/esphome/components/openthread/openthread.h @@ -33,11 +33,15 @@ class OpenThreadComponent : public Component { void on_factory_reset(std::function callback); void defer_factory_reset_external_callback(); + const std::string &get_use_address() const; + void set_use_address(const std::string &use_address); + protected: std::optional get_omr_address_(InstanceLock &lock); bool teardown_started_{false}; bool teardown_complete_{false}; std::function factory_reset_external_callback_; + std::string use_address_; }; extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 2d49d29c5e..fed5265d6b 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -636,11 +636,9 @@ class EsphomeCore: if self.config is None: raise ValueError("Config has not been loaded yet") - if CONF_WIFI in self.config: - return self.config[CONF_WIFI][CONF_USE_ADDRESS] - - if CONF_ETHERNET in self.config: - return self.config[CONF_ETHERNET][CONF_USE_ADDRESS] + for network_type in (CONF_WIFI, CONF_ETHERNET, CONF_OPENTHREAD): + if network_type in self.config: + return self.config[network_type][CONF_USE_ADDRESS] if CONF_OPENTHREAD in self.config: return f"{self.name}.local" diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index 41114ae18b..92b60efd93 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -571,9 +571,11 @@ class TestEsphomeCore: assert target.address == "4.3.2.1" def test_address__openthread(self, target): - target.name = "test-device" target.config = {} - target.config[const.CONF_OPENTHREAD] = {} + target.config[const.CONF_OPENTHREAD] = { + const.CONF_USE_ADDRESS: "test-device.local" + } + target.name = "test-device" assert target.address == "test-device.local" From 7f567bdfbe172b3fc0c60a7bd5b1d6eec4104746 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Oct 2025 11:53:15 -1000 Subject: [PATCH 237/526] [fan] Add basic fan compile tests (#11484) --- tests/components/fan/common.yaml | 11 +++++++++++ tests/components/fan/test.esp8266-ard.yaml | 1 + 2 files changed, 12 insertions(+) create mode 100644 tests/components/fan/common.yaml create mode 100644 tests/components/fan/test.esp8266-ard.yaml diff --git a/tests/components/fan/common.yaml b/tests/components/fan/common.yaml new file mode 100644 index 0000000000..55c2a656fd --- /dev/null +++ b/tests/components/fan/common.yaml @@ -0,0 +1,11 @@ +fan: + - platform: template + id: test_fan + name: "Test Fan" + preset_modes: + - Eco + - Sleep + - Turbo + has_oscillating: true + has_direction: true + speed_count: 3 diff --git a/tests/components/fan/test.esp8266-ard.yaml b/tests/components/fan/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/fan/test.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From b91b12d77a28236644970051d576c0c4b284b564 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 23 Oct 2025 02:55:34 +0200 Subject: [PATCH 238/526] [nrf52] support BLE --device for logging (#9861) Co-authored-by: J. Nick Koston --- esphome/components/nrf52/__init__.py | 17 ++++++++ esphome/components/nrf52/ble_logger.py | 60 ++++++++++++++++++++++++++ esphome/components/zephyr/__init__.py | 3 ++ requirements.txt | 1 + 4 files changed, 81 insertions(+) create mode 100644 esphome/components/nrf52/ble_logger.py diff --git a/esphome/components/nrf52/__init__.py b/esphome/components/nrf52/__init__.py index 727607933d..27e1246744 100644 --- a/esphome/components/nrf52/__init__.py +++ b/esphome/components/nrf52/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations +import asyncio import logging from pathlib import Path @@ -277,3 +278,19 @@ def upload_program(config: ConfigType, args, host: str) -> bool: raise EsphomeError(f"Upload failed with result: {result}") return handled + + +def show_logs(config: ConfigType, args, devices: list[str]) -> bool: + address = devices[0] + from .ble_logger import is_mac_address, logger_connect, logger_scan + + if devices[0] == "BLE": + ble_device = asyncio.run(logger_scan(CORE.config["esphome"]["name"])) + if ble_device: + address = ble_device.address + else: + return True + if is_mac_address(address): + asyncio.run(logger_connect(address)) + return True + return False diff --git a/esphome/components/nrf52/ble_logger.py b/esphome/components/nrf52/ble_logger.py new file mode 100644 index 0000000000..f74a49ea89 --- /dev/null +++ b/esphome/components/nrf52/ble_logger.py @@ -0,0 +1,60 @@ +import asyncio +import logging +import re +from typing import Final + +from bleak import BleakClient, BleakScanner, BLEDevice +from bleak.exc import ( + BleakCharacteristicNotFoundError, + BleakDBusError, + BleakDeviceNotFoundError, +) + +_LOGGER = logging.getLogger(__name__) + + +NUS_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" +NUS_TX_CHAR_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" + +MAC_ADDRESS_PATTERN: Final = re.compile( + r"([0-9A-F]{2}[:]){5}[0-9A-F]{2}$", flags=re.IGNORECASE +) + + +def is_mac_address(value: str) -> bool: + return MAC_ADDRESS_PATTERN.match(value) + + +async def logger_scan(name: str) -> BLEDevice | None: + _LOGGER.info("Scanning bluetooth for %s...", name) + device = await BleakScanner.find_device_by_name(name) + if not device: + _LOGGER.error("%s Bluetooth LE device was not found!", name) + return device + + +async def logger_connect(host: str) -> int | None: + disconnected_event = asyncio.Event() + + def handle_disconnect(client): + disconnected_event.set() + + def handle_rx(_, data: bytearray): + print(data.decode("utf-8"), end="") + + _LOGGER.info("Connecting %s...", host) + try: + async with BleakClient(host, disconnected_callback=handle_disconnect) as client: + _LOGGER.info("Connected %s...", host) + try: + await client.start_notify(NUS_TX_CHAR_UUID, handle_rx) + except BleakDBusError as e: + _LOGGER.error("Bluetooth LE logger: %s", e) + disconnected_event.set() + await disconnected_event.wait() + except BleakDeviceNotFoundError: + _LOGGER.error("Device %s not found", host) + return 1 + except BleakCharacteristicNotFoundError: + _LOGGER.error("Device %s has no NUS characteristic", host) + return 1 diff --git a/esphome/components/zephyr/__init__.py b/esphome/components/zephyr/__init__.py index 634c99876b..a2fb12a5e2 100644 --- a/esphome/components/zephyr/__init__.py +++ b/esphome/components/zephyr/__init__.py @@ -234,6 +234,9 @@ def copy_files(): "url": "https://esphome.io/", "vendor": "esphome", "build": { + "bsp": { + "name": "adafruit" + }, "softdevice": { "sd_fwid": "0x00B6" } diff --git a/requirements.txt b/requirements.txt index ec7794c75a..6966ebe583 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,6 +22,7 @@ pillow==11.3.0 cairosvg==2.8.2 freetype-py==2.5.1 jinja2==3.1.6 +bleak==1.0.1 # esp-idf >= 5.0 requires this pyparsing >= 3.0 From f2f6c597ef3768a37507acb5c4831ad7e858cf6f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Oct 2025 15:17:57 -1000 Subject: [PATCH 239/526] [light] Store effect names in flash (const char*) to save RAM (#11487) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../adalight/adalight_light_effect.cpp | 2 +- .../adalight/adalight_light_effect.h | 2 +- esphome/components/e131/e131.cpp | 8 ++++---- .../e131/e131_addressable_light_effect.cpp | 6 +++--- .../e131/e131_addressable_light_effect.h | 2 +- .../light/addressable_light_effect.h | 19 +++++++++---------- esphome/components/light/base_light_effects.h | 12 ++++++------ esphome/components/light/light_call.cpp | 4 ++-- esphome/components/light/light_effect.h | 12 +++++++----- esphome/components/light/light_state.h | 2 +- esphome/components/wled/wled_light_effect.cpp | 2 +- esphome/components/wled/wled_light_effect.h | 2 +- 12 files changed, 37 insertions(+), 36 deletions(-) diff --git a/esphome/components/adalight/adalight_light_effect.cpp b/esphome/components/adalight/adalight_light_effect.cpp index 35e98d7360..4cf639a01f 100644 --- a/esphome/components/adalight/adalight_light_effect.cpp +++ b/esphome/components/adalight/adalight_light_effect.cpp @@ -9,7 +9,7 @@ static const char *const TAG = "adalight_light_effect"; static const uint32_t ADALIGHT_ACK_INTERVAL = 1000; static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000; -AdalightLightEffect::AdalightLightEffect(const std::string &name) : AddressableLightEffect(name) {} +AdalightLightEffect::AdalightLightEffect(const char *name) : AddressableLightEffect(name) {} void AdalightLightEffect::start() { AddressableLightEffect::start(); diff --git a/esphome/components/adalight/adalight_light_effect.h b/esphome/components/adalight/adalight_light_effect.h index 72faf44269..bb7319c99c 100644 --- a/esphome/components/adalight/adalight_light_effect.h +++ b/esphome/components/adalight/adalight_light_effect.h @@ -11,7 +11,7 @@ namespace adalight { class AdalightLightEffect : public light::AddressableLightEffect, public uart::UARTDevice { public: - AdalightLightEffect(const std::string &name); + AdalightLightEffect(const char *name); void start() override; void stop() override; diff --git a/esphome/components/e131/e131.cpp b/esphome/components/e131/e131.cpp index a74fc9be4a..d18d945cec 100644 --- a/esphome/components/e131/e131.cpp +++ b/esphome/components/e131/e131.cpp @@ -80,8 +80,8 @@ void E131Component::add_effect(E131AddressableLightEffect *light_effect) { return; } - ESP_LOGD(TAG, "Registering '%s' for universes %d-%d.", light_effect->get_name().c_str(), - light_effect->get_first_universe(), light_effect->get_last_universe()); + ESP_LOGD(TAG, "Registering '%s' for universes %d-%d.", light_effect->get_name(), light_effect->get_first_universe(), + light_effect->get_last_universe()); light_effects_.insert(light_effect); @@ -95,8 +95,8 @@ void E131Component::remove_effect(E131AddressableLightEffect *light_effect) { return; } - ESP_LOGD(TAG, "Unregistering '%s' for universes %d-%d.", light_effect->get_name().c_str(), - light_effect->get_first_universe(), light_effect->get_last_universe()); + ESP_LOGD(TAG, "Unregistering '%s' for universes %d-%d.", light_effect->get_name(), light_effect->get_first_universe(), + light_effect->get_last_universe()); light_effects_.erase(light_effect); diff --git a/esphome/components/e131/e131_addressable_light_effect.cpp b/esphome/components/e131/e131_addressable_light_effect.cpp index 4d1f98ab6c..780e181f04 100644 --- a/esphome/components/e131/e131_addressable_light_effect.cpp +++ b/esphome/components/e131/e131_addressable_light_effect.cpp @@ -9,7 +9,7 @@ namespace e131 { static const char *const TAG = "e131_addressable_light_effect"; static const int MAX_DATA_SIZE = (sizeof(E131Packet::values) - 1); -E131AddressableLightEffect::E131AddressableLightEffect(const std::string &name) : AddressableLightEffect(name) {} +E131AddressableLightEffect::E131AddressableLightEffect(const char *name) : AddressableLightEffect(name) {} int E131AddressableLightEffect::get_data_per_universe() const { return get_lights_per_universe() * channels_; } @@ -58,8 +58,8 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet std::min(it->size(), std::min(output_offset + get_lights_per_universe(), output_offset + packet.count - 1)); auto *input_data = packet.values + 1; - ESP_LOGV(TAG, "Applying data for '%s' on %d universe, for %" PRId32 "-%d.", get_name().c_str(), universe, - output_offset, output_end); + ESP_LOGV(TAG, "Applying data for '%s' on %d universe, for %" PRId32 "-%d.", get_name(), universe, output_offset, + output_end); switch (channels_) { case E131_MONO: diff --git a/esphome/components/e131/e131_addressable_light_effect.h b/esphome/components/e131/e131_addressable_light_effect.h index 17d7bd2829..381e08163b 100644 --- a/esphome/components/e131/e131_addressable_light_effect.h +++ b/esphome/components/e131/e131_addressable_light_effect.h @@ -13,7 +13,7 @@ enum E131LightChannels { E131_MONO = 1, E131_RGB = 3, E131_RGBW = 4 }; class E131AddressableLightEffect : public light::AddressableLightEffect { public: - E131AddressableLightEffect(const std::string &name); + E131AddressableLightEffect(const char *name); void start() override; void stop() override; diff --git a/esphome/components/light/addressable_light_effect.h b/esphome/components/light/addressable_light_effect.h index 9caccad634..9840112040 100644 --- a/esphome/components/light/addressable_light_effect.h +++ b/esphome/components/light/addressable_light_effect.h @@ -30,7 +30,7 @@ inline static uint8_t half_sin8(uint8_t v) { return sin16_c(uint16_t(v) * 128u) class AddressableLightEffect : public LightEffect { public: - explicit AddressableLightEffect(const std::string &name) : LightEffect(name) {} + explicit AddressableLightEffect(const char *name) : LightEffect(name) {} void start_internal() override { this->get_addressable_()->set_effect_active(true); this->get_addressable_()->clear_effect_data(); @@ -57,8 +57,7 @@ class AddressableLightEffect : public LightEffect { class AddressableLambdaLightEffect : public AddressableLightEffect { public: - AddressableLambdaLightEffect(const std::string &name, - std::function f, + AddressableLambdaLightEffect(const char *name, std::function f, uint32_t update_interval) : AddressableLightEffect(name), f_(std::move(f)), update_interval_(update_interval) {} void start() override { this->initial_run_ = true; } @@ -81,7 +80,7 @@ class AddressableLambdaLightEffect : public AddressableLightEffect { class AddressableRainbowLightEffect : public AddressableLightEffect { public: - explicit AddressableRainbowLightEffect(const std::string &name) : AddressableLightEffect(name) {} + explicit AddressableRainbowLightEffect(const char *name) : AddressableLightEffect(name) {} void apply(AddressableLight &it, const Color ¤t_color) override { ESPHSVColor hsv; hsv.value = 255; @@ -112,7 +111,7 @@ struct AddressableColorWipeEffectColor { class AddressableColorWipeEffect : public AddressableLightEffect { public: - explicit AddressableColorWipeEffect(const std::string &name) : AddressableLightEffect(name) {} + explicit AddressableColorWipeEffect(const char *name) : AddressableLightEffect(name) {} void set_colors(const std::initializer_list &colors) { this->colors_ = colors; } void set_add_led_interval(uint32_t add_led_interval) { this->add_led_interval_ = add_led_interval; } void set_reverse(bool reverse) { this->reverse_ = reverse; } @@ -165,7 +164,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect { class AddressableScanEffect : public AddressableLightEffect { public: - explicit AddressableScanEffect(const std::string &name) : AddressableLightEffect(name) {} + explicit AddressableScanEffect(const char *name) : AddressableLightEffect(name) {} void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; } void set_scan_width(uint32_t scan_width) { this->scan_width_ = scan_width; } void apply(AddressableLight &it, const Color ¤t_color) override { @@ -202,7 +201,7 @@ class AddressableScanEffect : public AddressableLightEffect { class AddressableTwinkleEffect : public AddressableLightEffect { public: - explicit AddressableTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {} + explicit AddressableTwinkleEffect(const char *name) : AddressableLightEffect(name) {} void apply(AddressableLight &addressable, const Color ¤t_color) override { const uint32_t now = millis(); uint8_t pos_add = 0; @@ -244,7 +243,7 @@ class AddressableTwinkleEffect : public AddressableLightEffect { class AddressableRandomTwinkleEffect : public AddressableLightEffect { public: - explicit AddressableRandomTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {} + explicit AddressableRandomTwinkleEffect(const char *name) : AddressableLightEffect(name) {} void apply(AddressableLight &it, const Color ¤t_color) override { const uint32_t now = millis(); uint8_t pos_add = 0; @@ -293,7 +292,7 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect { class AddressableFireworksEffect : public AddressableLightEffect { public: - explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {} + explicit AddressableFireworksEffect(const char *name) : AddressableLightEffect(name) {} void start() override { auto &it = *this->get_addressable_(); it.all() = Color::BLACK; @@ -342,7 +341,7 @@ class AddressableFireworksEffect : public AddressableLightEffect { class AddressableFlickerEffect : public AddressableLightEffect { public: - explicit AddressableFlickerEffect(const std::string &name) : AddressableLightEffect(name) {} + explicit AddressableFlickerEffect(const char *name) : AddressableLightEffect(name) {} void apply(AddressableLight &it, const Color ¤t_color) override { const uint32_t now = millis(); const uint8_t intensity = this->intensity_; diff --git a/esphome/components/light/base_light_effects.h b/esphome/components/light/base_light_effects.h index c74d19fe14..327c243525 100644 --- a/esphome/components/light/base_light_effects.h +++ b/esphome/components/light/base_light_effects.h @@ -17,7 +17,7 @@ inline static float random_cubic_float() { /// Pulse effect. class PulseLightEffect : public LightEffect { public: - explicit PulseLightEffect(const std::string &name) : LightEffect(name) {} + explicit PulseLightEffect(const char *name) : LightEffect(name) {} void apply() override { const uint32_t now = millis(); @@ -60,7 +60,7 @@ class PulseLightEffect : public LightEffect { /// Random effect. Sets random colors every 10 seconds and slowly transitions between them. class RandomLightEffect : public LightEffect { public: - explicit RandomLightEffect(const std::string &name) : LightEffect(name) {} + explicit RandomLightEffect(const char *name) : LightEffect(name) {} void apply() override { const uint32_t now = millis(); @@ -112,7 +112,7 @@ class RandomLightEffect : public LightEffect { class LambdaLightEffect : public LightEffect { public: - LambdaLightEffect(const std::string &name, std::function f, uint32_t update_interval) + LambdaLightEffect(const char *name, std::function f, uint32_t update_interval) : LightEffect(name), f_(std::move(f)), update_interval_(update_interval) {} void start() override { this->initial_run_ = true; } @@ -138,7 +138,7 @@ class LambdaLightEffect : public LightEffect { class AutomationLightEffect : public LightEffect { public: - AutomationLightEffect(const std::string &name) : LightEffect(name) {} + AutomationLightEffect(const char *name) : LightEffect(name) {} void stop() override { this->trig_->stop_action(); } void apply() override { if (!this->trig_->is_action_running()) { @@ -163,7 +163,7 @@ struct StrobeLightEffectColor { class StrobeLightEffect : public LightEffect { public: - explicit StrobeLightEffect(const std::string &name) : LightEffect(name) {} + explicit StrobeLightEffect(const char *name) : LightEffect(name) {} void apply() override { const uint32_t now = millis(); if (now - this->last_switch_ < this->colors_[this->at_color_].duration) @@ -198,7 +198,7 @@ class StrobeLightEffect : public LightEffect { class FlickerLightEffect : public LightEffect { public: - explicit FlickerLightEffect(const std::string &name) : LightEffect(name) {} + explicit FlickerLightEffect(const char *name) : LightEffect(name) {} void apply() override { LightColorValues remote = this->state_->remote_values; diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index af193e1f11..f611baba71 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -156,7 +156,7 @@ void LightCall::perform() { if (this->effect_ == 0u) { effect_s = "None"; } else { - effect_s = this->parent_->effects_[this->effect_ - 1]->get_name().c_str(); + effect_s = this->parent_->effects_[this->effect_ - 1]->get_name(); } if (publish) { @@ -511,7 +511,7 @@ LightCall &LightCall::set_effect(const std::string &effect) { for (uint32_t i = 0; i < this->parent_->effects_.size(); i++) { LightEffect *e = this->parent_->effects_[i]; - if (strcasecmp(effect.c_str(), e->get_name().c_str()) == 0) { + if (strcasecmp(effect.c_str(), e->get_name()) == 0) { this->set_effect(i + 1); found = true; break; diff --git a/esphome/components/light/light_effect.h b/esphome/components/light/light_effect.h index dbaf1faf24..d4c2dc3582 100644 --- a/esphome/components/light/light_effect.h +++ b/esphome/components/light/light_effect.h @@ -1,7 +1,5 @@ #pragma once -#include - #include "esphome/core/component.h" namespace esphome { @@ -11,7 +9,7 @@ class LightState; class LightEffect { public: - explicit LightEffect(std::string name) : name_(std::move(name)) {} + explicit LightEffect(const char *name) : name_(name) {} /// Initialize this LightEffect. Will be called once after creation. virtual void start() {} @@ -24,7 +22,11 @@ class LightEffect { /// Apply this effect. Use the provided state for starting transitions, ... virtual void apply() = 0; - const std::string &get_name() { return this->name_; } + /** + * Returns the name of this effect. + * The returned pointer is valid for the lifetime of the program and must not be freed. + */ + const char *get_name() const { return this->name_; } /// Internal method called by the LightState when this light effect is registered in it. virtual void init() {} @@ -47,7 +49,7 @@ class LightEffect { protected: LightState *state_{nullptr}; - std::string name_; + const char *name_; /// Internal method to find this effect's index in the parent light's effect list. uint32_t get_index_in_parent_() const; diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index a07aeb6ae5..502a08c635 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -177,7 +177,7 @@ class LightState : public EntityBase, public Component { return 0; } for (size_t i = 0; i < this->effects_.size(); i++) { - if (strcasecmp(effect_name.c_str(), this->effects_[i]->get_name().c_str()) == 0) { + if (strcasecmp(effect_name.c_str(), this->effects_[i]->get_name()) == 0) { return i + 1; // Effects are 1-indexed in active_effect_index_ } } diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index 25577ccc11..d26b7a1750 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -28,7 +28,7 @@ const int DEFAULT_BLANK_TIME = 1000; static const char *const TAG = "wled_light_effect"; -WLEDLightEffect::WLEDLightEffect(const std::string &name) : AddressableLightEffect(name) {} +WLEDLightEffect::WLEDLightEffect(const char *name) : AddressableLightEffect(name) {} void WLEDLightEffect::start() { AddressableLightEffect::start(); diff --git a/esphome/components/wled/wled_light_effect.h b/esphome/components/wled/wled_light_effect.h index a591e1fd1a..6da5f4e9f9 100644 --- a/esphome/components/wled/wled_light_effect.h +++ b/esphome/components/wled/wled_light_effect.h @@ -15,7 +15,7 @@ namespace wled { class WLEDLightEffect : public light::AddressableLightEffect { public: - WLEDLightEffect(const std::string &name); + WLEDLightEffect(const char *name); void start() override; void stop() override; From 6efe346cc5bf18c29ff2a08042f51a6255dee9f3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Oct 2025 15:21:53 -1000 Subject: [PATCH 240/526] [light] Use std::initializer_list for add_effects to reduce flash overhead (#11485) --- esphome/components/light/light_state.cpp | 7 ++----- esphome/components/light/light_state.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 979dc2f5a1..7b0a698bb8 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -178,12 +178,9 @@ void LightState::set_restore_mode(LightRestoreMode restore_mode) { this->restore void LightState::set_initial_state(const LightStateRTCState &initial_state) { this->initial_state_ = initial_state; } bool LightState::supports_effects() { return !this->effects_.empty(); } const FixedVector &LightState::get_effects() const { return this->effects_; } -void LightState::add_effects(const std::vector &effects) { +void LightState::add_effects(const std::initializer_list &effects) { // Called once from Python codegen during setup with all effects from YAML config - this->effects_.init(effects.size()); - for (auto *effect : effects) { - this->effects_.push_back(effect); - } + this->effects_ = effects; } void LightState::current_values_as_binary(bool *binary) { this->current_values.as_binary(binary); } diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index 502a08c635..bf63c0ec27 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -163,7 +163,7 @@ class LightState : public EntityBase, public Component { const FixedVector &get_effects() const; /// Add effects for this light state. - void add_effects(const std::vector &effects); + void add_effects(const std::initializer_list &effects); /// Get the total number of effects available for this light. size_t get_effect_count() const { return this->effects_.size(); } From 2864e989bdbf9948cf3822575346556624daff56 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Oct 2025 15:22:46 -1000 Subject: [PATCH 241/526] [light] Extract ColorModeMask into generic FiniteSetMask helper (#11472) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- esphome/components/light/color_mode.h | 215 +++++++----------------- esphome/components/light/light_call.cpp | 2 +- esphome/components/light/light_traits.h | 4 +- esphome/core/finite_set_mask.h | 171 +++++++++++++++++++ 4 files changed, 237 insertions(+), 155 deletions(-) create mode 100644 esphome/core/finite_set_mask.h diff --git a/esphome/components/light/color_mode.h b/esphome/components/light/color_mode.h index a26f917167..aa3448c145 100644 --- a/esphome/components/light/color_mode.h +++ b/esphome/components/light/color_mode.h @@ -1,6 +1,7 @@ #pragma once #include +#include "esphome/core/finite_set_mask.h" namespace esphome { namespace light { @@ -107,13 +108,9 @@ constexpr ColorModeHelper operator|(ColorModeHelper lhs, ColorMode rhs) { // Type alias for raw color mode bitmask values using color_mode_bitmask_t = uint16_t; -// Constants for ColorMode count and bit range -static constexpr int COLOR_MODE_COUNT = 10; // UNKNOWN through RGB_COLD_WARM_WHITE -static constexpr int MAX_BIT_INDEX = sizeof(color_mode_bitmask_t) * 8; // Number of bits in bitmask type - -// Compile-time array of all ColorMode values in declaration order -// Bit positions (0-9) map directly to enum declaration order -static constexpr ColorMode COLOR_MODES[COLOR_MODE_COUNT] = { +// Lookup table for ColorMode bit mapping +// This array defines the canonical order of color modes (bit 0-9) +constexpr ColorMode COLOR_MODE_LOOKUP[] = { ColorMode::UNKNOWN, // bit 0 ColorMode::ON_OFF, // bit 1 ColorMode::BRIGHTNESS, // bit 2 @@ -126,33 +123,42 @@ static constexpr ColorMode COLOR_MODES[COLOR_MODE_COUNT] = { ColorMode::RGB_COLD_WARM_WHITE, // bit 9 }; -/// Map ColorMode enum values to bit positions (0-9) -/// Bit positions follow the enum declaration order -static constexpr int mode_to_bit(ColorMode mode) { - // Linear search through COLOR_MODES array - // Compiler optimizes this to efficient code since array is constexpr - for (int i = 0; i < COLOR_MODE_COUNT; ++i) { - if (COLOR_MODES[i] == mode) - return i; - } - return 0; -} +/// Bit mapping policy for ColorMode +/// Uses lookup table for non-contiguous enum values +struct ColorModeBitPolicy { + using mask_t = uint16_t; // 10 bits requires uint16_t + static constexpr int MAX_BITS = sizeof(COLOR_MODE_LOOKUP) / sizeof(COLOR_MODE_LOOKUP[0]); -/// Map bit positions (0-9) to ColorMode enum values -/// Bit positions follow the enum declaration order -static constexpr ColorMode bit_to_mode(int bit) { - // Direct lookup in COLOR_MODES array - return (bit >= 0 && bit < COLOR_MODE_COUNT) ? COLOR_MODES[bit] : ColorMode::UNKNOWN; -} + static constexpr unsigned to_bit(ColorMode mode) { + // Linear search through lookup table + // Compiler optimizes this to efficient code since array is constexpr + for (int i = 0; i < MAX_BITS; ++i) { + if (COLOR_MODE_LOOKUP[i] == mode) + return i; + } + return 0; + } + + static constexpr ColorMode from_bit(unsigned bit) { + return (bit < MAX_BITS) ? COLOR_MODE_LOOKUP[bit] : ColorMode::UNKNOWN; + } +}; + +// Type alias for ColorMode bitmask using policy-based design +using ColorModeMask = FiniteSetMask; + +// Number of ColorCapability enum values +constexpr int COLOR_CAPABILITY_COUNT = 6; /// Helper to compute capability bitmask at compile time -static constexpr color_mode_bitmask_t compute_capability_bitmask(ColorCapability capability) { - color_mode_bitmask_t mask = 0; +constexpr uint16_t compute_capability_bitmask(ColorCapability capability) { + uint16_t mask = 0; uint8_t cap_bit = static_cast(capability); // Check each ColorMode to see if it has this capability - for (int bit = 0; bit < COLOR_MODE_COUNT; ++bit) { - uint8_t mode_val = static_cast(bit_to_mode(bit)); + constexpr int color_mode_count = sizeof(COLOR_MODE_LOOKUP) / sizeof(COLOR_MODE_LOOKUP[0]); + for (int bit = 0; bit < color_mode_count; ++bit) { + uint8_t mode_val = static_cast(COLOR_MODE_LOOKUP[bit]); if ((mode_val & cap_bit) != 0) { mask |= (1 << bit); } @@ -160,12 +166,9 @@ static constexpr color_mode_bitmask_t compute_capability_bitmask(ColorCapability return mask; } -// Number of ColorCapability enum values -static constexpr int COLOR_CAPABILITY_COUNT = 6; - /// Compile-time lookup table mapping ColorCapability to bitmask /// This array is computed at compile time using constexpr -static constexpr color_mode_bitmask_t CAPABILITY_BITMASKS[] = { +constexpr uint16_t CAPABILITY_BITMASKS[] = { compute_capability_bitmask(ColorCapability::ON_OFF), // 1 << 0 compute_capability_bitmask(ColorCapability::BRIGHTNESS), // 1 << 1 compute_capability_bitmask(ColorCapability::WHITE), // 1 << 2 @@ -174,130 +177,38 @@ static constexpr color_mode_bitmask_t CAPABILITY_BITMASKS[] = { compute_capability_bitmask(ColorCapability::RGB), // 1 << 5 }; -/// Bitmask for storing a set of ColorMode values efficiently. -/// Replaces std::set to eliminate red-black tree overhead (~586 bytes). -class ColorModeMask { - public: - constexpr ColorModeMask() = default; - - /// Support initializer list syntax: {ColorMode::RGB, ColorMode::WHITE} - constexpr ColorModeMask(std::initializer_list modes) { - for (auto mode : modes) { - this->add(mode); - } - } - - constexpr void add(ColorMode mode) { this->mask_ |= (1 << mode_to_bit(mode)); } - - /// Add multiple modes at once using initializer list - constexpr void add(std::initializer_list modes) { - for (auto mode : modes) { - this->add(mode); - } - } - - constexpr bool contains(ColorMode mode) const { return (this->mask_ & (1 << mode_to_bit(mode))) != 0; } - - constexpr size_t size() const { - // Count set bits using Brian Kernighan's algorithm - // More efficient for sparse bitmasks (typical case: 2-4 modes out of 10) - uint16_t n = this->mask_; - size_t count = 0; - while (n) { - n &= n - 1; // Clear the least significant set bit - count++; - } - return count; - } - - constexpr bool empty() const { return this->mask_ == 0; } - - /// Iterator support for API encoding - class Iterator { - public: - using iterator_category = std::forward_iterator_tag; - using value_type = ColorMode; - using difference_type = std::ptrdiff_t; - using pointer = const ColorMode *; - using reference = ColorMode; - - constexpr Iterator(color_mode_bitmask_t mask, int bit) : mask_(mask), bit_(bit) { advance_to_next_set_bit_(); } - - constexpr ColorMode operator*() const { return bit_to_mode(bit_); } - - constexpr Iterator &operator++() { - ++bit_; - advance_to_next_set_bit_(); - return *this; - } - - constexpr bool operator==(const Iterator &other) const { return bit_ == other.bit_; } - - constexpr bool operator!=(const Iterator &other) const { return !(*this == other); } - - private: - constexpr void advance_to_next_set_bit_() { bit_ = ColorModeMask::find_next_set_bit(mask_, bit_); } - - color_mode_bitmask_t mask_; - int bit_; - }; - - constexpr Iterator begin() const { return Iterator(mask_, 0); } - constexpr Iterator end() const { return Iterator(mask_, MAX_BIT_INDEX); } - - /// Get the raw bitmask value for API encoding - constexpr color_mode_bitmask_t get_mask() const { return this->mask_; } - - /// Find the next set bit in a bitmask starting from a given position - /// Returns the bit position, or MAX_BIT_INDEX if no more bits are set - static constexpr int find_next_set_bit(color_mode_bitmask_t mask, int start_bit) { - int bit = start_bit; - while (bit < MAX_BIT_INDEX && !(mask & (1 << bit))) { - ++bit; - } - return bit; - } - - /// Find the first set bit in a bitmask and return the corresponding ColorMode - /// Used for optimizing compute_color_mode_() intersection logic - static constexpr ColorMode first_mode_from_mask(color_mode_bitmask_t mask) { - return bit_to_mode(find_next_set_bit(mask, 0)); - } - - /// Check if a ColorMode is present in a raw bitmask value - /// Useful for checking intersection results without creating a temporary ColorModeMask - static constexpr bool mask_contains(color_mode_bitmask_t mask, ColorMode mode) { - return (mask & (1 << mode_to_bit(mode))) != 0; - } - - /// Check if any mode in the bitmask has a specific capability - /// Used for checking if a light supports a capability (e.g., BRIGHTNESS, RGB) - bool has_capability(ColorCapability capability) const { - // Lookup the pre-computed bitmask for this capability and check intersection with our mask - // ColorCapability values: 1, 2, 4, 8, 16, 32 -> array indices: 0, 1, 2, 3, 4, 5 - // We need to convert the power-of-2 value to an index - uint8_t cap_val = static_cast(capability); +/** + * @brief Helper function to convert a power-of-2 ColorCapability value to an array index for CAPABILITY_BITMASKS + * lookup. + * + * This function maps ColorCapability values (1, 2, 4, 8, 16, 32) to array indices (0, 1, 2, 3, 4, 5). + * Used to index into the CAPABILITY_BITMASKS lookup table. + * + * @param capability A ColorCapability enum value (must be a power of 2). + * @return The corresponding array index (0-based). + */ +inline int capability_to_index(ColorCapability capability) { + uint8_t cap_val = static_cast(capability); #if defined(__GNUC__) || defined(__clang__) - // Use compiler intrinsic for efficient bit position lookup (O(1) vs O(log n)) - int index = __builtin_ctz(cap_val); + // Use compiler intrinsic for efficient bit position lookup (O(1) vs O(log n)) + return __builtin_ctz(cap_val); #else - // Fallback for compilers without __builtin_ctz - int index = 0; - while (cap_val > 1) { - cap_val >>= 1; - ++index; - } -#endif - return (this->mask_ & CAPABILITY_BITMASKS[index]) != 0; + // Fallback for compilers without __builtin_ctz + int index = 0; + while (cap_val > 1) { + cap_val >>= 1; + ++index; } + return index; +#endif +} - private: - // Using uint16_t instead of uint32_t for more efficient iteration (fewer bits to scan). - // Currently only 10 ColorMode values exist, so 16 bits is sufficient. - // Can be changed to uint32_t if more than 16 color modes are needed in the future. - // Note: Due to struct padding, uint16_t and uint32_t result in same LightTraits size (12 bytes). - color_mode_bitmask_t mask_{0}; -}; +/// Check if any mode in the bitmask has a specific capability +/// Used for checking if a light supports a capability (e.g., BRIGHTNESS, RGB) +inline bool has_capability(const ColorModeMask &mask, ColorCapability capability) { + // Lookup the pre-computed bitmask for this capability and check intersection with our mask + return (mask.get_mask() & CAPABILITY_BITMASKS[capability_to_index(capability)]) != 0; +} } // namespace light } // namespace esphome diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index f611baba71..df17f53adc 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -437,7 +437,7 @@ ColorMode LightCall::compute_color_mode_() { // Use the preferred suitable mode. if (intersection != 0) { - ColorMode mode = ColorModeMask::first_mode_from_mask(intersection); + ColorMode mode = ColorModeMask::first_value_from_mask(intersection); ESP_LOGI(TAG, "'%s': color mode not specified; using %s", this->parent_->get_name().c_str(), LOG_STR_ARG(color_mode_to_human(mode))); return mode; diff --git a/esphome/components/light/light_traits.h b/esphome/components/light/light_traits.h index 4532edca83..294b0cad1d 100644 --- a/esphome/components/light/light_traits.h +++ b/esphome/components/light/light_traits.h @@ -26,9 +26,9 @@ class LightTraits { this->supported_color_modes_ = ColorModeMask(modes); } - bool supports_color_mode(ColorMode color_mode) const { return this->supported_color_modes_.contains(color_mode); } + bool supports_color_mode(ColorMode color_mode) const { return this->supported_color_modes_.count(color_mode) > 0; } bool supports_color_capability(ColorCapability color_capability) const { - return this->supported_color_modes_.has_capability(color_capability); + return has_capability(this->supported_color_modes_, color_capability); } float get_min_mireds() const { return this->min_mireds_; } diff --git a/esphome/core/finite_set_mask.h b/esphome/core/finite_set_mask.h new file mode 100644 index 0000000000..f9cd0377c7 --- /dev/null +++ b/esphome/core/finite_set_mask.h @@ -0,0 +1,171 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace esphome { + +/// Default bit mapping policy for contiguous enums starting at 0 +/// Provides 1:1 mapping where enum value equals bit position +template struct DefaultBitPolicy { + // Automatic bitmask type selection based on MaxBits + // ≤8 bits: uint8_t, ≤16 bits: uint16_t, otherwise: uint32_t + using mask_t = typename std::conditional<(MaxBits <= 8), uint8_t, + typename std::conditional<(MaxBits <= 16), uint16_t, uint32_t>::type>::type; + + static constexpr int MAX_BITS = MaxBits; + + static constexpr unsigned to_bit(ValueType value) { return static_cast(value); } + + static constexpr ValueType from_bit(unsigned bit) { return static_cast(bit); } +}; + +/// Generic bitmask for storing a finite set of discrete values efficiently. +/// Replaces std::set to eliminate red-black tree overhead (~586 bytes per instantiation). +/// +/// Template parameters: +/// ValueType: The type to store (typically enum, but can be any discrete bounded type) +/// BitPolicy: Policy class defining bit mapping and mask type (defaults to DefaultBitPolicy) +/// +/// BitPolicy requirements: +/// - using mask_t = // Bitmask storage type +/// - static constexpr int MAX_BITS // Maximum number of bits +/// - static constexpr unsigned to_bit(ValueType) // Convert value to bit position +/// - static constexpr ValueType from_bit(unsigned) // Convert bit position to value +/// +/// Example usage (1:1 mapping - climate enums): +/// // For contiguous enums starting at 0, use DefaultBitPolicy +/// using ClimateModeMask = FiniteSetMask>; +/// ClimateModeMask modes({CLIMATE_MODE_HEAT, CLIMATE_MODE_COOL}); +/// if (modes.count(CLIMATE_MODE_HEAT)) { ... } +/// for (auto mode : modes) { ... } +/// +/// Example usage (custom mapping - ColorMode): +/// // For custom mappings, define a custom BitPolicy +/// // See esphome/components/light/color_mode.h for complete example +/// +/// Design notes: +/// - Policy-based design allows custom bit mappings without template specialization +/// - Iterator converts bit positions to actual values during traversal +/// - All operations are constexpr-compatible for compile-time initialization +/// - Drop-in replacement for std::set with simpler API +/// +template> class FiniteSetMask { + public: + using bitmask_t = typename BitPolicy::mask_t; + + constexpr FiniteSetMask() = default; + + /// Construct from initializer list: {VALUE1, VALUE2, ...} + constexpr FiniteSetMask(std::initializer_list values) { + for (auto value : values) { + this->insert(value); + } + } + + /// Add a single value to the set (std::set compatibility) + constexpr void insert(ValueType value) { this->mask_ |= (static_cast(1) << BitPolicy::to_bit(value)); } + + /// Add multiple values from initializer list + constexpr void insert(std::initializer_list values) { + for (auto value : values) { + this->insert(value); + } + } + + /// Remove a value from the set (std::set compatibility) + constexpr void erase(ValueType value) { this->mask_ &= ~(static_cast(1) << BitPolicy::to_bit(value)); } + + /// Clear all values from the set + constexpr void clear() { this->mask_ = 0; } + + /// Check if the set contains a specific value (std::set compatibility) + /// Returns 1 if present, 0 if not (same as std::set for unique elements) + constexpr size_t count(ValueType value) const { + return (this->mask_ & (static_cast(1) << BitPolicy::to_bit(value))) != 0 ? 1 : 0; + } + + /// Count the number of values in the set + constexpr size_t size() const { + // Brian Kernighan's algorithm - efficient for sparse bitmasks + // Typical case: 2-4 modes out of 10 possible + bitmask_t n = this->mask_; + size_t count = 0; + while (n) { + n &= n - 1; // Clear the least significant set bit + count++; + } + return count; + } + + /// Check if the set is empty + constexpr bool empty() const { return this->mask_ == 0; } + + /// Iterator support for range-based for loops and API encoding + /// Iterates over set bits and converts bit positions to values + /// Optimization: removes bits from mask as we iterate + class Iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = ValueType; + using difference_type = std::ptrdiff_t; + using pointer = const ValueType *; + using reference = ValueType; + + constexpr explicit Iterator(bitmask_t mask) : mask_(mask) {} + + constexpr ValueType operator*() const { + // Return value for the first set bit + return BitPolicy::from_bit(find_next_set_bit(mask_, 0)); + } + + constexpr Iterator &operator++() { + // Clear the lowest set bit (Brian Kernighan's algorithm) + mask_ &= mask_ - 1; + return *this; + } + + constexpr bool operator==(const Iterator &other) const { return mask_ == other.mask_; } + + constexpr bool operator!=(const Iterator &other) const { return !(*this == other); } + + private: + bitmask_t mask_; + }; + + constexpr Iterator begin() const { return Iterator(mask_); } + constexpr Iterator end() const { return Iterator(0); } + + /// Get the raw bitmask value for optimized operations + constexpr bitmask_t get_mask() const { return this->mask_; } + + /// Check if a specific value is present in a raw bitmask + /// Useful for checking intersection results without creating temporary objects + static constexpr bool mask_contains(bitmask_t mask, ValueType value) { + return (mask & (static_cast(1) << BitPolicy::to_bit(value))) != 0; + } + + /// Get the first value from a raw bitmask + /// Used for optimizing intersection logic (e.g., "pick first suitable mode") + static constexpr ValueType first_value_from_mask(bitmask_t mask) { + return BitPolicy::from_bit(find_next_set_bit(mask, 0)); + } + + /// Find the next set bit in a bitmask starting from a given position + /// Returns the bit position, or MAX_BITS if no more bits are set + static constexpr int find_next_set_bit(bitmask_t mask, int start_bit) { + int bit = start_bit; + while (bit < BitPolicy::MAX_BITS && !(mask & (static_cast(1) << bit))) { + ++bit; + } + return bit; + } + + protected: + bitmask_t mask_{0}; +}; + +} // namespace esphome From 7e5b82c5f3ece45a0f7c9d98eeecc1c2cf93126d Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 22 Oct 2025 20:24:08 -0500 Subject: [PATCH 242/526] [improv_serial] Various optimizations (#11473) Co-authored-by: J. Nick Koston --- .../improv_serial/improv_serial_component.cpp | 183 +++++++++--------- .../improv_serial/improv_serial_component.h | 27 ++- .../components/improv_base/common-uart0.yaml | 8 + .../improv_base/test-uart0.esp8266-ard.yaml | 1 + 4 files changed, 128 insertions(+), 91 deletions(-) create mode 100644 tests/components/improv_base/common-uart0.yaml create mode 100644 tests/components/improv_base/test-uart0.esp8266-ard.yaml diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index ce82504d3c..9d080ea98e 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -28,6 +28,38 @@ void ImprovSerialComponent::setup() { } } +void ImprovSerialComponent::loop() { + if (this->last_read_byte_ && (millis() - this->last_read_byte_ > IMPROV_SERIAL_TIMEOUT)) { + this->last_read_byte_ = 0; + this->rx_buffer_.clear(); + ESP_LOGV(TAG, "Timeout"); + } + + auto byte = this->read_byte_(); + while (byte.has_value()) { + if (this->parse_improv_serial_byte_(byte.value())) { + this->last_read_byte_ = millis(); + } else { + this->last_read_byte_ = 0; + this->rx_buffer_.clear(); + } + byte = this->read_byte_(); + } + + if (this->state_ == improv::STATE_PROVISIONING) { + if (wifi::global_wifi_component->is_connected()) { + wifi::global_wifi_component->save_wifi_sta(this->connecting_sta_.get_ssid(), + this->connecting_sta_.get_password()); + this->connecting_sta_ = {}; + this->cancel_timeout("wifi-connect-timeout"); + this->set_state_(improv::STATE_PROVISIONED); + + std::vector url = this->build_rpc_settings_response_(improv::WIFI_SETTINGS); + this->send_response_(url); + } + } +} + void ImprovSerialComponent::dump_config() { ESP_LOGCONFIG(TAG, "Improv Serial:"); } optional ImprovSerialComponent::read_byte_() { @@ -78,8 +110,28 @@ optional ImprovSerialComponent::read_byte_() { return byte; } -void ImprovSerialComponent::write_data_(std::vector &data) { - data.push_back('\n'); +void ImprovSerialComponent::write_data_(const uint8_t *data, const size_t size) { + // First, set length field + this->tx_header_[TX_LENGTH_IDX] = this->tx_header_[TX_TYPE_IDX] == TYPE_RPC_RESPONSE ? size : 1; + + const bool there_is_data = data != nullptr && size > 0; + // If there_is_data, checksum must not include our optional data byte + const uint8_t header_checksum_len = there_is_data ? TX_BUFFER_SIZE - 3 : TX_BUFFER_SIZE - 2; + // Only transmit the full buffer length if there is no data (only state/error byte is provided in this case) + const uint8_t header_tx_len = there_is_data ? TX_BUFFER_SIZE - 3 : TX_BUFFER_SIZE; + // Calculate checksum for message + uint8_t checksum = 0; + for (uint8_t i = 0; i < header_checksum_len; i++) { + checksum += this->tx_header_[i]; + } + if (there_is_data) { + // Include data in checksum + for (size_t i = 0; i < size; i++) { + checksum += data[i]; + } + } + this->tx_header_[TX_CHECKSUM_IDX] = checksum; + #ifdef USE_ESP32 switch (logger::global_logger->get_uart()) { case logger::UART_SELECTION_UART0: @@ -87,63 +139,45 @@ void ImprovSerialComponent::write_data_(std::vector &data) { #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) case logger::UART_SELECTION_UART2: -#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 - uart_write_bytes(this->uart_num_, data.data(), data.size()); +#endif + uart_write_bytes(this->uart_num_, this->tx_header_, header_tx_len); + if (there_is_data) { + uart_write_bytes(this->uart_num_, data, size); + uart_write_bytes(this->uart_num_, &this->tx_header_[TX_CHECKSUM_IDX], 2); // Footer: checksum and newline + } break; #if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC) - case logger::UART_SELECTION_USB_CDC: { - const char *msg = (char *) data.data(); - esp_usb_console_write_buf(msg, data.size()); + case logger::UART_SELECTION_USB_CDC: + esp_usb_console_write_buf((const char *) this->tx_header_, header_tx_len); + if (there_is_data) { + esp_usb_console_write_buf((const char *) data, size); + esp_usb_console_write_buf((const char *) &this->tx_header_[TX_CHECKSUM_IDX], + 2); // Footer: checksum and newline + } break; - } -#endif // USE_LOGGER_USB_CDC +#endif #ifdef USE_LOGGER_USB_SERIAL_JTAG case logger::UART_SELECTION_USB_SERIAL_JTAG: - usb_serial_jtag_write_bytes((char *) data.data(), data.size(), 20 / portTICK_PERIOD_MS); - delay(10); - usb_serial_jtag_ll_txfifo_flush(); // fixes for issue in IDF 4.4.7 + usb_serial_jtag_write_bytes((const char *) this->tx_header_, header_tx_len, 20 / portTICK_PERIOD_MS); + if (there_is_data) { + usb_serial_jtag_write_bytes((const char *) data, size, 20 / portTICK_PERIOD_MS); + usb_serial_jtag_write_bytes((const char *) &this->tx_header_[TX_CHECKSUM_IDX], 2, + 20 / portTICK_PERIOD_MS); // Footer: checksum and newline + } break; -#endif // USE_LOGGER_USB_SERIAL_JTAG +#endif default: break; } #elif defined(USE_ARDUINO) - this->hw_serial_->write(data.data(), data.size()); + this->hw_serial_->write(this->tx_header_, header_tx_len); + if (there_is_data) { + this->hw_serial_->write(data, size); + this->hw_serial_->write(&this->tx_header_[TX_CHECKSUM_IDX], 2); // Footer: checksum and newline + } #endif } -void ImprovSerialComponent::loop() { - if (this->last_read_byte_ && (millis() - this->last_read_byte_ > IMPROV_SERIAL_TIMEOUT)) { - this->last_read_byte_ = 0; - this->rx_buffer_.clear(); - ESP_LOGV(TAG, "Improv Serial timeout"); - } - - auto byte = this->read_byte_(); - while (byte.has_value()) { - if (this->parse_improv_serial_byte_(byte.value())) { - this->last_read_byte_ = millis(); - } else { - this->last_read_byte_ = 0; - this->rx_buffer_.clear(); - } - byte = this->read_byte_(); - } - - if (this->state_ == improv::STATE_PROVISIONING) { - if (wifi::global_wifi_component->is_connected()) { - wifi::global_wifi_component->save_wifi_sta(this->connecting_sta_.get_ssid(), - this->connecting_sta_.get_password()); - this->connecting_sta_ = {}; - this->cancel_timeout("wifi-connect-timeout"); - this->set_state_(improv::STATE_PROVISIONED); - - std::vector url = this->build_rpc_settings_response_(improv::WIFI_SETTINGS); - this->send_response_(url); - } - } -} - std::vector ImprovSerialComponent::build_rpc_settings_response_(improv::Command command) { std::vector urls; #ifdef USE_IMPROV_SERIAL_NEXT_URL @@ -177,13 +211,13 @@ std::vector ImprovSerialComponent::build_version_info_() { bool ImprovSerialComponent::parse_improv_serial_byte_(uint8_t byte) { size_t at = this->rx_buffer_.size(); this->rx_buffer_.push_back(byte); - ESP_LOGV(TAG, "Improv Serial byte: 0x%02X", byte); + ESP_LOGV(TAG, "Byte: 0x%02X", byte); const uint8_t *raw = &this->rx_buffer_[0]; return improv::parse_improv_serial_byte( at, byte, raw, [this](improv::ImprovCommand command) -> bool { return this->parse_improv_payload_(command); }, [this](improv::Error error) -> void { - ESP_LOGW(TAG, "Error decoding Improv payload"); + ESP_LOGW(TAG, "Error decoding payload"); this->set_error_(error); }); } @@ -199,7 +233,7 @@ bool ImprovSerialComponent::parse_improv_payload_(improv::ImprovCommand &command wifi::global_wifi_component->set_sta(sta); wifi::global_wifi_component->start_connecting(sta, false); this->set_state_(improv::STATE_PROVISIONING); - ESP_LOGD(TAG, "Received Improv wifi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(), + ESP_LOGD(TAG, "Received settings: SSID=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(), command.password.c_str()); auto f = std::bind(&ImprovSerialComponent::on_wifi_connect_timeout_, this); @@ -240,7 +274,7 @@ bool ImprovSerialComponent::parse_improv_payload_(improv::ImprovCommand &command return true; } default: { - ESP_LOGW(TAG, "Unknown Improv payload"); + ESP_LOGW(TAG, "Unknown payload"); this->set_error_(improv::ERROR_UNKNOWN_RPC); return false; } @@ -249,57 +283,26 @@ bool ImprovSerialComponent::parse_improv_payload_(improv::ImprovCommand &command void ImprovSerialComponent::set_state_(improv::State state) { this->state_ = state; - - std::vector data = {'I', 'M', 'P', 'R', 'O', 'V'}; - data.resize(11); - data[6] = IMPROV_SERIAL_VERSION; - data[7] = TYPE_CURRENT_STATE; - data[8] = 1; - data[9] = state; - - uint8_t checksum = 0x00; - for (uint8_t d : data) - checksum += d; - data[10] = checksum; - - this->write_data_(data); + this->tx_header_[TX_TYPE_IDX] = TYPE_CURRENT_STATE; + this->tx_header_[TX_DATA_IDX] = state; + this->write_data_(); } void ImprovSerialComponent::set_error_(improv::Error error) { - std::vector data = {'I', 'M', 'P', 'R', 'O', 'V'}; - data.resize(11); - data[6] = IMPROV_SERIAL_VERSION; - data[7] = TYPE_ERROR_STATE; - data[8] = 1; - data[9] = error; - - uint8_t checksum = 0x00; - for (uint8_t d : data) - checksum += d; - data[10] = checksum; - this->write_data_(data); + this->tx_header_[TX_TYPE_IDX] = TYPE_ERROR_STATE; + this->tx_header_[TX_DATA_IDX] = error; + this->write_data_(); } void ImprovSerialComponent::send_response_(std::vector &response) { - std::vector data = {'I', 'M', 'P', 'R', 'O', 'V'}; - data.resize(9); - data[6] = IMPROV_SERIAL_VERSION; - data[7] = TYPE_RPC_RESPONSE; - data[8] = response.size(); - data.insert(data.end(), response.begin(), response.end()); - - uint8_t checksum = 0x00; - for (uint8_t d : data) - checksum += d; - data.push_back(checksum); - - this->write_data_(data); + this->tx_header_[TX_TYPE_IDX] = TYPE_RPC_RESPONSE; + this->write_data_(response.data(), response.size()); } void ImprovSerialComponent::on_wifi_connect_timeout_() { this->set_error_(improv::ERROR_UNABLE_TO_CONNECT); this->set_state_(improv::STATE_AUTHORIZED); - ESP_LOGW(TAG, "Timed out trying to connect to given WiFi network"); + ESP_LOGW(TAG, "Timed out while connecting to Wi-Fi network"); wifi::global_wifi_component->clear_sta(); } diff --git a/esphome/components/improv_serial/improv_serial_component.h b/esphome/components/improv_serial/improv_serial_component.h index c3c9aee24e..057247f376 100644 --- a/esphome/components/improv_serial/improv_serial_component.h +++ b/esphome/components/improv_serial/improv_serial_component.h @@ -26,6 +26,16 @@ namespace esphome { namespace improv_serial { +// TX buffer layout constants +static constexpr uint8_t TX_HEADER_SIZE = 6; // Bytes 0-5 = "IMPROV" +static constexpr uint8_t TX_VERSION_IDX = 6; +static constexpr uint8_t TX_TYPE_IDX = 7; +static constexpr uint8_t TX_LENGTH_IDX = 8; +static constexpr uint8_t TX_DATA_IDX = 9; // For state/error messages only +static constexpr uint8_t TX_CHECKSUM_IDX = 10; +static constexpr uint8_t TX_NEWLINE_IDX = 11; +static constexpr uint8_t TX_BUFFER_SIZE = 12; + enum ImprovSerialType : uint8_t { TYPE_CURRENT_STATE = 0x01, TYPE_ERROR_STATE = 0x02, @@ -57,7 +67,22 @@ class ImprovSerialComponent : public Component, public improv_base::ImprovBase { std::vector build_version_info_(); optional read_byte_(); - void write_data_(std::vector &data); + void write_data_(const uint8_t *data = nullptr, size_t size = 0); + + uint8_t tx_header_[TX_BUFFER_SIZE] = { + 'I', // 0: Header + 'M', // 1: Header + 'P', // 2: Header + 'R', // 3: Header + 'O', // 4: Header + 'V', // 5: Header + IMPROV_SERIAL_VERSION, // 6: Version + 0, // 7: ImprovSerialType + 0, // 8: Length + 0, // 9...X: Data (here, one byte reserved for state/error) + 0, // X + 10: Checksum + '\n', + }; #ifdef USE_ESP32 uart_port_t uart_num_; diff --git a/tests/components/improv_base/common-uart0.yaml b/tests/components/improv_base/common-uart0.yaml new file mode 100644 index 0000000000..7b7730fd46 --- /dev/null +++ b/tests/components/improv_base/common-uart0.yaml @@ -0,0 +1,8 @@ +wifi: + ssid: MySSID + password: password1 + +logger: + hardware_uart: UART0 + +improv_serial: diff --git a/tests/components/improv_base/test-uart0.esp8266-ard.yaml b/tests/components/improv_base/test-uart0.esp8266-ard.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_base/test-uart0.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml From 9b78098eec3c965df24933d2230885381997b399 Mon Sep 17 00:00:00 2001 From: optimusprimespace <62800678+optimusprimespace@users.noreply.github.com> Date: Thu, 23 Oct 2025 05:24:17 +0300 Subject: [PATCH 243/526] [hdc2010] New component (#6674) Co-authored-by: Keith Burzinski Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/hdc2010/__init__.py | 1 + esphome/components/hdc2010/hdc2010.cpp | 111 ++++++++++++++++++ esphome/components/hdc2010/hdc2010.h | 32 +++++ esphome/components/hdc2010/sensor.py | 56 +++++++++ tests/components/hdc2010/common.yaml | 7 ++ .../components/hdc2010/test.esp32-c3-idf.yaml | 4 + tests/components/hdc2010/test.esp32-idf.yaml | 4 + .../components/hdc2010/test.esp8266-ard.yaml | 4 + tests/components/hdc2010/test.rp2040-ard.yaml | 4 + 10 files changed, 224 insertions(+) create mode 100644 esphome/components/hdc2010/__init__.py create mode 100644 esphome/components/hdc2010/hdc2010.cpp create mode 100644 esphome/components/hdc2010/hdc2010.h create mode 100644 esphome/components/hdc2010/sensor.py create mode 100644 tests/components/hdc2010/common.yaml create mode 100644 tests/components/hdc2010/test.esp32-c3-idf.yaml create mode 100644 tests/components/hdc2010/test.esp32-idf.yaml create mode 100644 tests/components/hdc2010/test.esp8266-ard.yaml create mode 100644 tests/components/hdc2010/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 4f860375d9..667a44fc03 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -201,6 +201,7 @@ esphome/components/havells_solar/* @sourabhjaiswal esphome/components/hbridge/fan/* @WeekendWarrior esphome/components/hbridge/light/* @DotNetDann esphome/components/hbridge/switch/* @dwmw2 +esphome/components/hdc2010/* @optimusprimespace @ssieb esphome/components/he60r/* @clydebarrow esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal diff --git a/esphome/components/hdc2010/__init__.py b/esphome/components/hdc2010/__init__.py new file mode 100644 index 0000000000..badf9dbb0c --- /dev/null +++ b/esphome/components/hdc2010/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@optimusprimespace", "@ssieb"] diff --git a/esphome/components/hdc2010/hdc2010.cpp b/esphome/components/hdc2010/hdc2010.cpp new file mode 100644 index 0000000000..c53fdb3f5b --- /dev/null +++ b/esphome/components/hdc2010/hdc2010.cpp @@ -0,0 +1,111 @@ +#include "esphome/core/hal.h" +#include "hdc2010.h" +// https://github.com/vigsterkr/homebridge-hdc2010/blob/main/src/hdc2010.js +// https://github.com/lime-labs/HDC2080-Arduino/blob/master/src/HDC2080.cpp +namespace esphome { +namespace hdc2010 { + +static const char *const TAG = "hdc2010"; + +static const uint8_t HDC2010_ADDRESS = 0x40; // 0b1000000 or 0b1000001 from datasheet +static const uint8_t HDC2010_CMD_CONFIGURATION_MEASUREMENT = 0x8F; +static const uint8_t HDC2010_CMD_START_MEASUREMENT = 0xF9; +static const uint8_t HDC2010_CMD_TEMPERATURE_LOW = 0x00; +static const uint8_t HDC2010_CMD_TEMPERATURE_HIGH = 0x01; +static const uint8_t HDC2010_CMD_HUMIDITY_LOW = 0x02; +static const uint8_t HDC2010_CMD_HUMIDITY_HIGH = 0x03; +static const uint8_t CONFIG = 0x0E; +static const uint8_t MEASUREMENT_CONFIG = 0x0F; + +void HDC2010Component::setup() { + ESP_LOGCONFIG(TAG, "Running setup"); + + const uint8_t data[2] = { + 0b00000000, // resolution 14bit for both humidity and temperature + 0b00000000 // reserved + }; + + if (!this->write_bytes(HDC2010_CMD_CONFIGURATION_MEASUREMENT, data, 2)) { + ESP_LOGW(TAG, "Initial config instruction error"); + this->status_set_warning(); + return; + } + + // Set measurement mode to temperature and humidity + uint8_t config_contents; + this->read_register(MEASUREMENT_CONFIG, &config_contents, 1); + config_contents = (config_contents & 0xF9); // Always set to TEMP_AND_HUMID mode + this->write_bytes(MEASUREMENT_CONFIG, &config_contents, 1); + + // Set rate to manual + this->read_register(CONFIG, &config_contents, 1); + config_contents &= 0x8F; + this->write_bytes(CONFIG, &config_contents, 1); + + // Set temperature resolution to 14bit + this->read_register(CONFIG, &config_contents, 1); + config_contents &= 0x3F; + this->write_bytes(CONFIG, &config_contents, 1); + + // Set humidity resolution to 14bit + this->read_register(CONFIG, &config_contents, 1); + config_contents &= 0xCF; + this->write_bytes(CONFIG, &config_contents, 1); +} + +void HDC2010Component::dump_config() { + ESP_LOGCONFIG(TAG, "HDC2010:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); + } + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); +} + +void HDC2010Component::update() { + // Trigger measurement + uint8_t config_contents; + this->read_register(CONFIG, &config_contents, 1); + config_contents |= 0x01; + this->write_bytes(MEASUREMENT_CONFIG, &config_contents, 1); + + // 1ms delay after triggering the sample + set_timeout(1, [this]() { + if (this->temperature_sensor_ != nullptr) { + float temp = this->read_temp(); + this->temperature_sensor_->publish_state(temp); + ESP_LOGD(TAG, "Temp=%.1f°C", temp); + } + + if (this->humidity_sensor_ != nullptr) { + float humidity = this->read_humidity(); + this->humidity_sensor_->publish_state(humidity); + ESP_LOGD(TAG, "Humidity=%.1f%%", humidity); + } + }); +} + +float HDC2010Component::read_temp() { + uint8_t byte[2]; + + this->read_register(HDC2010_CMD_TEMPERATURE_LOW, &byte[0], 1); + this->read_register(HDC2010_CMD_TEMPERATURE_HIGH, &byte[1], 1); + + uint16_t temp = encode_uint16(byte[1], byte[0]); + return (float) temp * 0.0025177f - 40.0f; +} + +float HDC2010Component::read_humidity() { + uint8_t byte[2]; + + this->read_register(HDC2010_CMD_HUMIDITY_LOW, &byte[0], 1); + this->read_register(HDC2010_CMD_HUMIDITY_HIGH, &byte[1], 1); + + uint16_t humidity = encode_uint16(byte[1], byte[0]); + return (float) humidity * 0.001525879f; +} + +} // namespace hdc2010 +} // namespace esphome diff --git a/esphome/components/hdc2010/hdc2010.h b/esphome/components/hdc2010/hdc2010.h new file mode 100644 index 0000000000..52c00686e6 --- /dev/null +++ b/esphome/components/hdc2010/hdc2010.h @@ -0,0 +1,32 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace hdc2010 { + +class HDC2010Component : public PollingComponent, public i2c::I2CDevice { + public: + void set_temperature_sensor(sensor::Sensor *temperature) { this->temperature_sensor_ = temperature; } + + void set_humidity_sensor(sensor::Sensor *humidity) { this->humidity_sensor_ = humidity; } + + /// Setup the sensor and check for connection. + void setup() override; + void dump_config() override; + /// Retrieve the latest sensor values. This operation takes approximately 16ms. + void update() override; + + float read_temp(); + + float read_humidity(); + + protected: + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; +}; + +} // namespace hdc2010 +} // namespace esphome diff --git a/esphome/components/hdc2010/sensor.py b/esphome/components/hdc2010/sensor.py new file mode 100644 index 0000000000..15e19f2cc8 --- /dev/null +++ b/esphome/components/hdc2010/sensor.py @@ -0,0 +1,56 @@ +import esphome.codegen as cg +from esphome.components import i2c, sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_PERCENT, +) + +DEPENDENCIES = ["i2c"] + +hdc2010_ns = cg.esphome_ns.namespace("hdc2010") +HDC2010Component = hdc2010_ns.class_( + "HDC2010Component", cg.PollingComponent, i2c.I2CDevice +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HDC2010Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x40)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + if temperature_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temperature_config) + cg.add(var.set_temperature_sensor(sens)) + + if humidity_config := config.get(CONF_HUMIDITY): + sens = await sensor.new_sensor(humidity_config) + cg.add(var.set_humidity_sensor(sens)) diff --git a/tests/components/hdc2010/common.yaml b/tests/components/hdc2010/common.yaml new file mode 100644 index 0000000000..a22b3f15ce --- /dev/null +++ b/tests/components/hdc2010/common.yaml @@ -0,0 +1,7 @@ +sensor: + - platform: hdc2010 + i2c_id: i2c_bus + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hdc2010/test.esp32-c3-idf.yaml b/tests/components/hdc2010/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..9990d96d29 --- /dev/null +++ b/tests/components/hdc2010/test.esp32-c3-idf.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/hdc2010/test.esp32-idf.yaml b/tests/components/hdc2010/test.esp32-idf.yaml new file mode 100644 index 0000000000..b47e39c389 --- /dev/null +++ b/tests/components/hdc2010/test.esp32-idf.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/hdc2010/test.esp8266-ard.yaml b/tests/components/hdc2010/test.esp8266-ard.yaml new file mode 100644 index 0000000000..4a98b9388a --- /dev/null +++ b/tests/components/hdc2010/test.esp8266-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + +<<: !include common.yaml diff --git a/tests/components/hdc2010/test.rp2040-ard.yaml b/tests/components/hdc2010/test.rp2040-ard.yaml new file mode 100644 index 0000000000..319a7c71a6 --- /dev/null +++ b/tests/components/hdc2010/test.rp2040-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + +<<: !include common.yaml From d23e25f0991cb20900c0de5576218c3623d7a59e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Oct 2025 16:31:51 -1000 Subject: [PATCH 244/526] [api] Fix clang-tidy modernize-use-emplace warning for light effects (#11490) --- esphome/components/api/api_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 7c135946f8..f76080253d 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -486,7 +486,7 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c if (light->supports_effects()) { msg.effects.emplace_back("None"); for (auto *effect : light->get_effects()) { - msg.effects.push_back(effect->get_name()); + msg.effects.emplace_back(effect->get_name()); } } return fill_and_encode_entity_info(light, msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size, From 6c2ce5cacff9bf151642ee8c2185349de697c420 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 16:36:30 -1000 Subject: [PATCH 245/526] Bump bleak from 1.0.1 to 1.1.1 (#11492) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6966ebe583..59592ec0a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,7 +22,7 @@ pillow==11.3.0 cairosvg==2.8.2 freetype-py==2.5.1 jinja2==3.1.6 -bleak==1.0.1 +bleak==1.1.1 # esp-idf >= 5.0 requires this pyparsing >= 3.0 From 5b023f9369e3ce84e4f8b5717928bd9bbc1e735c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Oct 2025 16:37:50 -1000 Subject: [PATCH 246/526] [ethernet] Add RMII GPIO pin conflict validation (#11488) --- esphome/components/ethernet/__init__.py | 84 ++++++++++++++++++- tests/components/ethernet/common-dp83848.yaml | 4 +- tests/components/ethernet/common-ip101.yaml | 4 +- tests/components/ethernet/common-jl1101.yaml | 4 +- tests/components/ethernet/common-ksz8081.yaml | 4 +- .../ethernet/common-ksz8081rna.yaml | 4 +- tests/components/ethernet/common-lan8670.yaml | 4 +- tests/components/ethernet/common-lan8720.yaml | 4 +- tests/components/ethernet/common-rtl8201.yaml | 4 +- tests/components/ethernet_info/common.yaml | 4 +- 10 files changed, 98 insertions(+), 22 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 7384bb26d3..77f70a3630 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -32,6 +32,7 @@ from esphome.const import ( CONF_MISO_PIN, CONF_MODE, CONF_MOSI_PIN, + CONF_NUMBER, CONF_PAGE_ID, CONF_PIN, CONF_POLLING_INTERVAL, @@ -52,12 +53,36 @@ from esphome.core import ( coroutine_with_priority, ) import esphome.final_validate as fv +from esphome.types import ConfigType CONFLICTS_WITH = ["wifi"] DEPENDENCIES = ["esp32"] AUTO_LOAD = ["network"] LOGGER = logging.getLogger(__name__) +# RMII pins that are hardcoded on ESP32 classic and cannot be changed +# These pins are used by the internal Ethernet MAC when using RMII PHYs +ESP32_RMII_FIXED_PINS = { + 19: "EMAC_TXD0", + 21: "EMAC_TX_EN", + 22: "EMAC_TXD1", + 25: "EMAC_RXD0", + 26: "EMAC_RXD1", + 27: "EMAC_RX_CRS_DV", +} + +# RMII default pins for ESP32-P4 +# These are the default pins used by ESP-IDF and are configurable in principle, +# but ESPHome's ethernet component currently has no way to change them +ESP32P4_RMII_DEFAULT_PINS = { + 34: "EMAC_TXD0", + 35: "EMAC_TXD1", + 28: "EMAC_RX_CRS_DV", + 29: "EMAC_RXD0", + 30: "EMAC_RXD1", + 49: "EMAC_TX_EN", +} + ethernet_ns = cg.esphome_ns.namespace("ethernet") PHYRegister = ethernet_ns.struct("PHYRegister") CONF_PHY_ADDR = "phy_addr" @@ -273,7 +298,7 @@ CONFIG_SCHEMA = cv.All( ) -def _final_validate(config): +def _final_validate_spi(config): if config[CONF_TYPE] not in SPI_ETHERNET_TYPES: return if spi_configs := fv.full_config.get().get(CONF_SPI): @@ -292,9 +317,6 @@ def _final_validate(config): ) -FINAL_VALIDATE_SCHEMA = _final_validate - - def manual_ip(config): return cg.StructInitializer( ManualIP, @@ -383,3 +405,57 @@ async def to_code(config): if CORE.using_arduino: cg.add_library("WiFi", None) + + +def _final_validate_rmii_pins(config: ConfigType) -> None: + """Validate that RMII pins are not used by other components.""" + # Only validate for RMII-based PHYs on ESP32/ESP32P4 + if config[CONF_TYPE] in SPI_ETHERNET_TYPES or config[CONF_TYPE] == "OPENETH": + return # SPI and OPENETH don't use RMII + + variant = get_esp32_variant() + if variant == VARIANT_ESP32: + rmii_pins = ESP32_RMII_FIXED_PINS + is_configurable = False + elif variant == VARIANT_ESP32P4: + rmii_pins = ESP32P4_RMII_DEFAULT_PINS + is_configurable = True + else: + return # No RMII validation needed for other variants + + # Check all used pins against RMII reserved pins + for pin_list in pins.PIN_SCHEMA_REGISTRY.pins_used.values(): + for pin_path, _, pin_config in pin_list: + pin_num = pin_config.get(CONF_NUMBER) + if pin_num not in rmii_pins: + continue + # Found a conflict - show helpful error message + pin_function = rmii_pins[pin_num] + component_path = ".".join(str(p) for p in pin_path) + if is_configurable: + error_msg = ( + f"GPIO{pin_num} is used by Ethernet RMII " + f"({pin_function}) with the current default " + f"configuration. This conflicts with '{component_path}'. " + f"Please choose a different GPIO pin for " + f"'{component_path}'." + ) + else: + error_msg = ( + f"GPIO{pin_num} is reserved for Ethernet RMII " + f"({pin_function}) and cannot be used. This pin is " + f"hardcoded by ESP-IDF and cannot be changed when using " + f"RMII Ethernet PHYs. Please choose a different GPIO pin " + f"for '{component_path}'." + ) + raise cv.Invalid(error_msg, path=pin_path) + + +def _final_validate(config: ConfigType) -> ConfigType: + """Final validation for Ethernet component.""" + _final_validate_spi(config) + _final_validate_rmii_pins(config) + return config + + +FINAL_VALIDATE_SCHEMA = _final_validate diff --git a/tests/components/ethernet/common-dp83848.yaml b/tests/components/ethernet/common-dp83848.yaml index 7cedfeaf08..f9069c5fb9 100644 --- a/tests/components/ethernet/common-dp83848.yaml +++ b/tests/components/ethernet/common-dp83848.yaml @@ -1,12 +1,12 @@ ethernet: type: DP83848 mdc_pin: 23 - mdio_pin: 25 + mdio_pin: 32 clk: pin: 0 mode: CLK_EXT_IN phy_addr: 0 - power_pin: 26 + power_pin: 33 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 diff --git a/tests/components/ethernet/common-ip101.yaml b/tests/components/ethernet/common-ip101.yaml index 2dece15171..cea7a5cc35 100644 --- a/tests/components/ethernet/common-ip101.yaml +++ b/tests/components/ethernet/common-ip101.yaml @@ -1,12 +1,12 @@ ethernet: type: IP101 mdc_pin: 23 - mdio_pin: 25 + mdio_pin: 32 clk: pin: 0 mode: CLK_EXT_IN phy_addr: 0 - power_pin: 26 + power_pin: 33 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 diff --git a/tests/components/ethernet/common-jl1101.yaml b/tests/components/ethernet/common-jl1101.yaml index b6ea884102..7b0a2dfdc4 100644 --- a/tests/components/ethernet/common-jl1101.yaml +++ b/tests/components/ethernet/common-jl1101.yaml @@ -1,12 +1,12 @@ ethernet: type: JL1101 mdc_pin: 23 - mdio_pin: 25 + mdio_pin: 32 clk: pin: 0 mode: CLK_EXT_IN phy_addr: 0 - power_pin: 26 + power_pin: 33 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 diff --git a/tests/components/ethernet/common-ksz8081.yaml b/tests/components/ethernet/common-ksz8081.yaml index f70d42319e..65541832c2 100644 --- a/tests/components/ethernet/common-ksz8081.yaml +++ b/tests/components/ethernet/common-ksz8081.yaml @@ -1,12 +1,12 @@ ethernet: type: KSZ8081 mdc_pin: 23 - mdio_pin: 25 + mdio_pin: 32 clk: pin: 0 mode: CLK_EXT_IN phy_addr: 0 - power_pin: 26 + power_pin: 33 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 diff --git a/tests/components/ethernet/common-ksz8081rna.yaml b/tests/components/ethernet/common-ksz8081rna.yaml index 18efdae0e1..f04cba15b2 100644 --- a/tests/components/ethernet/common-ksz8081rna.yaml +++ b/tests/components/ethernet/common-ksz8081rna.yaml @@ -1,12 +1,12 @@ ethernet: type: KSZ8081RNA mdc_pin: 23 - mdio_pin: 25 + mdio_pin: 32 clk: pin: 0 mode: CLK_EXT_IN phy_addr: 0 - power_pin: 26 + power_pin: 33 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 diff --git a/tests/components/ethernet/common-lan8670.yaml b/tests/components/ethernet/common-lan8670.yaml index ec2f24273d..fb751ebd23 100644 --- a/tests/components/ethernet/common-lan8670.yaml +++ b/tests/components/ethernet/common-lan8670.yaml @@ -1,12 +1,12 @@ ethernet: type: LAN8670 mdc_pin: 23 - mdio_pin: 25 + mdio_pin: 32 clk: pin: 0 mode: CLK_EXT_IN phy_addr: 0 - power_pin: 26 + power_pin: 33 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 diff --git a/tests/components/ethernet/common-lan8720.yaml b/tests/components/ethernet/common-lan8720.yaml index 204c1d9210..838d57df28 100644 --- a/tests/components/ethernet/common-lan8720.yaml +++ b/tests/components/ethernet/common-lan8720.yaml @@ -1,12 +1,12 @@ ethernet: type: LAN8720 mdc_pin: 23 - mdio_pin: 25 + mdio_pin: 32 clk: pin: 0 mode: CLK_EXT_IN phy_addr: 0 - power_pin: 26 + power_pin: 33 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 diff --git a/tests/components/ethernet/common-rtl8201.yaml b/tests/components/ethernet/common-rtl8201.yaml index 8b9f2b86f2..0e7cbe73c6 100644 --- a/tests/components/ethernet/common-rtl8201.yaml +++ b/tests/components/ethernet/common-rtl8201.yaml @@ -1,12 +1,12 @@ ethernet: type: RTL8201 mdc_pin: 23 - mdio_pin: 25 + mdio_pin: 32 clk: pin: 0 mode: CLK_EXT_IN phy_addr: 0 - power_pin: 26 + power_pin: 33 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 diff --git a/tests/components/ethernet_info/common.yaml b/tests/components/ethernet_info/common.yaml index f45f345316..b720521d10 100644 --- a/tests/components/ethernet_info/common.yaml +++ b/tests/components/ethernet_info/common.yaml @@ -1,12 +1,12 @@ ethernet: type: LAN8720 mdc_pin: 23 - mdio_pin: 25 + mdio_pin: 32 clk: pin: 0 mode: CLK_EXT_IN phy_addr: 0 - power_pin: 26 + power_pin: 33 manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 From 3d21adecd335e683b695ccfc00175da442214893 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Oct 2025 02:58:09 +0000 Subject: [PATCH 247/526] Bump aioesphomeapi from 42.2.0 to 42.3.0 (#11493) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 59592ec0a2..351143591a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==42.2.0 +aioesphomeapi==42.3.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.15 # dashboard_import From 917deac7cb7d6747f06a3c9546be85b3433e29b5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Oct 2025 18:02:19 -1000 Subject: [PATCH 248/526] [scheduler] Remove unused include after defer queue optimization (#11491) --- esphome/core/scheduler.h | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index ad0ec0284e..df0be0e4ce 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -4,7 +4,6 @@ #include #include #include -#include #ifdef ESPHOME_THREAD_MULTI_ATOMICS #include #endif From ab14c0cd723ccd9979cf30fed7fa2d12eedf1328 Mon Sep 17 00:00:00 2001 From: Patrick Date: Thu, 23 Oct 2025 17:32:02 +0200 Subject: [PATCH 249/526] [pipsolar] improve sensor readout in HA, set unknown state on timeout / error (#10292) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- esphome/components/pipsolar/pipsolar.cpp | 97 +++++++++---------- esphome/components/pipsolar/pipsolar.h | 3 + .../components/pipsolar/sensor/__init__.py | 96 +++++++++++++----- 3 files changed, 120 insertions(+), 76 deletions(-) diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index b92cc3be9f..bafd5273da 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -38,7 +38,6 @@ void Pipsolar::loop() { } if (this->state_ == STATE_COMMAND_COMPLETE) { if (this->check_incoming_length_(4)) { - ESP_LOGD(TAG, "response length for command OK"); if (this->check_incoming_crc_()) { // crc ok if (this->read_buffer_[1] == 'A' && this->read_buffer_[2] == 'C' && this->read_buffer_[3] == 'K') { @@ -49,15 +48,15 @@ void Pipsolar::loop() { this->command_queue_[this->command_queue_position_] = std::string(""); this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH; this->state_ = STATE_IDLE; - } else { // crc failed + // no log message necessary, check_incoming_crc_() logs this->command_queue_[this->command_queue_position_] = std::string(""); this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH; this->state_ = STATE_IDLE; } } else { - ESP_LOGD(TAG, "response length for command %s not OK: with length %zu", + ESP_LOGD(TAG, "command %s response length not OK: with length %zu", this->command_queue_[this->command_queue_position_].c_str(), this->read_pos_); this->command_queue_[this->command_queue_position_] = std::string(""); this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH; @@ -66,46 +65,10 @@ void Pipsolar::loop() { } if (this->state_ == STATE_POLL_CHECKED) { - switch (this->enabled_polling_commands_[this->last_polling_command_].identifier) { - case POLLING_QPIRI: - ESP_LOGD(TAG, "Decode QPIRI"); - handle_qpiri_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QPIGS: - ESP_LOGD(TAG, "Decode QPIGS"); - handle_qpigs_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QMOD: - ESP_LOGD(TAG, "Decode QMOD"); - handle_qmod_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QFLAG: - ESP_LOGD(TAG, "Decode QFLAG"); - handle_qflag_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QPIWS: - ESP_LOGD(TAG, "Decode QPIWS"); - handle_qpiws_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QT: - ESP_LOGD(TAG, "Decode QT"); - handle_qt_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QMN: - ESP_LOGD(TAG, "Decode QMN"); - handle_qmn_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - default: - this->state_ = STATE_IDLE; - break; - } + ESP_LOGD(TAG, "poll %s decode", this->enabled_polling_commands_[this->last_polling_command_].command); + this->handle_poll_response_(this->enabled_polling_commands_[this->last_polling_command_].identifier, + (const char *) this->read_buffer_); + this->state_ = STATE_IDLE; return; } @@ -113,6 +76,8 @@ void Pipsolar::loop() { if (this->check_incoming_crc_()) { if (this->read_buffer_[0] == '(' && this->read_buffer_[1] == 'N' && this->read_buffer_[2] == 'A' && this->read_buffer_[3] == 'K') { + ESP_LOGD(TAG, "poll %s NACK", this->enabled_polling_commands_[this->last_polling_command_].command); + this->handle_poll_error_(this->enabled_polling_commands_[this->last_polling_command_].identifier); this->state_ = STATE_IDLE; return; } @@ -121,6 +86,9 @@ void Pipsolar::loop() { this->state_ = STATE_POLL_CHECKED; return; } else { + // crc failed + // no log message necessary, check_incoming_crc_() logs + this->handle_poll_error_(this->enabled_polling_commands_[this->last_polling_command_].identifier); this->state_ = STATE_IDLE; } } @@ -158,21 +126,19 @@ void Pipsolar::loop() { // command timeout const char *command = this->command_queue_[this->command_queue_position_].c_str(); this->command_start_millis_ = millis(); - ESP_LOGD(TAG, "timeout command from queue: %s", command); + ESP_LOGD(TAG, "command %s timeout", command); this->command_queue_[this->command_queue_position_] = std::string(""); this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH; this->state_ = STATE_IDLE; return; - } else { } } if (this->state_ == STATE_POLL) { if (millis() - this->command_start_millis_ > esphome::pipsolar::Pipsolar::COMMAND_TIMEOUT) { // command timeout - ESP_LOGD(TAG, "timeout command to poll: %s", - this->enabled_polling_commands_[this->last_polling_command_].command); + ESP_LOGD(TAG, "poll %s timeout", this->enabled_polling_commands_[this->last_polling_command_].command); + this->handle_poll_error_(this->enabled_polling_commands_[this->last_polling_command_].identifier); this->state_ = STATE_IDLE; - } else { } } } @@ -187,7 +153,6 @@ uint8_t Pipsolar::check_incoming_length_(uint8_t length) { uint8_t Pipsolar::check_incoming_crc_() { uint16_t crc16; crc16 = this->pipsolar_crc_(read_buffer_, read_pos_ - 3); - ESP_LOGD(TAG, "checking crc on incoming message"); if (((uint8_t) ((crc16) >> 8)) == read_buffer_[read_pos_ - 3] && ((uint8_t) ((crc16) &0xff)) == read_buffer_[read_pos_ - 2]) { ESP_LOGD(TAG, "CRC OK"); @@ -253,7 +218,7 @@ bool Pipsolar::send_next_poll_() { this->write(((uint8_t) ((crc16) &0xff))); // lowbyte // end Byte this->write(0x0D); - ESP_LOGD(TAG, "Sending polling command : %s with length %d", + ESP_LOGD(TAG, "Sending polling command: %s with length %d", this->enabled_polling_commands_[this->last_polling_command_].command, this->enabled_polling_commands_[this->last_polling_command_].length); return true; @@ -274,6 +239,38 @@ void Pipsolar::queue_command(const std::string &command) { ESP_LOGD(TAG, "Command queue full dropping command: %s", command.c_str()); } +void Pipsolar::handle_poll_response_(ENUMPollingCommand polling_command, const char *message) { + switch (polling_command) { + case POLLING_QPIRI: + handle_qpiri_(message); + break; + case POLLING_QPIGS: + handle_qpigs_(message); + break; + case POLLING_QMOD: + handle_qmod_(message); + break; + case POLLING_QFLAG: + handle_qflag_(message); + break; + case POLLING_QPIWS: + handle_qpiws_(message); + break; + case POLLING_QT: + handle_qt_(message); + break; + case POLLING_QMN: + handle_qmn_(message); + break; + default: + break; + } +} +void Pipsolar::handle_poll_error_(ENUMPollingCommand polling_command) { + // handlers are designed in a way that an empty message sets all sensors to unknown + this->handle_poll_response_(polling_command, ""); +} + void Pipsolar::handle_qpiri_(const char *message) { if (this->last_qpiri_) { this->last_qpiri_->publish_state(message); diff --git a/esphome/components/pipsolar/pipsolar.h b/esphome/components/pipsolar/pipsolar.h index 40056bac9d..beae67a4e0 100644 --- a/esphome/components/pipsolar/pipsolar.h +++ b/esphome/components/pipsolar/pipsolar.h @@ -204,6 +204,9 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { bool send_next_command_(); bool send_next_poll_(); + void handle_poll_response_(ENUMPollingCommand polling_command, const char *message); + void handle_poll_error_(ENUMPollingCommand polling_command); + // these handlers are designed in a way that an empty message sets all sensors to unknown void handle_qpiri_(const char *message); void handle_qpigs_(const char *message); void handle_qmod_(const char *message); diff --git a/esphome/components/pipsolar/sensor/__init__.py b/esphome/components/pipsolar/sensor/__init__.py index 929865b480..d08a877b55 100644 --- a/esphome/components/pipsolar/sensor/__init__.py +++ b/esphome/components/pipsolar/sensor/__init__.py @@ -4,11 +4,18 @@ import esphome.config_validation as cv from esphome.const import ( CONF_BATTERY_VOLTAGE, CONF_BUS_VOLTAGE, + DEVICE_CLASS_APPARENT_POWER, + DEVICE_CLASS_BATTERY, DEVICE_CLASS_CURRENT, + DEVICE_CLASS_FREQUENCY, DEVICE_CLASS_POWER, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLTAGE, + ICON_BATTERY, ICON_CURRENT_AC, + ICON_FLASH, + ICON_GAUGE, + STATE_CLASS_MEASUREMENT, UNIT_AMPERE, UNIT_CELSIUS, UNIT_HERTZ, @@ -22,6 +29,10 @@ from .. import CONF_PIPSOLAR_ID, PIPSOLAR_COMPONENT_SCHEMA DEPENDENCIES = ["uart"] +ICON_SOLAR_POWER = "mdi:solar-power" +ICON_SOLAR_PANEL = "mdi:solar-panel" +ICON_CURRENT_DC = "mdi:current-dc" + # QPIRI sensors CONF_GRID_RATING_VOLTAGE = "grid_rating_voltage" CONF_GRID_RATING_CURRENT = "grid_rating_current" @@ -75,16 +86,19 @@ TYPES = { unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_GRID_RATING_CURRENT: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, accuracy_decimals=1, device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_RATING_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_RATING_FREQUENCY: sensor.sensor_schema( unit_of_measurement=UNIT_HERTZ, @@ -98,11 +112,12 @@ TYPES = { ), CONF_AC_OUTPUT_RATING_APPARENT_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS, - accuracy_decimals=1, + accuracy_decimals=0, + device_class=DEVICE_CLASS_APPARENT_POWER, ), CONF_AC_OUTPUT_RATING_ACTIVE_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_WATT, - accuracy_decimals=1, + accuracy_decimals=0, device_class=DEVICE_CLASS_POWER, ), CONF_BATTERY_RATING_VOLTAGE: sensor.sensor_schema( @@ -131,124 +146,151 @@ TYPES = { device_class=DEVICE_CLASS_VOLTAGE, ), CONF_BATTERY_TYPE: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_CURRENT_MAX_AC_CHARGING_CURRENT: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, - accuracy_decimals=1, + accuracy_decimals=0, device_class=DEVICE_CLASS_CURRENT, ), CONF_CURRENT_MAX_CHARGING_CURRENT: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, - accuracy_decimals=1, + accuracy_decimals=0, device_class=DEVICE_CLASS_CURRENT, ), CONF_INPUT_VOLTAGE_RANGE: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_OUTPUT_SOURCE_PRIORITY: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_CHARGER_SOURCE_PRIORITY: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_PARALLEL_MAX_NUM: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_MACHINE_TYPE: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_TOPOLOGY: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_OUTPUT_MODE: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_BATTERY_REDISCHARGE_VOLTAGE: sensor.sensor_schema( accuracy_decimals=1, ), CONF_PV_OK_CONDITION_FOR_PARALLEL: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_PV_POWER_BALANCE: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_GRID_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_GRID_FREQUENCY: sensor.sensor_schema( unit_of_measurement=UNIT_HERTZ, icon=ICON_CURRENT_AC, accuracy_decimals=1, + device_class=DEVICE_CLASS_FREQUENCY, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_FREQUENCY: sensor.sensor_schema( unit_of_measurement=UNIT_HERTZ, icon=ICON_CURRENT_AC, accuracy_decimals=1, + device_class=DEVICE_CLASS_FREQUENCY, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_APPARENT_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS, - accuracy_decimals=1, + accuracy_decimals=0, + device_class=DEVICE_CLASS_APPARENT_POWER, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_ACTIVE_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_WATT, - accuracy_decimals=1, + accuracy_decimals=0, device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_OUTPUT_LOAD_PERCENT: sensor.sensor_schema( unit_of_measurement=UNIT_PERCENT, - accuracy_decimals=1, + icon=ICON_GAUGE, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BUS_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, - accuracy_decimals=1, + icon=ICON_FLASH, + accuracy_decimals=0, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, - accuracy_decimals=1, + icon=ICON_BATTERY, + accuracy_decimals=2, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_CHARGING_CURRENT: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, - accuracy_decimals=1, + icon=ICON_CURRENT_DC, + accuracy_decimals=0, device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_CAPACITY_PERCENT: sensor.sensor_schema( unit_of_measurement=UNIT_PERCENT, - accuracy_decimals=1, + accuracy_decimals=0, + device_class=DEVICE_CLASS_BATTERY, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_INVERTER_HEAT_SINK_TEMPERATURE: sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, - accuracy_decimals=1, + accuracy_decimals=0, device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_PV_INPUT_CURRENT_FOR_BATTERY: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, + icon=ICON_SOLAR_PANEL, accuracy_decimals=1, device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_PV_INPUT_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, + icon=ICON_SOLAR_PANEL, accuracy_decimals=1, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_VOLTAGE_SCC: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, - accuracy_decimals=1, + accuracy_decimals=2, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_DISCHARGE_CURRENT: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, - accuracy_decimals=1, + icon=ICON_CURRENT_DC, + accuracy_decimals=0, device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_VOLTAGE_OFFSET_FOR_FANS_ON: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, @@ -256,12 +298,14 @@ TYPES = { device_class=DEVICE_CLASS_VOLTAGE, ), CONF_EEPROM_VERSION: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_PV_CHARGING_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_WATT, - accuracy_decimals=1, + icon=ICON_SOLAR_POWER, + accuracy_decimals=0, device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, ), } From 8da8095a6ab196f8dd1c69cafdb80a9a5d4050c6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 23 Oct 2025 10:11:13 -0700 Subject: [PATCH 250/526] [tests] Isolate gps component to prevent TinyGPSPlus millis() conflicts (#11499) --- script/analyze_component_buses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/script/analyze_component_buses.py b/script/analyze_component_buses.py index 78f5ca3344..38d1f8c2b7 100755 --- a/script/analyze_component_buses.py +++ b/script/analyze_component_buses.py @@ -77,6 +77,7 @@ ISOLATED_COMPONENTS = { "esphome": "Defines devices/areas in esphome: section that are referenced in other sections - breaks when merged", "ethernet": "Defines ethernet: which conflicts with wifi: used by most components", "ethernet_info": "Related to ethernet component which conflicts with wifi", + "gps": "TinyGPSPlus library declares millis() function that creates ambiguity with ESPHome millis() macro when merged with components using millis() in lambdas", "lvgl": "Defines multiple SDL displays on host platform that conflict when merged with other display configs", "mapping": "Uses dict format for image/display sections incompatible with standard list format - ESPHome merge_config cannot handle", "openthread": "Conflicts with wifi: used by most components", From e490aec6b41e4876964cb3a50249c570942c7fdf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Oct 2025 10:25:36 -0700 Subject: [PATCH 251/526] Bump ruamel-yaml from 0.18.15 to 0.18.16 (#11482) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 351143591a..4a64bd39cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ esphome-dashboard==20251013.0 aioesphomeapi==42.3.0 zeroconf==0.148.0 puremagic==1.30 -ruamel.yaml==0.18.15 # dashboard_import +ruamel.yaml==0.18.16 # dashboard_import ruamel.yaml.clib==0.2.14 # dashboard_import esphome-glyphsets==0.2.0 pillow==11.3.0 From fa3ec6f732c3b94d67feba51a820197752074a0b Mon Sep 17 00:00:00 2001 From: Markus <974709+Links2004@users.noreply.github.com> Date: Thu, 23 Oct 2025 20:32:07 +0200 Subject: [PATCH 252/526] [core] handle mixed IP and DNS addresses correctly in resolve_ip_address (#11503) Co-authored-by: J. Nick Koston --- esphome/helpers.py | 29 +++++++++++++++-------------- tests/unit_tests/test_helpers.py | 22 ++++++++++++++++++++-- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/esphome/helpers.py b/esphome/helpers.py index fb7b71775d..ea6abff50a 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -224,36 +224,37 @@ def resolve_ip_address( return res # Process hosts - cached_addresses: list[str] = [] + uncached_hosts: list[str] = [] - has_cache = address_cache is not None for h in hosts: if is_ip_address(h): - if has_cache: - # If we have a cache, treat IPs as cached - cached_addresses.append(h) - else: - # If no cache, pass IPs through to resolver with hostnames - uncached_hosts.append(h) + _add_ip_addresses_to_addrinfo([h], port, res) elif address_cache and (cached := address_cache.get_addresses(h)): - # Found in cache - cached_addresses.extend(cached) + _add_ip_addresses_to_addrinfo(cached, port, res) else: # Not cached, need to resolve if address_cache and address_cache.has_cache(): _LOGGER.info("Host %s not in cache, will need to resolve", h) uncached_hosts.append(h) - # Process cached addresses (includes direct IPs and cached lookups) - _add_ip_addresses_to_addrinfo(cached_addresses, port, res) - # If we have uncached hosts (only non-IP hostnames), resolve them if uncached_hosts: + from aioesphomeapi.host_resolver import AddrInfo as AioAddrInfo + + from esphome.core import EsphomeError from esphome.resolver import AsyncResolver resolver = AsyncResolver(uncached_hosts, port) - addr_infos = resolver.resolve() + addr_infos: list[AioAddrInfo] = [] + try: + addr_infos = resolver.resolve() + except EsphomeError as err: + if not res: + # No pre-resolved addresses available, DNS resolution is fatal + raise + _LOGGER.info("%s (using %d already resolved IP addresses)", err, len(res)) + # Convert aioesphomeapi AddrInfo to our format for addr_info in addr_infos: sockaddr = addr_info.sockaddr diff --git a/tests/unit_tests/test_helpers.py b/tests/unit_tests/test_helpers.py index 87ed901ecb..47b945e0eb 100644 --- a/tests/unit_tests/test_helpers.py +++ b/tests/unit_tests/test_helpers.py @@ -454,9 +454,27 @@ def test_resolve_ip_address_mixed_list() -> None: # Mix of IP and hostname - should use async resolver result = helpers.resolve_ip_address(["192.168.1.100", "test.local"], 6053) + assert len(result) == 2 + assert result[0][4][0] == "192.168.1.100" + assert result[1][4][0] == "192.168.1.200" + MockResolver.assert_called_once_with(["test.local"], 6053) + mock_resolver.resolve.assert_called_once() + + +def test_resolve_ip_address_mixed_list_fail() -> None: + """Test resolving a mix of IPs and hostnames with resolve failed.""" + with patch("esphome.resolver.AsyncResolver") as MockResolver: + mock_resolver = MockResolver.return_value + mock_resolver.resolve.side_effect = EsphomeError( + "Error resolving IP address: [test.local]" + ) + + # Mix of IP and hostname - should use async resolver + result = helpers.resolve_ip_address(["192.168.1.100", "test.local"], 6053) + assert len(result) == 1 - assert result[0][4][0] == "192.168.1.200" - MockResolver.assert_called_once_with(["192.168.1.100", "test.local"], 6053) + assert result[0][4][0] == "192.168.1.100" + MockResolver.assert_called_once_with(["test.local"], 6053) mock_resolver.resolve.assert_called_once() From 2440bbdceb385319821876d79f8a26d9646bdd85 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 23 Oct 2025 20:01:23 -0700 Subject: [PATCH 253/526] [core][sensor] Eliminate redundant default value setters in generated code (#11495) --- esphome/components/sensor/__init__.py | 4 +++- esphome/core/entity_helpers.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 7e91bb83c4..93283e4d47 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -878,7 +878,9 @@ async def setup_sensor_core_(var, config): cg.add(var.set_unit_of_measurement(unit_of_measurement)) if (accuracy_decimals := config.get(CONF_ACCURACY_DECIMALS)) is not None: cg.add(var.set_accuracy_decimals(accuracy_decimals)) - cg.add(var.set_force_update(config[CONF_FORCE_UPDATE])) + # Only set force_update if True (default is False) + if config[CONF_FORCE_UPDATE]: + cg.add(var.set_force_update(True)) if config.get(CONF_FILTERS): # must exist and not be empty filters = await build_filters(config[CONF_FILTERS]) cg.add(var.set_filters(filters)) diff --git a/esphome/core/entity_helpers.py b/esphome/core/entity_helpers.py index f0a04b4860..9b4786f835 100644 --- a/esphome/core/entity_helpers.py +++ b/esphome/core/entity_helpers.py @@ -105,7 +105,9 @@ async def setup_entity(var: MockObj, config: ConfigType, platform: str) -> None: config[CONF_NAME], platform, ) - add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) + # Only set disabled_by_default if True (default is False) + if config[CONF_DISABLED_BY_DEFAULT]: + add(var.set_disabled_by_default(True)) if CONF_INTERNAL in config: add(var.set_internal(config[CONF_INTERNAL])) if CONF_ICON in config: From 2c85ba037ec96974b077fe471f94d96e623209d5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 23 Oct 2025 20:01:48 -0700 Subject: [PATCH 254/526] [http_request] Pass collect_headers by const reference instead of by value (#11494) --- esphome/components/http_request/http_request.h | 2 +- esphome/components/http_request/http_request_arduino.cpp | 2 +- esphome/components/http_request/http_request_arduino.h | 2 +- esphome/components/http_request/http_request_host.cpp | 2 +- esphome/components/http_request/http_request_host.h | 2 +- esphome/components/http_request/http_request_idf.cpp | 2 +- esphome/components/http_request/http_request_idf.h | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index bb14cc6f51..40c85d51ed 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -169,7 +169,7 @@ class HttpRequestComponent : public Component { protected: virtual std::shared_ptr perform(const std::string &url, const std::string &method, const std::string &body, const std::list
&request_headers, - std::set collect_headers) = 0; + const std::set &collect_headers) = 0; const char *useragent_{nullptr}; bool follow_redirects_{}; uint16_t redirect_limit_{}; diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index dfdbbd3fab..c64a7be554 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -17,7 +17,7 @@ static const char *const TAG = "http_request.arduino"; std::shared_ptr HttpRequestArduino::perform(const std::string &url, const std::string &method, const std::string &body, const std::list
&request_headers, - std::set collect_headers) { + const std::set &collect_headers) { if (!network::is_connected()) { this->status_momentary_error("failed", 1000); ESP_LOGW(TAG, "HTTP Request failed; Not connected to network"); diff --git a/esphome/components/http_request/http_request_arduino.h b/esphome/components/http_request/http_request_arduino.h index c8208c74d8..b736bb56d1 100644 --- a/esphome/components/http_request/http_request_arduino.h +++ b/esphome/components/http_request/http_request_arduino.h @@ -33,7 +33,7 @@ class HttpRequestArduino : public HttpRequestComponent { protected: std::shared_ptr perform(const std::string &url, const std::string &method, const std::string &body, const std::list
&request_headers, - std::set collect_headers) override; + const std::set &collect_headers) override; }; } // namespace http_request diff --git a/esphome/components/http_request/http_request_host.cpp b/esphome/components/http_request/http_request_host.cpp index c20ea552b7..402affc1d1 100644 --- a/esphome/components/http_request/http_request_host.cpp +++ b/esphome/components/http_request/http_request_host.cpp @@ -20,7 +20,7 @@ static const char *const TAG = "http_request.host"; std::shared_ptr HttpRequestHost::perform(const std::string &url, const std::string &method, const std::string &body, const std::list
&request_headers, - std::set response_headers) { + const std::set &response_headers) { if (!network::is_connected()) { this->status_momentary_error("failed", 1000); ESP_LOGW(TAG, "HTTP Request failed; Not connected to network"); diff --git a/esphome/components/http_request/http_request_host.h b/esphome/components/http_request/http_request_host.h index fdd72e7ea5..886ba94938 100644 --- a/esphome/components/http_request/http_request_host.h +++ b/esphome/components/http_request/http_request_host.h @@ -20,7 +20,7 @@ class HttpRequestHost : public HttpRequestComponent { public: std::shared_ptr perform(const std::string &url, const std::string &method, const std::string &body, const std::list
&request_headers, - std::set response_headers) override; + const std::set &response_headers) override; void set_ca_path(const char *ca_path) { this->ca_path_ = ca_path; } protected: diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index a91c0bfc25..34a3fb87eb 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -55,7 +55,7 @@ esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) { std::shared_ptr HttpRequestIDF::perform(const std::string &url, const std::string &method, const std::string &body, const std::list
&request_headers, - std::set collect_headers) { + const std::set &collect_headers) { if (!network::is_connected()) { this->status_momentary_error("failed", 1000); ESP_LOGE(TAG, "HTTP Request failed; Not connected to network"); diff --git a/esphome/components/http_request/http_request_idf.h b/esphome/components/http_request/http_request_idf.h index 90dee0be68..e51b3aaebc 100644 --- a/esphome/components/http_request/http_request_idf.h +++ b/esphome/components/http_request/http_request_idf.h @@ -39,7 +39,7 @@ class HttpRequestIDF : public HttpRequestComponent { protected: std::shared_ptr perform(const std::string &url, const std::string &method, const std::string &body, const std::list
&request_headers, - std::set collect_headers) override; + const std::set &collect_headers) override; // if zero ESP-IDF will use DEFAULT_HTTP_BUF_SIZE uint16_t buffer_size_rx_{}; uint16_t buffer_size_tx_{}; From 6929bdb4151f6a970b12a357d202d98596ecd115 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Fri, 24 Oct 2025 15:01:30 -0400 Subject: [PATCH 255/526] [remote_transmitter] Remove delays and use RMT instead (#11505) --- .../remote_transmitter/remote_transmitter.h | 26 +++- .../remote_transmitter_esp32.cpp | 145 +++++++++++++++++- 2 files changed, 167 insertions(+), 4 deletions(-) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index aa1f54911d..b5d8e8d83f 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -12,6 +12,25 @@ namespace esphome { namespace remote_transmitter { +#ifdef USE_ESP32 +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) +// IDF version 5.5.1 and above is required because of a bug in +// the RMT encoder: https://github.com/espressif/esp-idf/issues/17244 +typedef union { // NOLINT(modernize-use-using) + struct { + uint16_t duration : 15; + uint16_t level : 1; + }; + uint16_t val; +} rmt_symbol_half_t; + +struct RemoteTransmitterComponentStore { + uint32_t times{0}; + uint32_t index{0}; +}; +#endif +#endif + class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, public Component #ifdef USE_ESP32 @@ -56,9 +75,14 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #ifdef USE_ESP32 void configure_rmt_(); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) + RemoteTransmitterComponentStore store_{}; + std::vector rmt_temp_; +#else + std::vector rmt_temp_; +#endif uint32_t current_carrier_frequency_{38000}; bool initialized_{false}; - std::vector rmt_temp_; bool with_dma_{false}; bool eot_level_{false}; rmt_channel_handle_t channel_{NULL}; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index 119aa81e7e..27bbf3c210 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -10,6 +10,46 @@ namespace remote_transmitter { static const char *const TAG = "remote_transmitter"; +// Maximum RMT symbol duration (15-bit field) +static constexpr uint32_t RMT_SYMBOL_DURATION_MAX = 0x7FFF; + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) +static size_t IRAM_ATTR HOT encoder_callback(const void *data, size_t size, size_t written, size_t free, + rmt_symbol_word_t *symbols, bool *done, void *arg) { + auto *store = static_cast(arg); + const auto *encoded = static_cast(data); + size_t length = size / sizeof(rmt_symbol_half_t); + size_t count = 0; + + // copy symbols + for (size_t i = 0; i < free; i++) { + uint16_t sym_0 = encoded[store->index++].val; + if (store->index >= length) { + store->index = 0; + store->times--; + if (store->times == 0) { + *done = true; + symbols[count++].val = sym_0; + return count; + } + } + uint16_t sym_1 = encoded[store->index++].val; + if (store->index >= length) { + store->index = 0; + store->times--; + if (store->times == 0) { + *done = true; + symbols[count++].val = sym_0 | (sym_1 << 16); + return count; + } + } + symbols[count++].val = sym_0 | (sym_1 << 16); + } + *done = false; + return count; +} +#endif + void RemoteTransmitterComponent::setup() { this->inverted_ = this->pin_->is_inverted(); this->configure_rmt_(); @@ -34,6 +74,17 @@ void RemoteTransmitterComponent::dump_config() { } void RemoteTransmitterComponent::digital_write(bool value) { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) + rmt_symbol_half_t symbol = { + .duration = 1, + .level = value, + }; + rmt_transmit_config_t config; + memset(&config, 0, sizeof(config)); + config.flags.eot_level = value; + this->store_.times = 1; + this->store_.index = 0; +#else rmt_symbol_word_t symbol = { .duration0 = 1, .level0 = value, @@ -42,8 +93,8 @@ void RemoteTransmitterComponent::digital_write(bool value) { }; rmt_transmit_config_t config; memset(&config, 0, sizeof(config)); - config.loop_count = 0; config.flags.eot_level = value; +#endif esp_err_t error = rmt_transmit(this->channel_, this->encoder_, &symbol, sizeof(symbol), &config); if (error != ESP_OK) { ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error)); @@ -90,6 +141,20 @@ void RemoteTransmitterComponent::configure_rmt_() { gpio_pullup_dis(gpio_num_t(this->pin_->get_pin())); } +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) + rmt_simple_encoder_config_t encoder; + memset(&encoder, 0, sizeof(encoder)); + encoder.callback = encoder_callback; + encoder.arg = &this->store_; + encoder.min_chunk_size = 1; + error = rmt_new_simple_encoder(&encoder, &this->encoder_); + if (error != ESP_OK) { + this->error_code_ = error; + this->error_string_ = "in rmt_new_simple_encoder"; + this->mark_failed(); + return; + } +#else rmt_copy_encoder_config_t encoder; memset(&encoder, 0, sizeof(encoder)); error = rmt_new_copy_encoder(&encoder, &this->encoder_); @@ -99,6 +164,7 @@ void RemoteTransmitterComponent::configure_rmt_() { this->mark_failed(); return; } +#endif error = rmt_enable(this->channel_); if (error != ESP_OK) { @@ -130,6 +196,79 @@ void RemoteTransmitterComponent::configure_rmt_() { } } +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) +void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { + if (this->is_failed()) { + return; + } + + if (this->current_carrier_frequency_ != this->temp_.get_carrier_frequency()) { + this->current_carrier_frequency_ = this->temp_.get_carrier_frequency(); + this->configure_rmt_(); + } + + this->rmt_temp_.clear(); + this->rmt_temp_.reserve(this->temp_.get_data().size() + 1); + + // encode any delay at the start of the buffer to simplify the encoder callback + // this will be skipped the first time around + send_wait = this->from_microseconds_(static_cast(send_wait)); + while (send_wait > 0) { + int32_t duration = std::min(send_wait, uint32_t(RMT_SYMBOL_DURATION_MAX)); + this->rmt_temp_.push_back({ + .duration = static_cast(duration), + .level = static_cast(this->eot_level_), + }); + send_wait -= duration; + } + + // encode data + size_t offset = this->rmt_temp_.size(); + for (int32_t value : this->temp_.get_data()) { + bool level = value >= 0; + if (!level) { + value = -value; + } + value = this->from_microseconds_(static_cast(value)); + while (value > 0) { + int32_t duration = std::min(value, int32_t(RMT_SYMBOL_DURATION_MAX)); + this->rmt_temp_.push_back({ + .duration = static_cast(duration), + .level = static_cast(level ^ this->inverted_), + }); + value -= duration; + } + } + + if ((this->rmt_temp_.data() == nullptr) || this->rmt_temp_.size() <= offset) { + ESP_LOGE(TAG, "Empty data"); + return; + } + + this->transmit_trigger_->trigger(); + + rmt_transmit_config_t config; + memset(&config, 0, sizeof(config)); + config.flags.eot_level = this->eot_level_; + this->store_.times = send_times; + this->store_.index = offset; + esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(), + this->rmt_temp_.size() * sizeof(rmt_symbol_half_t), &config); + if (error != ESP_OK) { + ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error)); + this->status_set_warning(); + } else { + this->status_clear_warning(); + } + error = rmt_tx_wait_all_done(this->channel_, -1); + if (error != ESP_OK) { + ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error)); + this->status_set_warning(); + } + + this->complete_trigger_->trigger(); +} +#else void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { if (this->is_failed()) return; @@ -151,7 +290,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen val = this->from_microseconds_(static_cast(val)); do { - int32_t item = std::min(val, int32_t(32767)); + int32_t item = std::min(val, int32_t(RMT_SYMBOL_DURATION_MAX)); val -= item; if (rmt_i % 2 == 0) { @@ -180,7 +319,6 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen for (uint32_t i = 0; i < send_times; i++) { rmt_transmit_config_t config; memset(&config, 0, sizeof(config)); - config.loop_count = 0; config.flags.eot_level = this->eot_level_; esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(), this->rmt_temp_.size() * sizeof(rmt_symbol_word_t), &config); @@ -200,6 +338,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen } this->complete_trigger_->trigger(); } +#endif } // namespace remote_transmitter } // namespace esphome From 5fdd90c71a4479992dbb93460afd3f6ae89aeaab Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Sat, 25 Oct 2025 03:27:39 -0400 Subject: [PATCH 256/526] [esp32] Add IDF 5.4.3 to platform list and switch to tar.xz (#11528) --- esphome/components/esp32/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 48d11f46fa..ddb8dbb1f0 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -304,9 +304,13 @@ def _format_framework_arduino_version(ver: cv.Version) -> str: def _format_framework_espidf_version(ver: cv.Version, release: str) -> str: # format the given espidf (https://github.com/pioarduino/esp-idf/releases) version to # a PIO platformio/framework-espidf value + if ver == cv.Version(5, 4, 3) or ver >= cv.Version(5, 5, 1): + ext = "tar.xz" + else: + ext = "zip" if release: - return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}.{release}/esp-idf-v{str(ver)}.zip" - return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.zip" + return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}.{release}/esp-idf-v{str(ver)}.{ext}" + return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.{ext}" def _is_framework_url(source: str) -> str: @@ -355,6 +359,7 @@ ESP_IDF_FRAMEWORK_VERSION_LOOKUP = { ESP_IDF_PLATFORM_VERSION_LOOKUP = { cv.Version(5, 5, 1): cv.Version(55, 3, 31, "1"), cv.Version(5, 5, 0): cv.Version(55, 3, 31, "1"), + cv.Version(5, 4, 3): cv.Version(55, 3, 32), cv.Version(5, 4, 2): cv.Version(54, 3, 21, "2"), cv.Version(5, 4, 1): cv.Version(54, 3, 21, "2"), cv.Version(5, 4, 0): cv.Version(54, 3, 21, "2"), From e212ed024d80bf6b03b0ce5786ca5c294f791e27 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 25 Oct 2025 10:00:43 -0700 Subject: [PATCH 257/526] [sntp] Replace std::vector with std::array to save heap memory (#11525) --- esphome/components/sntp/sntp_component.cpp | 6 +++--- esphome/components/sntp/sntp_component.h | 14 +++++++++----- esphome/components/sntp/time.py | 5 +++++ esphome/core/defines.h | 1 + 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 1cca5e8043..331a9b3509 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -27,7 +27,7 @@ void SNTPComponent::setup() { esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL); size_t i = 0; for (auto &server : this->servers_) { - esp_sntp_setservername(i++, server.c_str()); + esp_sntp_setservername(i++, server); } esp_sntp_set_sync_interval(this->get_update_interval()); esp_sntp_set_time_sync_notification_cb([](struct timeval *tv) { @@ -42,7 +42,7 @@ void SNTPComponent::setup() { size_t i = 0; for (auto &server : this->servers_) { - sntp_setservername(i++, server.c_str()); + sntp_setservername(i++, server); } #if defined(USE_ESP8266) @@ -59,7 +59,7 @@ void SNTPComponent::dump_config() { ESP_LOGCONFIG(TAG, "SNTP Time:"); size_t i = 0; for (auto &server : this->servers_) { - ESP_LOGCONFIG(TAG, " Server %zu: '%s'", i++, server.c_str()); + ESP_LOGCONFIG(TAG, " Server %zu: '%s'", i++, server); } } void SNTPComponent::update() { diff --git a/esphome/components/sntp/sntp_component.h b/esphome/components/sntp/sntp_component.h index dd4c71e082..8f2e411c18 100644 --- a/esphome/components/sntp/sntp_component.h +++ b/esphome/components/sntp/sntp_component.h @@ -2,10 +2,14 @@ #include "esphome/core/component.h" #include "esphome/components/time/real_time_clock.h" +#include namespace esphome { namespace sntp { +// Server count is calculated at compile time by Python codegen +// SNTP_SERVER_COUNT will always be defined + /// The SNTP component allows you to configure local timekeeping via Simple Network Time Protocol. /// /// \note @@ -14,10 +18,7 @@ namespace sntp { /// \see https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html class SNTPComponent : public time::RealTimeClock { public: - SNTPComponent(const std::vector &servers) : servers_(servers) {} - - // Note: set_servers() has been removed and replaced by a constructor - calling set_servers after setup would - // have had no effect anyway, and making the strings immutable avoids the need to strdup their contents. + SNTPComponent(const std::array &servers) : servers_(servers) {} void setup() override; void dump_config() override; @@ -29,7 +30,10 @@ class SNTPComponent : public time::RealTimeClock { void time_synced(); protected: - std::vector servers_; + // Store const char pointers to string literals + // ESP8266: strings in rodata (RAM), but avoids std::string overhead (~24 bytes each) + // Other platforms: strings in flash + std::array servers_; bool has_time_{false}; #if defined(USE_ESP32) diff --git a/esphome/components/sntp/time.py b/esphome/components/sntp/time.py index 1c8ee402ad..d27fc9991d 100644 --- a/esphome/components/sntp/time.py +++ b/esphome/components/sntp/time.py @@ -43,6 +43,11 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): servers = config[CONF_SERVERS] + + # Define server count at compile time + cg.add_define("SNTP_SERVER_COUNT", len(servers)) + + # Pass string literals to constructor - stored in flash/rodata by compiler var = cg.new_Pvariable(config[CONF_ID], servers) await cg.register_component(var, config) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 39698c1004..8095ffed4a 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -87,6 +87,7 @@ #define USE_MDNS_STORE_SERVICES #define MDNS_SERVICE_COUNT 3 #define MDNS_DYNAMIC_TXT_COUNT 3 +#define SNTP_SERVER_COUNT 3 #define USE_MEDIA_PLAYER #define USE_NEXTION_TFT_UPLOAD #define USE_NUMBER From 1577a46efdb6ab828c5b3efb7caa85888380d54d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 25 Oct 2025 22:09:42 -0700 Subject: [PATCH 258/526] [gpio] Skip set_inverted() call for default false value (#11538) --- esphome/components/esp32/gpio.h | 6 +++--- esphome/components/esp32/gpio.py | 5 ++++- esphome/components/esp8266/gpio.h | 4 ++-- esphome/components/esp8266/gpio.py | 5 ++++- esphome/components/host/gpio.h | 4 ++-- esphome/components/host/gpio.py | 5 ++++- esphome/components/libretiny/gpio.py | 5 ++++- esphome/components/libretiny/gpio_arduino.h | 4 ++-- esphome/components/nrf52/gpio.py | 5 ++++- esphome/components/rp2040/gpio.h | 4 ++-- esphome/components/rp2040/gpio.py | 5 ++++- esphome/components/zephyr/gpio.h | 8 ++++---- 12 files changed, 39 insertions(+), 21 deletions(-) diff --git a/esphome/components/esp32/gpio.h b/esphome/components/esp32/gpio.h index 565e276ea8..d30f4bdcba 100644 --- a/esphome/components/esp32/gpio.h +++ b/esphome/components/esp32/gpio.h @@ -40,13 +40,13 @@ class ESP32InternalGPIOPin : public InternalGPIOPin { // - 3 bytes for members below // - 1 byte padding for alignment // - 4 bytes for vtable pointer - uint8_t pin_; // GPIO pin number (0-255, actual max ~54 on ESP32) - gpio::Flags flags_; // GPIO flags (1 byte) + uint8_t pin_; // GPIO pin number (0-255, actual max ~54 on ESP32) + gpio::Flags flags_{}; // GPIO flags (1 byte) struct PinFlags { uint8_t inverted : 1; // Invert pin logic (1 bit) uint8_t drive_strength : 2; // Drive strength 0-3 (2 bits) uint8_t reserved : 5; // Reserved for future use (5 bits) - } pin_flags_; // Total: 1 byte + } pin_flags_{}; // Total: 1 byte // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static bool isr_service_installed; }; diff --git a/esphome/components/esp32/gpio.py b/esphome/components/esp32/gpio.py index 513f463d57..954891ea8d 100644 --- a/esphome/components/esp32/gpio.py +++ b/esphome/components/esp32/gpio.py @@ -223,7 +223,10 @@ async def esp32_pin_to_code(config): var = cg.new_Pvariable(config[CONF_ID]) num = config[CONF_NUMBER] cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}"))) - cg.add(var.set_inverted(config[CONF_INVERTED])) + # Only set if true to avoid bloating setup() function + # (inverted bit in pin_flags_ bitfield is zero-initialized to false) + if config[CONF_INVERTED]: + cg.add(var.set_inverted(True)) if CONF_DRIVE_STRENGTH in config: cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH])) cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) diff --git a/esphome/components/esp8266/gpio.h b/esphome/components/esp8266/gpio.h index dd6407885e..a1b6d79b3b 100644 --- a/esphome/components/esp8266/gpio.h +++ b/esphome/components/esp8266/gpio.h @@ -29,8 +29,8 @@ class ESP8266GPIOPin : public InternalGPIOPin { void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; uint8_t pin_; - bool inverted_; - gpio::Flags flags_; + bool inverted_{}; + gpio::Flags flags_{}; }; } // namespace esp8266 diff --git a/esphome/components/esp8266/gpio.py b/esphome/components/esp8266/gpio.py index e7492fc505..2e8d6496bc 100644 --- a/esphome/components/esp8266/gpio.py +++ b/esphome/components/esp8266/gpio.py @@ -165,7 +165,10 @@ async def esp8266_pin_to_code(config): num = config[CONF_NUMBER] mode = config[CONF_MODE] cg.add(var.set_pin(num)) - cg.add(var.set_inverted(config[CONF_INVERTED])) + # Only set if true to avoid bloating setup() function + # (inverted bit in pin_flags_ bitfield is zero-initialized to false) + if config[CONF_INVERTED]: + cg.add(var.set_inverted(True)) cg.add(var.set_flags(pins.gpio_flags_expr(mode))) if num < 16: initial_state: PinInitialState = CORE.data[KEY_ESP8266][KEY_PIN_INITIAL_STATES][ diff --git a/esphome/components/host/gpio.h b/esphome/components/host/gpio.h index a60d535912..ae677291b9 100644 --- a/esphome/components/host/gpio.h +++ b/esphome/components/host/gpio.h @@ -28,8 +28,8 @@ class HostGPIOPin : public InternalGPIOPin { void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; uint8_t pin_; - bool inverted_; - gpio::Flags flags_; + bool inverted_{}; + gpio::Flags flags_{}; }; } // namespace host diff --git a/esphome/components/host/gpio.py b/esphome/components/host/gpio.py index 0f22a790bd..fcfb0b6c54 100644 --- a/esphome/components/host/gpio.py +++ b/esphome/components/host/gpio.py @@ -57,6 +57,9 @@ async def host_pin_to_code(config): var = cg.new_Pvariable(config[CONF_ID]) num = config[CONF_NUMBER] cg.add(var.set_pin(num)) - cg.add(var.set_inverted(config[CONF_INVERTED])) + # Only set if true to avoid bloating setup() function + # (inverted bit in pin_flags_ bitfield is zero-initialized to false) + if config[CONF_INVERTED]: + cg.add(var.set_inverted(True)) cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) return var diff --git a/esphome/components/libretiny/gpio.py b/esphome/components/libretiny/gpio.py index 07eb0ce133..9bad400eb7 100644 --- a/esphome/components/libretiny/gpio.py +++ b/esphome/components/libretiny/gpio.py @@ -199,6 +199,9 @@ async def component_pin_to_code(config): var = cg.new_Pvariable(config[CONF_ID]) num = config[CONF_NUMBER] cg.add(var.set_pin(num)) - cg.add(var.set_inverted(config[CONF_INVERTED])) + # Only set if true to avoid bloating setup() function + # (inverted bit in pin_flags_ bitfield is zero-initialized to false) + if config[CONF_INVERTED]: + cg.add(var.set_inverted(True)) cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) return var diff --git a/esphome/components/libretiny/gpio_arduino.h b/esphome/components/libretiny/gpio_arduino.h index 9adc425a41..3674748c18 100644 --- a/esphome/components/libretiny/gpio_arduino.h +++ b/esphome/components/libretiny/gpio_arduino.h @@ -27,8 +27,8 @@ class ArduinoInternalGPIOPin : public InternalGPIOPin { void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; uint8_t pin_; - bool inverted_; - gpio::Flags flags_; + bool inverted_{}; + gpio::Flags flags_{}; }; } // namespace libretiny diff --git a/esphome/components/nrf52/gpio.py b/esphome/components/nrf52/gpio.py index 260114f90e..17329042b2 100644 --- a/esphome/components/nrf52/gpio.py +++ b/esphome/components/nrf52/gpio.py @@ -74,6 +74,9 @@ async def nrf52_pin_to_code(config): var = cg.new_Pvariable(config[CONF_ID]) num = config[CONF_NUMBER] cg.add(var.set_pin(num)) - cg.add(var.set_inverted(config[CONF_INVERTED])) + # Only set if true to avoid bloating setup() function + # (inverted bit in pin_flags_ bitfield is zero-initialized to false) + if config[CONF_INVERTED]: + cg.add(var.set_inverted(True)) cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) return var diff --git a/esphome/components/rp2040/gpio.h b/esphome/components/rp2040/gpio.h index 9bc66d9e4b..47a6fe17f2 100644 --- a/esphome/components/rp2040/gpio.h +++ b/esphome/components/rp2040/gpio.h @@ -29,8 +29,8 @@ class RP2040GPIOPin : public InternalGPIOPin { void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; uint8_t pin_; - bool inverted_; - gpio::Flags flags_; + bool inverted_{}; + gpio::Flags flags_{}; }; } // namespace rp2040 diff --git a/esphome/components/rp2040/gpio.py b/esphome/components/rp2040/gpio.py index 58514f7db5..193e567d17 100644 --- a/esphome/components/rp2040/gpio.py +++ b/esphome/components/rp2040/gpio.py @@ -94,6 +94,9 @@ async def rp2040_pin_to_code(config): var = cg.new_Pvariable(config[CONF_ID]) num = config[CONF_NUMBER] cg.add(var.set_pin(num)) - cg.add(var.set_inverted(config[CONF_INVERTED])) + # Only set if true to avoid bloating setup() function + # (inverted bit in pin_flags_ bitfield is zero-initialized to false) + if config[CONF_INVERTED]: + cg.add(var.set_inverted(True)) cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) return var diff --git a/esphome/components/zephyr/gpio.h b/esphome/components/zephyr/gpio.h index f512ae4648..6e8f81857a 100644 --- a/esphome/components/zephyr/gpio.h +++ b/esphome/components/zephyr/gpio.h @@ -26,10 +26,10 @@ class ZephyrGPIOPin : public InternalGPIOPin { protected: void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; uint8_t pin_; - bool inverted_; - gpio::Flags flags_; - const device *gpio_ = nullptr; - bool value_ = false; + bool inverted_{}; + gpio::Flags flags_{}; + const device *gpio_{nullptr}; + bool value_{false}; }; } // namespace zephyr From 7394cbf77329f3b5a035e5ce334c9ac0ae19818c Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Sun, 26 Oct 2025 09:00:08 -0400 Subject: [PATCH 259/526] [core] Don't allow python 3.14 (#11527) --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b7b4a48d7e..49598d434d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,9 @@ classifiers = [ "Programming Language :: Python :: 3", "Topic :: Home Automation", ] -requires-python = ">=3.11.0" + +# Python 3.14 is currently not supported by IDF <= 5.5.1, see https://github.com/esphome/esphome/issues/11502 +requires-python = ">=3.11.0,<3.14" dynamic = ["dependencies", "optional-dependencies", "version"] From 3c18558003aab8a4ea193ae043a9847831365220 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 14:06:22 -0500 Subject: [PATCH 260/526] Optimize stateless lambdas to use function pointers (#11551) --- esphome/automation.py | 45 ++++++++++++++-- esphome/components/binary_sensor/__init__.py | 3 +- esphome/components/binary_sensor/filter.h | 15 ++++++ esphome/components/logger/__init__.py | 10 ++-- esphome/components/sensor/__init__.py | 3 +- esphome/components/sensor/filter.h | 15 ++++++ esphome/components/text_sensor/__init__.py | 3 +- esphome/components/text_sensor/filter.h | 15 ++++++ esphome/core/base_automation.h | 25 +++++++++ esphome/cpp_generator.py | 8 +++ tests/component_tests/text/test_text.py | 4 +- tests/unit_tests/test_cpp_generator.py | 55 ++++++++++++++++++++ 12 files changed, 190 insertions(+), 11 deletions(-) diff --git a/esphome/automation.py b/esphome/automation.py index 99def9f273..cfe0af1b59 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -16,7 +16,12 @@ from esphome.const import ( CONF_UPDATE_INTERVAL, ) from esphome.core import ID -from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType +from esphome.cpp_generator import ( + LambdaExpression, + MockObj, + MockObjClass, + TemplateArgsType, +) from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from esphome.types import ConfigType from esphome.util import Registry @@ -87,6 +92,7 @@ def validate_potentially_or_condition(value): DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component) LambdaAction = cg.esphome_ns.class_("LambdaAction", Action) +StatelessLambdaAction = cg.esphome_ns.class_("StatelessLambdaAction", Action) IfAction = cg.esphome_ns.class_("IfAction", Action) WhileAction = cg.esphome_ns.class_("WhileAction", Action) RepeatAction = cg.esphome_ns.class_("RepeatAction", Action) @@ -97,9 +103,40 @@ ResumeComponentAction = cg.esphome_ns.class_("ResumeComponentAction", Action) Automation = cg.esphome_ns.class_("Automation") LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition) +StatelessLambdaCondition = cg.esphome_ns.class_("StatelessLambdaCondition", Condition) ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component) +def new_lambda_pvariable( + id_obj: ID, + lambda_expr: LambdaExpression, + stateless_class: MockObjClass, + template_arg: cg.TemplateArguments | None = None, +) -> MockObj: + """Create Pvariable for lambda, using stateless class if applicable. + + Combines ID selection and Pvariable creation in one call. For stateless + lambdas (empty capture), uses function pointer instead of std::function. + + Args: + id_obj: The ID object (action_id, condition_id, or filter_id) + lambda_expr: The lambda expression object + stateless_class: The stateless class to use for stateless lambdas + template_arg: Optional template arguments (for actions/conditions) + + Returns: + The created Pvariable + """ + # For stateless lambdas, use function pointer instead of std::function + if lambda_expr.capture == "": + id_obj = id_obj.copy() + id_obj.type = stateless_class + + if template_arg is not None: + return cg.new_Pvariable(id_obj, template_arg, lambda_expr) + return cg.new_Pvariable(id_obj, lambda_expr) + + def validate_automation(extra_schema=None, extra_validators=None, single=False): if extra_schema is None: extra_schema = {} @@ -240,7 +277,9 @@ async def lambda_condition_to_code( args: TemplateArgsType, ) -> MockObj: lambda_ = await cg.process_lambda(config, args, return_type=bool) - return cg.new_Pvariable(condition_id, template_arg, lambda_) + return new_lambda_pvariable( + condition_id, lambda_, StatelessLambdaCondition, template_arg + ) @register_condition( @@ -406,7 +445,7 @@ async def lambda_action_to_code( args: TemplateArgsType, ) -> MockObj: lambda_ = await cg.process_lambda(config, args, return_type=cg.void) - return cg.new_Pvariable(action_id, template_arg, lambda_) + return new_lambda_pvariable(action_id, lambda_, StatelessLambdaAction, template_arg) @register_action( diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 26e784a0b8..8892b57e6e 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -155,6 +155,7 @@ DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Compon InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter) AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component) LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter) +StatelessLambdaFilter = binary_sensor_ns.class_("StatelessLambdaFilter", Filter) SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component) _LOGGER = getLogger(__name__) @@ -299,7 +300,7 @@ async def lambda_filter_to_code(config, filter_id): lambda_ = await cg.process_lambda( config, [(bool, "x")], return_type=cg.optional.template(bool) ) - return cg.new_Pvariable(filter_id, lambda_) + return automation.new_lambda_pvariable(filter_id, lambda_, StatelessLambdaFilter) @register_filter( diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index a7eb080feb..2d473c3b64 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -111,6 +111,21 @@ class LambdaFilter : public Filter { std::function(bool)> f_; }; +/** Optimized lambda filter for stateless lambdas (no capture). + * + * Uses function pointer instead of std::function to reduce memory overhead. + * Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function). + */ +class StatelessLambdaFilter : public Filter { + public: + explicit StatelessLambdaFilter(optional (*f)(bool)) : f_(f) {} + + optional new_value(bool value) override { return this->f_(value); } + + protected: + optional (*f_)(bool); +}; + class SettleFilter : public Filter, public Component { public: optional new_value(bool value) override; diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 1d02073d27..22bf3d2f4c 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -1,7 +1,7 @@ import re from esphome import automation -from esphome.automation import LambdaAction +from esphome.automation import LambdaAction, StatelessLambdaAction import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant from esphome.components.esp32.const import ( @@ -430,7 +430,9 @@ async def logger_log_action_to_code(config, action_id, template_arg, args): text = str(cg.statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args_))) lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void) - return cg.new_Pvariable(action_id, template_arg, lambda_) + return automation.new_lambda_pvariable( + action_id, lambda_, StatelessLambdaAction, template_arg + ) @automation.register_action( @@ -455,7 +457,9 @@ async def logger_set_level_to_code(config, action_id, template_arg, args): text = str(cg.statement(logger.set_log_level(level))) lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void) - return cg.new_Pvariable(action_id, template_arg, lambda_) + return automation.new_lambda_pvariable( + action_id, lambda_, StatelessLambdaAction, template_arg + ) FILTER_SOURCE_FILES = filter_source_files_from_platform( diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 93283e4d47..41ac3516b9 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -261,6 +261,7 @@ ExponentialMovingAverageFilter = sensor_ns.class_( ) ThrottleAverageFilter = sensor_ns.class_("ThrottleAverageFilter", Filter, cg.Component) LambdaFilter = sensor_ns.class_("LambdaFilter", Filter) +StatelessLambdaFilter = sensor_ns.class_("StatelessLambdaFilter", Filter) OffsetFilter = sensor_ns.class_("OffsetFilter", Filter) MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter) ValueListFilter = sensor_ns.class_("ValueListFilter", Filter) @@ -573,7 +574,7 @@ async def lambda_filter_to_code(config, filter_id): lambda_ = await cg.process_lambda( config, [(float, "x")], return_type=cg.optional.template(float) ) - return cg.new_Pvariable(filter_id, lambda_) + return automation.new_lambda_pvariable(filter_id, lambda_, StatelessLambdaFilter) DELTA_SCHEMA = cv.Schema( diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index ecd55308d1..75e28a1efe 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -296,6 +296,21 @@ class LambdaFilter : public Filter { lambda_filter_t lambda_filter_; }; +/** Optimized lambda filter for stateless lambdas (no capture). + * + * Uses function pointer instead of std::function to reduce memory overhead. + * Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function). + */ +class StatelessLambdaFilter : public Filter { + public: + explicit StatelessLambdaFilter(optional (*lambda_filter)(float)) : lambda_filter_(lambda_filter) {} + + optional new_value(float value) override { return this->lambda_filter_(value); } + + protected: + optional (*lambda_filter_)(float); +}; + /// A simple filter that adds `offset` to each value it receives. class OffsetFilter : public Filter { public: diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index 7a9e947abd..adc8a76fcd 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -57,6 +57,7 @@ validate_filters = cv.validate_registry("filter", FILTER_REGISTRY) # Filters Filter = text_sensor_ns.class_("Filter") LambdaFilter = text_sensor_ns.class_("LambdaFilter", Filter) +StatelessLambdaFilter = text_sensor_ns.class_("StatelessLambdaFilter", Filter) ToUpperFilter = text_sensor_ns.class_("ToUpperFilter", Filter) ToLowerFilter = text_sensor_ns.class_("ToLowerFilter", Filter) AppendFilter = text_sensor_ns.class_("AppendFilter", Filter) @@ -70,7 +71,7 @@ async def lambda_filter_to_code(config, filter_id): lambda_ = await cg.process_lambda( config, [(cg.std_string, "x")], return_type=cg.optional.template(cg.std_string) ) - return cg.new_Pvariable(filter_id, lambda_) + return automation.new_lambda_pvariable(filter_id, lambda_, StatelessLambdaFilter) @FILTER_REGISTRY.register("to_upper", ToUpperFilter, {}) diff --git a/esphome/components/text_sensor/filter.h b/esphome/components/text_sensor/filter.h index c77c221235..85acac5c8d 100644 --- a/esphome/components/text_sensor/filter.h +++ b/esphome/components/text_sensor/filter.h @@ -62,6 +62,21 @@ class LambdaFilter : public Filter { lambda_filter_t lambda_filter_; }; +/** Optimized lambda filter for stateless lambdas (no capture). + * + * Uses function pointer instead of std::function to reduce memory overhead. + * Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function). + */ +class StatelessLambdaFilter : public Filter { + public: + explicit StatelessLambdaFilter(optional (*lambda_filter)(std::string)) : lambda_filter_(lambda_filter) {} + + optional new_value(std::string value) override { return this->lambda_filter_(value); } + + protected: + optional (*lambda_filter_)(std::string); +}; + /// A simple filter that converts all text to uppercase class ToUpperFilter : public Filter { public: diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index af8cde971b..1c60dd1c7a 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -79,6 +79,18 @@ template class LambdaCondition : public Condition { std::function f_; }; +/// Optimized lambda condition for stateless lambdas (no capture). +/// Uses function pointer instead of std::function to reduce memory overhead. +/// Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function). +template class StatelessLambdaCondition : public Condition { + public: + explicit StatelessLambdaCondition(bool (*f)(Ts...)) : f_(f) {} + bool check(Ts... x) override { return this->f_(x...); } + + protected: + bool (*f_)(Ts...); +}; + template class ForCondition : public Condition, public Component { public: explicit ForCondition(Condition<> *condition) : condition_(condition) {} @@ -190,6 +202,19 @@ template class LambdaAction : public Action { std::function f_; }; +/// Optimized lambda action for stateless lambdas (no capture). +/// Uses function pointer instead of std::function to reduce memory overhead. +/// Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function). +template class StatelessLambdaAction : public Action { + public: + explicit StatelessLambdaAction(void (*f)(Ts...)) : f_(f) {} + + void play(Ts... x) override { this->f_(x...); } + + protected: + void (*f_)(Ts...); +}; + template class IfAction : public Action { public: explicit IfAction(Condition *condition) : condition_(condition) {} diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index b2022c7ae6..a2da424e5a 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -198,6 +198,8 @@ class LambdaExpression(Expression): self.return_type = safe_exp(return_type) if return_type is not None else None def __str__(self): + # Stateless lambdas (empty capture) implicitly convert to function pointers + # when assigned to function pointer types - no unary + needed cpp = f"[{self.capture}]({self.parameters})" if self.return_type is not None: cpp += f" -> {self.return_type}" @@ -700,6 +702,12 @@ async def process_lambda( parts[i * 3 + 1] = var parts[i * 3 + 2] = "" + # All id() references are global variables in generated C++ code. + # Global variables should not be captured - they're accessible everywhere. + # Use empty capture instead of capture-by-value. + if capture == "=": + capture = "" + if isinstance(value, ESPHomeDataBase) and value.esp_range is not None: location = value.esp_range.start_mark location.line += value.content_offset diff --git a/tests/component_tests/text/test_text.py b/tests/component_tests/text/test_text.py index 75f1c4b88b..99ddd78ee7 100644 --- a/tests/component_tests/text/test_text.py +++ b/tests/component_tests/text/test_text.py @@ -58,7 +58,7 @@ def test_text_config_value_mode_set(generate_main): def test_text_config_lamda_is_set(generate_main): """ - Test if lambda is set for lambda mode + Test if lambda is set for lambda mode (optimized with stateless lambda) """ # Given @@ -66,5 +66,5 @@ def test_text_config_lamda_is_set(generate_main): main_cpp = generate_main("tests/component_tests/text/test_text.yaml") # Then - assert "it_4->set_template([=]() -> esphome::optional {" in main_cpp + assert "it_4->set_template([]() -> esphome::optional {" in main_cpp assert 'return std::string{"Hello"};' in main_cpp diff --git a/tests/unit_tests/test_cpp_generator.py b/tests/unit_tests/test_cpp_generator.py index 95633ca0c6..2c9f760c8e 100644 --- a/tests/unit_tests/test_cpp_generator.py +++ b/tests/unit_tests/test_cpp_generator.py @@ -173,6 +173,61 @@ class TestLambdaExpression: "}" ) + def test_str__stateless_no_return(self): + """Test stateless lambda (empty capture) generates correctly""" + target = cg.LambdaExpression( + ('ESP_LOGD("main", "Test message");',), + (), # No parameters + "", # Empty capture (stateless) + ) + + actual = str(target) + + assert actual == ('[]() {\n ESP_LOGD("main", "Test message");\n}') + + def test_str__stateless_with_return(self): + """Test stateless lambda with return type generates correctly""" + target = cg.LambdaExpression( + ("return global_value > 0;",), + (), # No parameters + "", # Empty capture (stateless) + bool, # Return type + ) + + actual = str(target) + + assert actual == ("[]() -> bool {\n return global_value > 0;\n}") + + def test_str__stateless_with_params(self): + """Test stateless lambda with parameters generates correctly""" + target = cg.LambdaExpression( + ("return foo + bar;",), + ((int, "foo"), (float, "bar")), + "", # Empty capture (stateless) + float, + ) + + actual = str(target) + + assert actual == ( + "[](int32_t foo, float bar) -> float {\n return foo + bar;\n}" + ) + + def test_str__with_capture(self): + """Test lambda with capture generates correctly""" + target = cg.LambdaExpression( + ("return captured_var + x;",), + ((int, "x"),), + "captured_var", # Has capture (not stateless) + int, + ) + + actual = str(target) + + assert actual == ( + "[captured_var](int32_t x) -> int32_t {\n return captured_var + x;\n}" + ) + class TestLiterals: @pytest.mark.parametrize( From 51e080c2d33e762ee13f417180a8039ceeee0ee5 Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Mon, 27 Oct 2025 20:46:26 +0100 Subject: [PATCH 261/526] [substitutions] fix #11077 Preserve ESPHomeDatabase (document metadata) in substitutions (#11087) Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston --- esphome/components/substitutions/__init__.py | 58 +++++++++++++-- esphome/components/substitutions/jinja.py | 78 ++++++++++++++++---- tests/unit_tests/test_substitutions.py | 27 +++++++ 3 files changed, 140 insertions(+), 23 deletions(-) diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index 098d56bfad..7e15f714f7 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -1,4 +1,6 @@ import logging +from re import Match +from typing import Any from esphome import core from esphome.config_helpers import Extend, Remove, merge_config, merge_dicts_ordered @@ -39,7 +41,34 @@ async def to_code(config): pass -def _expand_jinja(value, orig_value, path, jinja, ignore_missing): +def _restore_data_base(value: Any, orig_value: ESPHomeDataBase) -> ESPHomeDataBase: + """This function restores ESPHomeDataBase metadata held by the original string. + This is needed because during jinja evaluation, strings can be replaced by other types, + but we want to keep the original metadata for error reporting and source mapping. + For example, if a substitution replaces a string with a dictionary, we want that items + in the dictionary to still point to the original document location + """ + if isinstance(value, ESPHomeDataBase): + return value + if isinstance(value, dict): + return { + _restore_data_base(k, orig_value): _restore_data_base(v, orig_value) + for k, v in value.items() + } + if isinstance(value, list): + return [_restore_data_base(v, orig_value) for v in value] + if isinstance(value, str): + return make_data_base(value, orig_value) + return value + + +def _expand_jinja( + value: str | JinjaStr, + orig_value: str | JinjaStr, + path, + jinja: Jinja, + ignore_missing: bool, +) -> Any: if has_jinja(value): # If the original value passed in to this function is a JinjaStr, it means it contains an unresolved # Jinja expression from a previous pass. @@ -65,10 +94,17 @@ def _expand_jinja(value, orig_value, path, jinja, ignore_missing): f"\nSee {'->'.join(str(x) for x in path)}", path, ) + # If the original, unexpanded string, contained document metadata (ESPHomeDatabase), + # assign this same document metadata to the resulting value. + if isinstance(orig_value, ESPHomeDataBase): + value = _restore_data_base(value, orig_value) + return value -def _expand_substitutions(substitutions, value, path, jinja, ignore_missing): +def _expand_substitutions( + substitutions: dict, value: str, path, jinja: Jinja, ignore_missing: bool +) -> Any: if "$" not in value: return value @@ -76,14 +112,14 @@ def _expand_substitutions(substitutions, value, path, jinja, ignore_missing): i = 0 while True: - m = cv.VARIABLE_PROG.search(value, i) + m: Match[str] = cv.VARIABLE_PROG.search(value, i) if not m: # No more variable substitutions found. See if the remainder looks like a jinja template value = _expand_jinja(value, orig_value, path, jinja, ignore_missing) break i, j = m.span(0) - name = m.group(1) + name: str = m.group(1) if name.startswith("{") and name.endswith("}"): name = name[1:-1] if name not in substitutions: @@ -98,7 +134,7 @@ def _expand_substitutions(substitutions, value, path, jinja, ignore_missing): i = j continue - sub = substitutions[name] + sub: Any = substitutions[name] if i == 0 and j == len(value): # The variable spans the whole expression, e.g., "${varName}". Return its resolved value directly @@ -121,7 +157,13 @@ def _expand_substitutions(substitutions, value, path, jinja, ignore_missing): return value -def _substitute_item(substitutions, item, path, jinja, ignore_missing): +def _substitute_item( + substitutions: dict, + item: Any, + path: list[int | str], + jinja: Jinja, + ignore_missing: bool, +) -> Any | None: if isinstance(item, ESPLiteralValue): return None # do not substitute inside literal blocks if isinstance(item, list): @@ -160,7 +202,9 @@ def _substitute_item(substitutions, item, path, jinja, ignore_missing): return None -def do_substitution_pass(config, command_line_substitutions, ignore_missing=False): +def do_substitution_pass( + config: dict, command_line_substitutions: dict, ignore_missing: bool = False +) -> None: if CONF_SUBSTITUTIONS not in config and not command_line_substitutions: return diff --git a/esphome/components/substitutions/jinja.py b/esphome/components/substitutions/jinja.py index dde0162993..cb3c6dfac5 100644 --- a/esphome/components/substitutions/jinja.py +++ b/esphome/components/substitutions/jinja.py @@ -1,10 +1,14 @@ from ast import literal_eval +from collections.abc import Iterator +from itertools import chain, islice import logging import math import re +from types import GeneratorType +from typing import Any import jinja2 as jinja -from jinja2.sandbox import SandboxedEnvironment +from jinja2.nativetypes import NativeCodeGenerator, NativeTemplate from esphome.yaml_util import ESPLiteralValue @@ -24,7 +28,7 @@ detect_jinja_re = re.compile( ) -def has_jinja(st): +def has_jinja(st: str) -> bool: return detect_jinja_re.search(st) is not None @@ -109,12 +113,56 @@ class TrackerContext(jinja.runtime.Context): return val -class Jinja(SandboxedEnvironment): +def _concat_nodes_override(values: Iterator[Any]) -> Any: + """ + This function customizes how Jinja preserves native types when concatenating + multiple result nodes together. If the result is a single node, its value + is returned. Otherwise, the nodes are concatenated as strings. If + the result can be parsed with `ast.literal_eval`, the parsed + value is returned. Otherwise, the string is returned. + This helps preserve metadata such as ESPHomeDataBase from original values + and mimicks how HomeAssistant deals with template evaluation and preserving + the original datatype. + """ + head: list[Any] = list(islice(values, 2)) + + if not head: + return None + + if len(head) == 1: + raw = head[0] + if not isinstance(raw, str): + return raw + else: + if isinstance(values, GeneratorType): + values = chain(head, values) + raw = "".join([str(v) for v in values]) + + try: + # Attempt to parse the concatenated string into a Python literal. + # This allows expressions like "1 + 2" to be evaluated to the integer 3. + # If the result is also a string or there is a parsing error, + # fall back to returning the raw string. This is consistent with + # Home Assistant's behavior when evaluating templates + result = literal_eval(raw) + if not isinstance(result, str): + return result + + except (ValueError, SyntaxError, MemoryError, TypeError): + pass + return raw + + +class Jinja(jinja.Environment): """ Wraps a Jinja environment """ - def __init__(self, context_vars): + # jinja environment customization overrides + code_generator_class = NativeCodeGenerator + concat = staticmethod(_concat_nodes_override) + + def __init__(self, context_vars: dict): super().__init__( trim_blocks=True, lstrip_blocks=True, @@ -142,19 +190,10 @@ class Jinja(SandboxedEnvironment): **SAFE_GLOBALS, } - def safe_eval(self, expr): - try: - result = literal_eval(expr) - if not isinstance(result, str): - return result - except (ValueError, SyntaxError, MemoryError, TypeError): - pass - return expr - - def expand(self, content_str): + def expand(self, content_str: str | JinjaStr) -> Any: """ Renders a string that may contain Jinja expressions or statements - Returns the resulting processed string if all values could be resolved. + Returns the resulting value if all variables and expressions could be resolved. Otherwise, it returns a tagged (JinjaStr) string that captures variables in scope (upvalues), like a closure for later evaluation. """ @@ -172,7 +211,7 @@ class Jinja(SandboxedEnvironment): self.context_trace = {} try: template = self.from_string(content_str) - result = self.safe_eval(template.render(override_vars)) + result = template.render(override_vars) if isinstance(result, Undefined): print("" + result) # force a UndefinedError exception except (TemplateSyntaxError, UndefinedError) as err: @@ -201,3 +240,10 @@ class Jinja(SandboxedEnvironment): content_str.result = result return result, None + + +class JinjaTemplate(NativeTemplate): + environment_class = Jinja + + +Jinja.template_class = JinjaTemplate diff --git a/tests/unit_tests/test_substitutions.py b/tests/unit_tests/test_substitutions.py index beb1ebc73e..7d50b44506 100644 --- a/tests/unit_tests/test_substitutions.py +++ b/tests/unit_tests/test_substitutions.py @@ -1,6 +1,7 @@ import glob import logging from pathlib import Path +from typing import Any from esphome import config as config_module, yaml_util from esphome.components import substitutions @@ -60,6 +61,29 @@ def write_yaml(path: Path, data: dict) -> None: path.write_text(yaml_util.dump(data), encoding="utf-8") +def verify_database(value: Any, path: str = "") -> str | None: + if isinstance(value, list): + for i, v in enumerate(value): + result = verify_database(v, f"{path}[{i}]") + if result is not None: + return result + return None + if isinstance(value, dict): + for k, v in value.items(): + key_result = verify_database(k, f"{path}/{k}") + if key_result is not None: + return key_result + value_result = verify_database(v, f"{path}/{k}") + if value_result is not None: + return value_result + return None + if isinstance(value, str): + if not isinstance(value, yaml_util.ESPHomeDataBase): + return f"{path}: {value!r} is not ESPHomeDataBase" + return None + return None + + def test_substitutions_fixtures(fixture_path): base_dir = fixture_path / "substitutions" sources = sorted(glob.glob(str(base_dir / "*.input.yaml"))) @@ -83,6 +107,9 @@ def test_substitutions_fixtures(fixture_path): substitutions.do_substitution_pass(config, None) resolve_extend_remove(config) + verify_database_result = verify_database(config) + if verify_database_result is not None: + raise AssertionError(verify_database_result) # Also load expected using ESPHome's loader, or use {} if missing and DEV_MODE if expected_path.is_file(): From 00f22e5c3644f8c645d5741615271b78423ee453 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 14:51:08 -0500 Subject: [PATCH 262/526] [network] Eliminate runtime string parsing for IP address initialization (#11561) --- esphome/components/ethernet/__init__.py | 12 ++++---- esphome/components/network/__init__.py | 37 +++++++++++++++++++++++++ esphome/components/wifi/__init__.py | 6 ++-- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 77f70a3630..2f02d227d7 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -14,7 +14,7 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S2, VARIANT_ESP32S3, ) -from esphome.components.network import IPAddress +from esphome.components.network import ip_address_literal from esphome.components.spi import CONF_INTERFACE_INDEX, get_spi_interface import esphome.config_validation as cv from esphome.const import ( @@ -320,11 +320,11 @@ def _final_validate_spi(config): def manual_ip(config): return cg.StructInitializer( ManualIP, - ("static_ip", IPAddress(str(config[CONF_STATIC_IP]))), - ("gateway", IPAddress(str(config[CONF_GATEWAY]))), - ("subnet", IPAddress(str(config[CONF_SUBNET]))), - ("dns1", IPAddress(str(config[CONF_DNS1]))), - ("dns2", IPAddress(str(config[CONF_DNS2]))), + ("static_ip", ip_address_literal(config[CONF_STATIC_IP])), + ("gateway", ip_address_literal(config[CONF_GATEWAY])), + ("subnet", ip_address_literal(config[CONF_SUBNET])), + ("dns1", ip_address_literal(config[CONF_DNS1])), + ("dns2", ip_address_literal(config[CONF_DNS2])), ) diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 1a74350c4c..502803da1e 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -1,3 +1,5 @@ +import ipaddress + import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option import esphome.config_validation as cv @@ -10,6 +12,41 @@ AUTO_LOAD = ["mdns"] network_ns = cg.esphome_ns.namespace("network") IPAddress = network_ns.class_("IPAddress") + +def ip_address_literal(ip: str | int | None) -> cg.MockObj: + """Generate an IPAddress with compile-time initialization instead of runtime parsing. + + This function parses the IP address in Python during code generation and generates + a call to the 4-octet constructor (IPAddress(192, 168, 1, 1)) instead of the + string constructor (IPAddress("192.168.1.1")). This eliminates runtime string + parsing overhead and reduces flash usage on embedded systems. + + Args: + ip: IP address as string (e.g., "192.168.1.1"), ipaddress.IPv4Address, or None + + Returns: + IPAddress expression that uses 4-octet constructor for efficiency + """ + if ip is None: + return IPAddress(0, 0, 0, 0) + + try: + # Parse using Python's ipaddress module + ip_obj = ipaddress.ip_address(ip) + except (ValueError, TypeError): + pass + else: + # Only support IPv4 for now + if isinstance(ip_obj, ipaddress.IPv4Address): + # Extract octets from the packed bytes representation + octets = ip_obj.packed + # Generate call to 4-octet constructor: IPAddress(192, 168, 1, 1) + return IPAddress(octets[0], octets[1], octets[2], octets[3]) + + # Fallback to string constructor if parsing fails + return IPAddress(str(ip)) + + CONFIG_SCHEMA = cv.Schema( { cv.SplitDefault( diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index ba488728b7..b980bab4aa 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -3,7 +3,7 @@ from esphome.automation import Condition import esphome.codegen as cg from esphome.components.const import CONF_USE_PSRAM from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant -from esphome.components.network import IPAddress +from esphome.components.network import ip_address_literal from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.config_validation import only_with_esp_idf @@ -334,9 +334,7 @@ def eap_auth(config): def safe_ip(ip): - if ip is None: - return IPAddress(0, 0, 0, 0) - return IPAddress(str(ip)) + return ip_address_literal(ip) def manual_ip(config): From e26b5874d76332aa35125e3be434a9d7ab525d3a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 15:07:31 -0500 Subject: [PATCH 263/526] [api] Register user services with initializer_list (#11545) --- esphome/components/api/__init__.py | 10 +++++++++- esphome/components/api/api_server.h | 6 ++++++ esphome/components/api/custom_api_device.h | 12 ++++++++++++ esphome/core/defines.h | 1 + 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index e91e922204..363f5b73e1 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -258,6 +258,10 @@ async def to_code(config): if config.get(CONF_ACTIONS) or config[CONF_CUSTOM_SERVICES]: cg.add_define("USE_API_SERVICES") + # Set USE_API_CUSTOM_SERVICES if external components need dynamic service registration + if config[CONF_CUSTOM_SERVICES]: + cg.add_define("USE_API_CUSTOM_SERVICES") + if config[CONF_HOMEASSISTANT_SERVICES]: cg.add_define("USE_API_HOMEASSISTANT_SERVICES") @@ -265,6 +269,8 @@ async def to_code(config): cg.add_define("USE_API_HOMEASSISTANT_STATES") if actions := config.get(CONF_ACTIONS, []): + # Collect all triggers first, then register all at once with initializer_list + triggers: list[cg.Pvariable] = [] for conf in actions: template_args = [] func_args = [] @@ -278,8 +284,10 @@ async def to_code(config): trigger = cg.new_Pvariable( conf[CONF_TRIGGER_ID], templ, conf[CONF_ACTION], service_arg_names ) - cg.add(var.register_user_service(trigger)) + triggers.append(trigger) await automation.build_automation(trigger, func_args, conf) + # Register all services at once - single allocation, no reallocations + cg.add(var.initialize_user_services(triggers)) if CONF_ON_CLIENT_CONNECTED in config: cg.add_define("USE_API_CLIENT_CONNECTED_TRIGGER") diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index e0e23301d0..d29181250e 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -125,8 +125,14 @@ class APIServer : public Component, public Controller { #endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES #endif // USE_API_HOMEASSISTANT_SERVICES #ifdef USE_API_SERVICES + void initialize_user_services(std::initializer_list services) { + this->user_services_.assign(services); + } +#ifdef USE_API_CUSTOM_SERVICES + // Only compile push_back method when custom_services: true (external components) void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } #endif +#endif #ifdef USE_HOMEASSISTANT_TIME void request_time(); #endif diff --git a/esphome/components/api/custom_api_device.h b/esphome/components/api/custom_api_device.h index 711eba2444..d34ccfa0ce 100644 --- a/esphome/components/api/custom_api_device.h +++ b/esphome/components/api/custom_api_device.h @@ -53,8 +53,14 @@ class CustomAPIDevice { template void register_service(void (T::*callback)(Ts...), const std::string &name, const std::array &arg_names) { +#ifdef USE_API_CUSTOM_SERVICES auto *service = new CustomAPIDeviceService(name, arg_names, (T *) this, callback); // NOLINT global_api_server->register_user_service(service); +#else + static_assert( + sizeof(T) == 0, + "register_service() requires 'custom_services: true' in the 'api:' section of your YAML configuration"); +#endif } #else template @@ -86,8 +92,14 @@ class CustomAPIDevice { */ #ifdef USE_API_SERVICES template void register_service(void (T::*callback)(), const std::string &name) { +#ifdef USE_API_CUSTOM_SERVICES auto *service = new CustomAPIDeviceService(name, {}, (T *) this, callback); // NOLINT global_api_server->register_user_service(service); +#else + static_assert( + sizeof(T) == 0, + "register_service() requires 'custom_services: true' in the 'api:' section of your YAML configuration"); +#endif } #else template void register_service(void (T::*callback)(), const std::string &name) { diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 8095ffed4a..97e766455a 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -123,6 +123,7 @@ #define USE_API_NOISE #define USE_API_PLAINTEXT #define USE_API_SERVICES +#define USE_API_CUSTOM_SERVICES #define API_MAX_SEND_QUEUE 8 #define USE_MD5 #define USE_SHA256 From 14b057f54e5f552de9ef1450cea509616bb1b230 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 15:14:16 -0500 Subject: [PATCH 264/526] [light] Optimize LambdaLightEffect and AddressableLambdaLightEffect with function pointers (#11556) --- esphome/components/light/addressable_light_effect.h | 6 +++--- esphome/components/light/base_light_effects.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/light/addressable_light_effect.h b/esphome/components/light/addressable_light_effect.h index 9840112040..0847db3770 100644 --- a/esphome/components/light/addressable_light_effect.h +++ b/esphome/components/light/addressable_light_effect.h @@ -57,9 +57,9 @@ class AddressableLightEffect : public LightEffect { class AddressableLambdaLightEffect : public AddressableLightEffect { public: - AddressableLambdaLightEffect(const char *name, std::function f, + AddressableLambdaLightEffect(const char *name, void (*f)(AddressableLight &, Color, bool initial_run), uint32_t update_interval) - : AddressableLightEffect(name), f_(std::move(f)), update_interval_(update_interval) {} + : AddressableLightEffect(name), f_(f), update_interval_(update_interval) {} void start() override { this->initial_run_ = true; } void apply(AddressableLight &it, const Color ¤t_color) override { const uint32_t now = millis(); @@ -72,7 +72,7 @@ class AddressableLambdaLightEffect : public AddressableLightEffect { } protected: - std::function f_; + void (*f_)(AddressableLight &, Color, bool initial_run); uint32_t update_interval_; uint32_t last_run_{0}; bool initial_run_; diff --git a/esphome/components/light/base_light_effects.h b/esphome/components/light/base_light_effects.h index 327c243525..515afc5c59 100644 --- a/esphome/components/light/base_light_effects.h +++ b/esphome/components/light/base_light_effects.h @@ -112,8 +112,8 @@ class RandomLightEffect : public LightEffect { class LambdaLightEffect : public LightEffect { public: - LambdaLightEffect(const char *name, std::function f, uint32_t update_interval) - : LightEffect(name), f_(std::move(f)), update_interval_(update_interval) {} + LambdaLightEffect(const char *name, void (*f)(bool initial_run), uint32_t update_interval) + : LightEffect(name), f_(f), update_interval_(update_interval) {} void start() override { this->initial_run_ = true; } void apply() override { @@ -130,7 +130,7 @@ class LambdaLightEffect : public LightEffect { uint32_t get_current_index() const { return this->get_index(); } protected: - std::function f_; + void (*f_)(bool initial_run); uint32_t update_interval_; uint32_t last_run_{0}; bool initial_run_; From bda4769bd3df0ac81eae0c4dcdcc5dc49e960eb7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 16:05:40 -0500 Subject: [PATCH 265/526] [core] Optimize TemplatableValue to use function pointers for stateless lambdas (#11554) --- esphome/core/automation.h | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/esphome/core/automation.h b/esphome/core/automation.h index 0512752d50..aace7889f0 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -4,6 +4,8 @@ #include "esphome/core/defines.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" +#include +#include #include #include @@ -27,11 +29,20 @@ template class TemplatableValue { public: TemplatableValue() : type_(NONE) {} - template::value, int> = 0> TemplatableValue(F value) : type_(VALUE) { + template TemplatableValue(F value) requires(!std::invocable) : type_(VALUE) { new (&this->value_) T(std::move(value)); } - template::value, int> = 0> TemplatableValue(F f) : type_(LAMBDA) { + // For stateless lambdas (convertible to function pointer): use function pointer + template + TemplatableValue(F f) requires std::invocable && std::convertible_to + : type_(STATELESS_LAMBDA) { + this->stateless_f_ = f; // Implicit conversion to function pointer + } + + // For stateful lambdas (not convertible to function pointer): use std::function + template + TemplatableValue(F f) requires std::invocable &&(!std::convertible_to) : type_(LAMBDA) { this->f_ = new std::function(std::move(f)); } @@ -41,6 +52,8 @@ template class TemplatableValue { new (&this->value_) T(other.value_); } else if (type_ == LAMBDA) { this->f_ = new std::function(*other.f_); + } else if (type_ == STATELESS_LAMBDA) { + this->stateless_f_ = other.stateless_f_; } } @@ -51,6 +64,8 @@ template class TemplatableValue { } else if (type_ == LAMBDA) { this->f_ = other.f_; other.f_ = nullptr; + } else if (type_ == STATELESS_LAMBDA) { + this->stateless_f_ = other.stateless_f_; } other.type_ = NONE; } @@ -78,16 +93,23 @@ template class TemplatableValue { } else if (type_ == LAMBDA) { delete this->f_; } + // STATELESS_LAMBDA/NONE: no cleanup needed (function pointer or empty, not heap-allocated) } bool has_value() { return this->type_ != NONE; } T value(X... x) { - if (this->type_ == LAMBDA) { - return (*this->f_)(x...); + switch (this->type_) { + case STATELESS_LAMBDA: + return this->stateless_f_(x...); // Direct function pointer call + case LAMBDA: + return (*this->f_)(x...); // std::function call + case VALUE: + return this->value_; + case NONE: + default: + return T{}; } - // return value also when none - return this->type_ == VALUE ? this->value_ : T{}; } optional optional_value(X... x) { @@ -109,11 +131,13 @@ template class TemplatableValue { NONE, VALUE, LAMBDA, + STATELESS_LAMBDA, } type_; union { T value_; std::function *f_; + T (*stateless_f_)(X...); }; }; From f44615cc8daac4ee1b9e7830c2adf8cca91f1667 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 17:00:02 -0500 Subject: [PATCH 266/526] [template] Optimize all template platforms to use function pointers for stateless lambdas (#11555) --- esphome/components/template/binary_sensor/__init__.py | 8 +++++++- .../template/binary_sensor/template_binary_sensor.cpp | 4 ++-- .../template/binary_sensor/template_binary_sensor.h | 4 ++-- esphome/components/template/cover/template_cover.cpp | 4 ++-- esphome/components/template/cover/template_cover.h | 8 ++++---- esphome/components/template/datetime/template_date.h | 4 ++-- esphome/components/template/datetime/template_datetime.h | 4 ++-- esphome/components/template/datetime/template_time.h | 4 ++-- esphome/components/template/lock/template_lock.cpp | 2 +- esphome/components/template/lock/template_lock.h | 4 ++-- esphome/components/template/number/template_number.h | 4 ++-- esphome/components/template/select/template_select.h | 4 ++-- esphome/components/template/sensor/template_sensor.cpp | 2 +- esphome/components/template/sensor/template_sensor.h | 4 ++-- esphome/components/template/switch/template_switch.cpp | 2 +- esphome/components/template/switch/template_switch.h | 4 ++-- esphome/components/template/text/template_text.h | 4 ++-- .../template/text_sensor/template_text_sensor.cpp | 2 +- .../template/text_sensor/template_text_sensor.h | 4 ++-- esphome/components/template/valve/template_valve.cpp | 2 +- esphome/components/template/valve/template_valve.h | 4 ++-- 21 files changed, 44 insertions(+), 38 deletions(-) diff --git a/esphome/components/template/binary_sensor/__init__.py b/esphome/components/template/binary_sensor/__init__.py index c93876380d..9d4208dcca 100644 --- a/esphome/components/template/binary_sensor/__init__.py +++ b/esphome/components/template/binary_sensor/__init__.py @@ -38,8 +38,14 @@ async def to_code(config): condition = await automation.build_condition( condition, cg.TemplateArguments(), [] ) + # Generate a stateless lambda that calls condition.check() + # capture="" is safe because condition is a global variable in generated C++ code + # and doesn't need to be captured. This allows implicit conversion to function pointer. template_ = LambdaExpression( - f"return {condition.check()};", [], return_type=cg.optional.template(bool) + f"return {condition.check()};", + [], + return_type=cg.optional.template(bool), + capture="", ) cg.add(var.set_template(template_)) diff --git a/esphome/components/template/binary_sensor/template_binary_sensor.cpp b/esphome/components/template/binary_sensor/template_binary_sensor.cpp index d1fb618695..8543dff4dc 100644 --- a/esphome/components/template/binary_sensor/template_binary_sensor.cpp +++ b/esphome/components/template/binary_sensor/template_binary_sensor.cpp @@ -9,10 +9,10 @@ static const char *const TAG = "template.binary_sensor"; void TemplateBinarySensor::setup() { this->loop(); } void TemplateBinarySensor::loop() { - if (this->f_ == nullptr) + if (!this->f_.has_value()) return; - auto s = this->f_(); + auto s = (*this->f_)(); if (s.has_value()) { this->publish_state(*s); } diff --git a/esphome/components/template/binary_sensor/template_binary_sensor.h b/esphome/components/template/binary_sensor/template_binary_sensor.h index 5e5624d82e..2e0b216eb4 100644 --- a/esphome/components/template/binary_sensor/template_binary_sensor.h +++ b/esphome/components/template/binary_sensor/template_binary_sensor.h @@ -8,7 +8,7 @@ namespace template_ { class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor { public: - void set_template(std::function()> &&f) { this->f_ = f; } + void set_template(optional (*f)()) { this->f_ = f; } void setup() override; void loop() override; @@ -17,7 +17,7 @@ class TemplateBinarySensor : public Component, public binary_sensor::BinarySenso float get_setup_priority() const override { return setup_priority::HARDWARE; } protected: - std::function()> f_{nullptr}; + optional (*)()> f_; }; } // namespace template_ diff --git a/esphome/components/template/cover/template_cover.cpp b/esphome/components/template/cover/template_cover.cpp index 84c687536e..bed3931e78 100644 --- a/esphome/components/template/cover/template_cover.cpp +++ b/esphome/components/template/cover/template_cover.cpp @@ -63,7 +63,7 @@ void TemplateCover::loop() { } void TemplateCover::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } void TemplateCover::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; } -void TemplateCover::set_state_lambda(std::function()> &&f) { this->state_f_ = f; } +void TemplateCover::set_state_lambda(optional (*f)()) { this->state_f_ = f; } float TemplateCover::get_setup_priority() const { return setup_priority::HARDWARE; } Trigger<> *TemplateCover::get_open_trigger() const { return this->open_trigger_; } Trigger<> *TemplateCover::get_close_trigger() const { return this->close_trigger_; } @@ -124,7 +124,7 @@ CoverTraits TemplateCover::get_traits() { } Trigger *TemplateCover::get_position_trigger() const { return this->position_trigger_; } Trigger *TemplateCover::get_tilt_trigger() const { return this->tilt_trigger_; } -void TemplateCover::set_tilt_lambda(std::function()> &&tilt_f) { this->tilt_f_ = tilt_f; } +void TemplateCover::set_tilt_lambda(optional (*tilt_f)()) { this->tilt_f_ = tilt_f; } void TemplateCover::set_has_stop(bool has_stop) { this->has_stop_ = has_stop; } void TemplateCover::set_has_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; } void TemplateCover::set_has_position(bool has_position) { this->has_position_ = has_position; } diff --git a/esphome/components/template/cover/template_cover.h b/esphome/components/template/cover/template_cover.h index 958c94b0a6..ed1ebf4e43 100644 --- a/esphome/components/template/cover/template_cover.h +++ b/esphome/components/template/cover/template_cover.h @@ -17,7 +17,7 @@ class TemplateCover : public cover::Cover, public Component { public: TemplateCover(); - void set_state_lambda(std::function()> &&f); + void set_state_lambda(optional (*f)()); Trigger<> *get_open_trigger() const; Trigger<> *get_close_trigger() const; Trigger<> *get_stop_trigger() const; @@ -26,7 +26,7 @@ class TemplateCover : public cover::Cover, public Component { Trigger *get_tilt_trigger() const; void set_optimistic(bool optimistic); void set_assumed_state(bool assumed_state); - void set_tilt_lambda(std::function()> &&tilt_f); + void set_tilt_lambda(optional (*tilt_f)()); void set_has_stop(bool has_stop); void set_has_position(bool has_position); void set_has_tilt(bool has_tilt); @@ -45,8 +45,8 @@ class TemplateCover : public cover::Cover, public Component { void stop_prev_trigger_(); TemplateCoverRestoreMode restore_mode_{COVER_RESTORE}; - optional()>> state_f_; - optional()>> tilt_f_; + optional (*)()> state_f_; + optional (*)()> tilt_f_; bool assumed_state_{false}; bool optimistic_{false}; Trigger<> *open_trigger_; diff --git a/esphome/components/template/datetime/template_date.h b/esphome/components/template/datetime/template_date.h index 185c7ed49d..2a0967fc94 100644 --- a/esphome/components/template/datetime/template_date.h +++ b/esphome/components/template/datetime/template_date.h @@ -15,7 +15,7 @@ namespace template_ { class TemplateDate : public datetime::DateEntity, public PollingComponent { public: - void set_template(std::function()> &&f) { this->f_ = f; } + void set_template(optional (*f)()) { this->f_ = f; } void setup() override; void update() override; @@ -35,7 +35,7 @@ class TemplateDate : public datetime::DateEntity, public PollingComponent { ESPTime initial_value_{}; bool restore_value_{false}; Trigger *set_trigger_ = new Trigger(); - optional()>> f_; + optional (*)()> f_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/template/datetime/template_datetime.h b/esphome/components/template/datetime/template_datetime.h index ef80ded89a..d917015b67 100644 --- a/esphome/components/template/datetime/template_datetime.h +++ b/esphome/components/template/datetime/template_datetime.h @@ -15,7 +15,7 @@ namespace template_ { class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponent { public: - void set_template(std::function()> &&f) { this->f_ = f; } + void set_template(optional (*f)()) { this->f_ = f; } void setup() override; void update() override; @@ -35,7 +35,7 @@ class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponen ESPTime initial_value_{}; bool restore_value_{false}; Trigger *set_trigger_ = new Trigger(); - optional()>> f_; + optional (*)()> f_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/template/datetime/template_time.h b/esphome/components/template/datetime/template_time.h index 4a7c0098ec..2f05ba0737 100644 --- a/esphome/components/template/datetime/template_time.h +++ b/esphome/components/template/datetime/template_time.h @@ -15,7 +15,7 @@ namespace template_ { class TemplateTime : public datetime::TimeEntity, public PollingComponent { public: - void set_template(std::function()> &&f) { this->f_ = f; } + void set_template(optional (*f)()) { this->f_ = f; } void setup() override; void update() override; @@ -35,7 +35,7 @@ class TemplateTime : public datetime::TimeEntity, public PollingComponent { ESPTime initial_value_{}; bool restore_value_{false}; Trigger *set_trigger_ = new Trigger(); - optional()>> f_; + optional (*)()> f_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/template/lock/template_lock.cpp b/esphome/components/template/lock/template_lock.cpp index 87ba1046eb..c2e227c26d 100644 --- a/esphome/components/template/lock/template_lock.cpp +++ b/esphome/components/template/lock/template_lock.cpp @@ -45,7 +45,7 @@ void TemplateLock::open_latch() { this->open_trigger_->trigger(); } void TemplateLock::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } -void TemplateLock::set_state_lambda(std::function()> &&f) { this->f_ = f; } +void TemplateLock::set_state_lambda(optional (*f)()) { this->f_ = f; } float TemplateLock::get_setup_priority() const { return setup_priority::HARDWARE; } Trigger<> *TemplateLock::get_lock_trigger() const { return this->lock_trigger_; } Trigger<> *TemplateLock::get_unlock_trigger() const { return this->unlock_trigger_; } diff --git a/esphome/components/template/lock/template_lock.h b/esphome/components/template/lock/template_lock.h index 4f798eca81..428744a66f 100644 --- a/esphome/components/template/lock/template_lock.h +++ b/esphome/components/template/lock/template_lock.h @@ -13,7 +13,7 @@ class TemplateLock : public lock::Lock, public Component { void dump_config() override; - void set_state_lambda(std::function()> &&f); + void set_state_lambda(optional (*f)()); Trigger<> *get_lock_trigger() const; Trigger<> *get_unlock_trigger() const; Trigger<> *get_open_trigger() const; @@ -26,7 +26,7 @@ class TemplateLock : public lock::Lock, public Component { void control(const lock::LockCall &call) override; void open_latch() override; - optional()>> f_; + optional (*)()> f_; bool optimistic_{false}; Trigger<> *lock_trigger_; Trigger<> *unlock_trigger_; diff --git a/esphome/components/template/number/template_number.h b/esphome/components/template/number/template_number.h index 9a82e44339..e77b181d25 100644 --- a/esphome/components/template/number/template_number.h +++ b/esphome/components/template/number/template_number.h @@ -10,7 +10,7 @@ namespace template_ { class TemplateNumber : public number::Number, public PollingComponent { public: - void set_template(std::function()> &&f) { this->f_ = f; } + void set_template(optional (*f)()) { this->f_ = f; } void setup() override; void update() override; @@ -28,7 +28,7 @@ class TemplateNumber : public number::Number, public PollingComponent { float initial_value_{NAN}; bool restore_value_{false}; Trigger *set_trigger_ = new Trigger(); - optional()>> f_; + optional (*)()> f_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/template/select/template_select.h b/esphome/components/template/select/template_select.h index 2f00765c3d..c1b348b26a 100644 --- a/esphome/components/template/select/template_select.h +++ b/esphome/components/template/select/template_select.h @@ -10,7 +10,7 @@ namespace template_ { class TemplateSelect : public select::Select, public PollingComponent { public: - void set_template(std::function()> &&f) { this->f_ = f; } + void set_template(optional (*f)()) { this->f_ = f; } void setup() override; void update() override; @@ -28,7 +28,7 @@ class TemplateSelect : public select::Select, public PollingComponent { std::string initial_option_; bool restore_value_ = false; Trigger *set_trigger_ = new Trigger(); - optional()>> f_; + optional (*)()> f_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/template/sensor/template_sensor.cpp b/esphome/components/template/sensor/template_sensor.cpp index f2d0e7363e..65f2417670 100644 --- a/esphome/components/template/sensor/template_sensor.cpp +++ b/esphome/components/template/sensor/template_sensor.cpp @@ -17,7 +17,7 @@ void TemplateSensor::update() { } } float TemplateSensor::get_setup_priority() const { return setup_priority::HARDWARE; } -void TemplateSensor::set_template(std::function()> &&f) { this->f_ = f; } +void TemplateSensor::set_template(optional (*f)()) { this->f_ = f; } void TemplateSensor::dump_config() { LOG_SENSOR("", "Template Sensor", this); LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/template/sensor/template_sensor.h b/esphome/components/template/sensor/template_sensor.h index 2630cb0b14..369313d607 100644 --- a/esphome/components/template/sensor/template_sensor.h +++ b/esphome/components/template/sensor/template_sensor.h @@ -8,7 +8,7 @@ namespace template_ { class TemplateSensor : public sensor::Sensor, public PollingComponent { public: - void set_template(std::function()> &&f); + void set_template(optional (*f)()); void update() override; @@ -17,7 +17,7 @@ class TemplateSensor : public sensor::Sensor, public PollingComponent { float get_setup_priority() const override; protected: - optional()>> f_; + optional (*)()> f_; }; } // namespace template_ diff --git a/esphome/components/template/switch/template_switch.cpp b/esphome/components/template/switch/template_switch.cpp index fa236f6364..5aaf514b2a 100644 --- a/esphome/components/template/switch/template_switch.cpp +++ b/esphome/components/template/switch/template_switch.cpp @@ -35,7 +35,7 @@ void TemplateSwitch::write_state(bool state) { } void TemplateSwitch::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } bool TemplateSwitch::assumed_state() { return this->assumed_state_; } -void TemplateSwitch::set_state_lambda(std::function()> &&f) { this->f_ = f; } +void TemplateSwitch::set_state_lambda(optional (*f)()) { this->f_ = f; } float TemplateSwitch::get_setup_priority() const { return setup_priority::HARDWARE - 2.0f; } Trigger<> *TemplateSwitch::get_turn_on_trigger() const { return this->turn_on_trigger_; } Trigger<> *TemplateSwitch::get_turn_off_trigger() const { return this->turn_off_trigger_; } diff --git a/esphome/components/template/switch/template_switch.h b/esphome/components/template/switch/template_switch.h index bfe9ac25d6..0fba66b9bd 100644 --- a/esphome/components/template/switch/template_switch.h +++ b/esphome/components/template/switch/template_switch.h @@ -14,7 +14,7 @@ class TemplateSwitch : public switch_::Switch, public Component { void setup() override; void dump_config() override; - void set_state_lambda(std::function()> &&f); + void set_state_lambda(optional (*f)()); Trigger<> *get_turn_on_trigger() const; Trigger<> *get_turn_off_trigger() const; void set_optimistic(bool optimistic); @@ -28,7 +28,7 @@ class TemplateSwitch : public switch_::Switch, public Component { void write_state(bool state) override; - optional()>> f_; + optional (*)()> f_; bool optimistic_{false}; bool assumed_state_{false}; Trigger<> *turn_on_trigger_; diff --git a/esphome/components/template/text/template_text.h b/esphome/components/template/text/template_text.h index bcfc54a2ba..6c17d2016a 100644 --- a/esphome/components/template/text/template_text.h +++ b/esphome/components/template/text/template_text.h @@ -61,7 +61,7 @@ template class TextSaver : public TemplateTextSaverBase { class TemplateText : public text::Text, public PollingComponent { public: - void set_template(std::function()> &&f) { this->f_ = f; } + void set_template(optional (*f)()) { this->f_ = f; } void setup() override; void update() override; @@ -78,7 +78,7 @@ class TemplateText : public text::Text, public PollingComponent { bool optimistic_ = false; std::string initial_value_; Trigger *set_trigger_ = new Trigger(); - optional()>> f_{nullptr}; + optional (*)()> f_{nullptr}; TemplateTextSaverBase *pref_ = nullptr; }; diff --git a/esphome/components/template/text_sensor/template_text_sensor.cpp b/esphome/components/template/text_sensor/template_text_sensor.cpp index 885ad47bbf..2b0297d62f 100644 --- a/esphome/components/template/text_sensor/template_text_sensor.cpp +++ b/esphome/components/template/text_sensor/template_text_sensor.cpp @@ -16,7 +16,7 @@ void TemplateTextSensor::update() { } } float TemplateTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; } -void TemplateTextSensor::set_template(std::function()> &&f) { this->f_ = f; } +void TemplateTextSensor::set_template(optional (*f)()) { this->f_ = f; } void TemplateTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Template Sensor", this); } } // namespace template_ diff --git a/esphome/components/template/text_sensor/template_text_sensor.h b/esphome/components/template/text_sensor/template_text_sensor.h index 07a2bd96fc..48e40c2493 100644 --- a/esphome/components/template/text_sensor/template_text_sensor.h +++ b/esphome/components/template/text_sensor/template_text_sensor.h @@ -9,7 +9,7 @@ namespace template_ { class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent { public: - void set_template(std::function()> &&f); + void set_template(optional (*f)()); void update() override; @@ -18,7 +18,7 @@ class TemplateTextSensor : public text_sensor::TextSensor, public PollingCompone void dump_config() override; protected: - optional()>> f_{}; + optional (*)()> f_{}; }; } // namespace template_ diff --git a/esphome/components/template/valve/template_valve.cpp b/esphome/components/template/valve/template_valve.cpp index 5fa14a2de7..b27cc00968 100644 --- a/esphome/components/template/valve/template_valve.cpp +++ b/esphome/components/template/valve/template_valve.cpp @@ -55,7 +55,7 @@ void TemplateValve::loop() { void TemplateValve::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } void TemplateValve::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; } -void TemplateValve::set_state_lambda(std::function()> &&f) { this->state_f_ = f; } +void TemplateValve::set_state_lambda(optional (*f)()) { this->state_f_ = f; } float TemplateValve::get_setup_priority() const { return setup_priority::HARDWARE; } Trigger<> *TemplateValve::get_open_trigger() const { return this->open_trigger_; } diff --git a/esphome/components/template/valve/template_valve.h b/esphome/components/template/valve/template_valve.h index 5e3fb6aff3..92c32f3487 100644 --- a/esphome/components/template/valve/template_valve.h +++ b/esphome/components/template/valve/template_valve.h @@ -17,7 +17,7 @@ class TemplateValve : public valve::Valve, public Component { public: TemplateValve(); - void set_state_lambda(std::function()> &&f); + void set_state_lambda(optional (*f)()); Trigger<> *get_open_trigger() const; Trigger<> *get_close_trigger() const; Trigger<> *get_stop_trigger() const; @@ -42,7 +42,7 @@ class TemplateValve : public valve::Valve, public Component { void stop_prev_trigger_(); TemplateValveRestoreMode restore_mode_{VALVE_NO_RESTORE}; - optional()>> state_f_; + optional (*)()> state_f_; bool assumed_state_{false}; bool optimistic_{false}; Trigger<> *open_trigger_; From dfa69173ead5457f23aa6daf49bf85aa3dd59e59 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 17:03:44 -0500 Subject: [PATCH 267/526] [api] Use FixedVector const references for service array arguments (#11546) --- esphome/codegen.py | 1 + esphome/components/api/__init__.py | 10 +++-- esphome/components/api/user_services.cpp | 57 ++++++++++++++++++++++-- esphome/cpp_types.py | 1 + 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/esphome/codegen.py b/esphome/codegen.py index 6decd77c62..6d55c6023d 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -62,6 +62,7 @@ from esphome.cpp_types import ( # noqa: F401 EntityBase, EntityCategory, ESPTime, + FixedVector, GPIOPin, InternalGPIOPin, JsonObject, diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 363f5b73e1..449572c0e5 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -71,10 +71,12 @@ SERVICE_ARG_NATIVE_TYPES = { "int": cg.int32, "float": float, "string": cg.std_string, - "bool[]": cg.std_vector.template(bool), - "int[]": cg.std_vector.template(cg.int32), - "float[]": cg.std_vector.template(float), - "string[]": cg.std_vector.template(cg.std_string), + "bool[]": cg.FixedVector.template(bool).operator("const").operator("ref"), + "int[]": cg.FixedVector.template(cg.int32).operator("const").operator("ref"), + "float[]": cg.FixedVector.template(float).operator("const").operator("ref"), + "string[]": cg.FixedVector.template(cg.std_string) + .operator("const") + .operator("ref"), } CONF_ENCRYPTION = "encryption" CONF_BATCH_DELAY = "batch_delay" diff --git a/esphome/components/api/user_services.cpp b/esphome/components/api/user_services.cpp index 3cbf2ab5f9..9c2b4aa79a 100644 --- a/esphome/components/api/user_services.cpp +++ b/esphome/components/api/user_services.cpp @@ -11,23 +11,58 @@ template<> int32_t get_execute_arg_value(const ExecuteServiceArgument & } template<> float get_execute_arg_value(const ExecuteServiceArgument &arg) { return arg.float_; } template<> std::string get_execute_arg_value(const ExecuteServiceArgument &arg) { return arg.string_; } + +// Legacy std::vector versions for external components using custom_api_device.h - optimized with reserve template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { - return std::vector(arg.bool_array.begin(), arg.bool_array.end()); + std::vector result; + result.reserve(arg.bool_array.size()); + result.insert(result.end(), arg.bool_array.begin(), arg.bool_array.end()); + return result; } template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { - return std::vector(arg.int_array.begin(), arg.int_array.end()); + std::vector result; + result.reserve(arg.int_array.size()); + result.insert(result.end(), arg.int_array.begin(), arg.int_array.end()); + return result; } template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { - return std::vector(arg.float_array.begin(), arg.float_array.end()); + std::vector result; + result.reserve(arg.float_array.size()); + result.insert(result.end(), arg.float_array.begin(), arg.float_array.end()); + return result; } template<> std::vector get_execute_arg_value>(const ExecuteServiceArgument &arg) { - return std::vector(arg.string_array.begin(), arg.string_array.end()); + std::vector result; + result.reserve(arg.string_array.size()); + result.insert(result.end(), arg.string_array.begin(), arg.string_array.end()); + return result; +} + +// New FixedVector const reference versions for YAML-generated services - zero-copy +template<> +const FixedVector &get_execute_arg_value &>(const ExecuteServiceArgument &arg) { + return arg.bool_array; +} +template<> +const FixedVector &get_execute_arg_value &>(const ExecuteServiceArgument &arg) { + return arg.int_array; +} +template<> +const FixedVector &get_execute_arg_value &>(const ExecuteServiceArgument &arg) { + return arg.float_array; +} +template<> +const FixedVector &get_execute_arg_value &>( + const ExecuteServiceArgument &arg) { + return arg.string_array; } template<> enums::ServiceArgType to_service_arg_type() { return enums::SERVICE_ARG_TYPE_BOOL; } template<> enums::ServiceArgType to_service_arg_type() { return enums::SERVICE_ARG_TYPE_INT; } template<> enums::ServiceArgType to_service_arg_type() { return enums::SERVICE_ARG_TYPE_FLOAT; } template<> enums::ServiceArgType to_service_arg_type() { return enums::SERVICE_ARG_TYPE_STRING; } + +// Legacy std::vector versions for external components using custom_api_device.h template<> enums::ServiceArgType to_service_arg_type>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; } template<> enums::ServiceArgType to_service_arg_type>() { return enums::SERVICE_ARG_TYPE_INT_ARRAY; @@ -39,4 +74,18 @@ template<> enums::ServiceArgType to_service_arg_type>() return enums::SERVICE_ARG_TYPE_STRING_ARRAY; } +// New FixedVector const reference versions for YAML-generated services +template<> enums::ServiceArgType to_service_arg_type &>() { + return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; +} +template<> enums::ServiceArgType to_service_arg_type &>() { + return enums::SERVICE_ARG_TYPE_INT_ARRAY; +} +template<> enums::ServiceArgType to_service_arg_type &>() { + return enums::SERVICE_ARG_TYPE_FLOAT_ARRAY; +} +template<> enums::ServiceArgType to_service_arg_type &>() { + return enums::SERVICE_ARG_TYPE_STRING_ARRAY; +} + } // namespace esphome::api diff --git a/esphome/cpp_types.py b/esphome/cpp_types.py index a0dd62cb4e..0d1813f63b 100644 --- a/esphome/cpp_types.py +++ b/esphome/cpp_types.py @@ -23,6 +23,7 @@ size_t = global_ns.namespace("size_t") const_char_ptr = global_ns.namespace("const char *") NAN = global_ns.namespace("NAN") esphome_ns = global_ns # using namespace esphome; +FixedVector = esphome_ns.class_("FixedVector") App = esphome_ns.App EntityBase = esphome_ns.class_("EntityBase") Component = esphome_ns.class_("Component") From d65ad693381b3f2e61e32093577f3322fd9fa5b8 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 27 Oct 2025 17:09:45 -0500 Subject: [PATCH 268/526] [uart] Fix order of initialization calls (#11510) --- .../uart/uart_component_esp_idf.cpp | 66 ++++++++++--------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index cffa3308eb..73813d2d5b 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -99,10 +99,26 @@ void IDFUARTComponent::setup() { } void IDFUARTComponent::load_settings(bool dump_config) { - uart_config_t uart_config = this->get_config_(); - esp_err_t err = uart_param_config(this->uart_num_, &uart_config); + esp_err_t err; + + if (uart_is_driver_installed(this->uart_num_)) { + err = uart_driver_delete(this->uart_num_); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_driver_delete failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + } + err = uart_driver_install(this->uart_num_, // UART number + this->rx_buffer_size_, // RX ring buffer size + 0, // TX ring buffer size. If zero, driver will not use a TX buffer and TX function will + // block task until all data has been sent out + 20, // event queue size/depth + &this->uart_event_queue_, // event queue + 0 // Flags used to allocate the interrupt + ); if (err != ESP_OK) { - ESP_LOGW(TAG, "uart_param_config failed: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "uart_driver_install failed: %s", esp_err_to_name(err)); this->mark_failed(); return; } @@ -119,10 +135,12 @@ void IDFUARTComponent::load_settings(bool dump_config) { int8_t flow_control = this->flow_control_pin_ != nullptr ? this->flow_control_pin_->get_pin() : -1; uint32_t invert = 0; - if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted()) + if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted()) { invert |= UART_SIGNAL_TXD_INV; - if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted()) + } + if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted()) { invert |= UART_SIGNAL_RXD_INV; + } err = uart_set_line_inverse(this->uart_num_, invert); if (err != ESP_OK) { @@ -138,26 +156,6 @@ void IDFUARTComponent::load_settings(bool dump_config) { return; } - if (uart_is_driver_installed(this->uart_num_)) { - uart_driver_delete(this->uart_num_); - if (err != ESP_OK) { - ESP_LOGW(TAG, "uart_driver_delete failed: %s", esp_err_to_name(err)); - this->mark_failed(); - return; - } - } - err = uart_driver_install(this->uart_num_, /* UART RX ring buffer size. */ this->rx_buffer_size_, - /* UART TX ring buffer size. If set to zero, driver will not use TX buffer, TX function will - block task until all data have been sent out.*/ - 0, - /* UART event queue size/depth. */ 20, &(this->uart_event_queue_), - /* Flags used to allocate the interrupt. */ 0); - if (err != ESP_OK) { - ESP_LOGW(TAG, "uart_driver_install failed: %s", esp_err_to_name(err)); - this->mark_failed(); - return; - } - err = uart_set_rx_full_threshold(this->uart_num_, this->rx_full_threshold_); if (err != ESP_OK) { ESP_LOGW(TAG, "uart_set_rx_full_threshold failed: %s", esp_err_to_name(err)); @@ -173,24 +171,32 @@ void IDFUARTComponent::load_settings(bool dump_config) { } auto mode = this->flow_control_pin_ != nullptr ? UART_MODE_RS485_HALF_DUPLEX : UART_MODE_UART; - err = uart_set_mode(this->uart_num_, mode); + err = uart_set_mode(this->uart_num_, mode); // per docs, must be called only after uart_driver_install() if (err != ESP_OK) { ESP_LOGW(TAG, "uart_set_mode failed: %s", esp_err_to_name(err)); this->mark_failed(); return; } + uart_config_t uart_config = this->get_config_(); + err = uart_param_config(this->uart_num_, &uart_config); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_param_config failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + if (dump_config) { - ESP_LOGCONFIG(TAG, "UART %u was reloaded.", this->uart_num_); + ESP_LOGCONFIG(TAG, "Reloaded UART %u", this->uart_num_); this->dump_config(); } } void IDFUARTComponent::dump_config() { ESP_LOGCONFIG(TAG, "UART Bus %u:", this->uart_num_); - LOG_PIN(" TX Pin: ", tx_pin_); - LOG_PIN(" RX Pin: ", rx_pin_); - LOG_PIN(" Flow Control Pin: ", flow_control_pin_); + LOG_PIN(" TX Pin: ", this->tx_pin_); + LOG_PIN(" RX Pin: ", this->rx_pin_); + LOG_PIN(" Flow Control Pin: ", this->flow_control_pin_); if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u\n" From 3377080272ea4b6a201cda35e1d97e504d16b247 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 17:16:09 -0500 Subject: [PATCH 269/526] [core] Simplify ESPTime::strftime() and save 20 bytes flash (#11539) --- esphome/core/time.cpp | 22 ++++++++-------------- esphome/core/time.h | 12 +++++++----- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/esphome/core/time.cpp b/esphome/core/time.cpp index 1285ec6448..d30dac4394 100644 --- a/esphome/core/time.cpp +++ b/esphome/core/time.cpp @@ -46,24 +46,18 @@ struct tm ESPTime::to_c_tm() { return c_tm; } -std::string ESPTime::strftime(const std::string &format) { - std::string timestr; - timestr.resize(format.size() * 4); +std::string ESPTime::strftime(const char *format) { struct tm c_tm = this->to_c_tm(); - size_t len = ::strftime(×tr[0], timestr.size(), format.c_str(), &c_tm); - while (len == 0) { - if (timestr.size() >= 128) { - // strftime has failed for reasons unrelated to the size of the buffer - // so return a formatting error - return "ERROR"; - } - timestr.resize(timestr.size() * 2); - len = ::strftime(×tr[0], timestr.size(), format.c_str(), &c_tm); + char buf[128]; + size_t len = ::strftime(buf, sizeof(buf), format, &c_tm); + if (len > 0) { + return std::string(buf, len); } - timestr.resize(len); - return timestr; + return "ERROR"; } +std::string ESPTime::strftime(const std::string &format) { return this->strftime(format.c_str()); } + bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) { uint16_t year; uint8_t month; diff --git a/esphome/core/time.h b/esphome/core/time.h index a53fca2346..ffcfced418 100644 --- a/esphome/core/time.h +++ b/esphome/core/time.h @@ -44,17 +44,19 @@ struct ESPTime { size_t strftime(char *buffer, size_t buffer_len, const char *format); /** Convert this ESPTime struct to a string as specified by the format argument. - * @see https://www.gnu.org/software/libc/manual/html_node/Formatting-Calendar-Time.html#index-strftime + * @see https://en.cppreference.com/w/c/chrono/strftime * - * @warning This method uses dynamically allocated strings which can cause heap fragmentation with some + * @warning This method returns a dynamically allocated string which can cause heap fragmentation with some * microcontrollers. * - * @warning This method can return "ERROR" when the underlying strftime() call fails, e.g. when the - * format string contains unsupported specifiers or when the format string doesn't produce any - * output. + * @warning This method can return "ERROR" when the underlying strftime() call fails or when the + * output exceeds 128 bytes. */ std::string strftime(const std::string &format); + /// @copydoc strftime(const std::string &format) + std::string strftime(const char *format); + /// Check if this ESPTime is valid (all fields in range and year is greater than 2018) bool is_valid() const { return this->year >= 2019 && this->fields_in_range(); } From 31b1b50ad9d6811b1077fdedc6ae2df850c30fa0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 17:16:38 -0500 Subject: [PATCH 270/526] [number] Skip set_mode call when using default AUTO mode (#11537) --- esphome/components/number/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index 230c3aa0c1..ac0329fcc6 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -252,7 +252,10 @@ async def setup_number_core_( cg.add(var.traits.set_max_value(max_value)) cg.add(var.traits.set_step(step)) - cg.add(var.traits.set_mode(config[CONF_MODE])) + # Only set if non-default to avoid bloating setup() function + # (mode_ is initialized to NUMBER_MODE_AUTO in the header) + if config[CONF_MODE] != NumberMode.NUMBER_MODE_AUTO: + cg.add(var.traits.set_mode(config[CONF_MODE])) for conf in config.get(CONF_ON_VALUE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) From dfb4b31bf9ee381f5ebe2261b8aeaf1872edebbf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 17:37:40 -0500 Subject: [PATCH 271/526] [template] Store initial option as index in template select (#11523) --- .../components/template/select/__init__.py | 15 +++++++--- .../template/select/template_select.cpp | 28 ++++++++----------- .../template/select/template_select.h | 4 +-- .../host_mode_empty_string_options.yaml | 11 ++++++++ .../test_host_mode_empty_string_options.py | 28 +++++++++++++++++-- 5 files changed, 61 insertions(+), 25 deletions(-) diff --git a/esphome/components/template/select/__init__.py b/esphome/components/template/select/__init__.py index 3282092d63..0e9c240547 100644 --- a/esphome/components/template/select/__init__.py +++ b/esphome/components/template/select/__init__.py @@ -73,11 +73,18 @@ async def to_code(config): cg.add(var.set_template(template_)) else: - cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) - cg.add(var.set_initial_option(config[CONF_INITIAL_OPTION])) + # Only set if non-default to avoid bloating setup() function + if config[CONF_OPTIMISTIC]: + cg.add(var.set_optimistic(True)) + initial_option_index = config[CONF_OPTIONS].index(config[CONF_INITIAL_OPTION]) + # Only set if non-zero to avoid bloating setup() function + # (initial_option_index_ is zero-initialized in the header) + if initial_option_index != 0: + cg.add(var.set_initial_option_index(initial_option_index)) - if CONF_RESTORE_VALUE in config: - cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE])) + # Only set if True (default is False) + if config.get(CONF_RESTORE_VALUE): + cg.add(var.set_restore_value(True)) if CONF_SET_ACTION in config: await automation.build_automation( diff --git a/esphome/components/template/select/template_select.cpp b/esphome/components/template/select/template_select.cpp index 95b0ee0d2b..3765cf02bf 100644 --- a/esphome/components/template/select/template_select.cpp +++ b/esphome/components/template/select/template_select.cpp @@ -10,26 +10,21 @@ void TemplateSelect::setup() { if (this->f_.has_value()) return; - std::string value; - if (!this->restore_value_) { - value = this->initial_option_; - ESP_LOGD(TAG, "State from initial: %s", value.c_str()); - } else { - size_t index; + size_t index = this->initial_option_index_; + if (this->restore_value_) { this->pref_ = global_preferences->make_preference(this->get_preference_hash()); - if (!this->pref_.load(&index)) { - value = this->initial_option_; - ESP_LOGD(TAG, "State from initial (could not load stored index): %s", value.c_str()); - } else if (!this->has_index(index)) { - value = this->initial_option_; - ESP_LOGD(TAG, "State from initial (restored index %d out of bounds): %s", index, value.c_str()); + size_t restored_index; + if (this->pref_.load(&restored_index) && this->has_index(restored_index)) { + index = restored_index; + ESP_LOGD(TAG, "State from restore: %s", this->at(index).value().c_str()); } else { - value = this->at(index).value(); - ESP_LOGD(TAG, "State from restore: %s", value.c_str()); + ESP_LOGD(TAG, "State from initial (could not load or invalid stored index): %s", this->at(index).value().c_str()); } + } else { + ESP_LOGD(TAG, "State from initial: %s", this->at(index).value().c_str()); } - this->publish_state(value); + this->publish_state(this->at(index).value()); } void TemplateSelect::update() { @@ -69,7 +64,8 @@ void TemplateSelect::dump_config() { " Optimistic: %s\n" " Initial Option: %s\n" " Restore Value: %s", - YESNO(this->optimistic_), this->initial_option_.c_str(), YESNO(this->restore_value_)); + YESNO(this->optimistic_), this->at(this->initial_option_index_).value().c_str(), + YESNO(this->restore_value_)); } } // namespace template_ diff --git a/esphome/components/template/select/template_select.h b/esphome/components/template/select/template_select.h index c1b348b26a..e77e4d8f14 100644 --- a/esphome/components/template/select/template_select.h +++ b/esphome/components/template/select/template_select.h @@ -19,13 +19,13 @@ class TemplateSelect : public select::Select, public PollingComponent { Trigger *get_set_trigger() const { return this->set_trigger_; } void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } - void set_initial_option(const std::string &initial_option) { this->initial_option_ = initial_option; } + void set_initial_option_index(size_t initial_option_index) { this->initial_option_index_ = initial_option_index; } void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; } protected: void control(const std::string &value) override; bool optimistic_ = false; - std::string initial_option_; + size_t initial_option_index_{0}; bool restore_value_ = false; Trigger *set_trigger_ = new Trigger(); optional (*)()> f_; diff --git a/tests/integration/fixtures/host_mode_empty_string_options.yaml b/tests/integration/fixtures/host_mode_empty_string_options.yaml index ab8e6cd005..a170511c46 100644 --- a/tests/integration/fixtures/host_mode_empty_string_options.yaml +++ b/tests/integration/fixtures/host_mode_empty_string_options.yaml @@ -41,6 +41,17 @@ select: - "" # Empty string at the end initial_option: "Choice X" + - platform: template + name: "Select Initial Option Test" + id: select_initial_option_test + optimistic: true + options: + - "First" + - "Second" + - "Third" + - "Fourth" + initial_option: "Third" # Test non-default initial option + # Add a sensor to ensure we have other entities in the list sensor: - platform: template diff --git a/tests/integration/test_host_mode_empty_string_options.py b/tests/integration/test_host_mode_empty_string_options.py index 242db2d40f..1180ce75fc 100644 --- a/tests/integration/test_host_mode_empty_string_options.py +++ b/tests/integration/test_host_mode_empty_string_options.py @@ -36,8 +36,8 @@ async def test_host_mode_empty_string_options( # Find our select entities select_entities = [e for e in entity_info if isinstance(e, SelectInfo)] - assert len(select_entities) == 3, ( - f"Expected 3 select entities, got {len(select_entities)}" + assert len(select_entities) == 4, ( + f"Expected 4 select entities, got {len(select_entities)}" ) # Verify each select entity by name and check their options @@ -71,6 +71,15 @@ async def test_host_mode_empty_string_options( assert empty_last.options[2] == "Choice Z" assert empty_last.options[3] == "" # Empty string at end + # Check "Select Initial Option Test" - verify non-default initial option + assert "Select Initial Option Test" in selects_by_name + initial_option_test = selects_by_name["Select Initial Option Test"] + assert len(initial_option_test.options) == 4 + assert initial_option_test.options[0] == "First" + assert initial_option_test.options[1] == "Second" + assert initial_option_test.options[2] == "Third" + assert initial_option_test.options[3] == "Fourth" + # If we got here without protobuf decoding errors, the fix is working # The bug would have caused "Invalid protobuf message" errors with trailing bytes @@ -78,7 +87,12 @@ async def test_host_mode_empty_string_options( # This ensures empty strings work properly in state messages too states: dict[int, EntityState] = {} states_received_future: asyncio.Future[None] = loop.create_future() - expected_select_keys = {empty_first.key, empty_middle.key, empty_last.key} + expected_select_keys = { + empty_first.key, + empty_middle.key, + empty_last.key, + initial_option_test.key, + } received_select_keys = set() def on_state(state: EntityState) -> None: @@ -109,6 +123,14 @@ async def test_host_mode_empty_string_options( assert empty_first.key in states assert empty_middle.key in states assert empty_last.key in states + assert initial_option_test.key in states + + # Verify the initial option is set correctly to "Third" (not the default "First") + initial_state = states[initial_option_test.key] + assert initial_state.state == "Third", ( + f"Expected initial state 'Third' but got '{initial_state.state}' - " + f"initial_option not correctly applied" + ) # The main test is that we got here without protobuf errors # The select entities with empty string options were properly encoded From ce8a6a6c438716add8256daf3d2d3b95d51fbacd Mon Sep 17 00:00:00 2001 From: Daniel Herrmann Date: Tue, 28 Oct 2025 00:24:13 +0100 Subject: [PATCH 272/526] fix: load_cert_chain requires the path, not a file object (#11543) --- esphome/mqtt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/mqtt.py b/esphome/mqtt.py index f1c631697a..093ee64df4 100644 --- a/esphome/mqtt.py +++ b/esphome/mqtt.py @@ -120,7 +120,7 @@ def prepare( cert_file.flush() key_file.write(config[CONF_MQTT].get(CONF_CLIENT_CERTIFICATE_KEY)) key_file.flush() - context.load_cert_chain(cert_file, key_file) + context.load_cert_chain(cert_file.name, key_file.name) client.tls_set_context(context) try: From 1e9309ffffe10ea5310340ce834fa73ea5145403 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 27 Oct 2025 17:20:21 -0700 Subject: [PATCH 273/526] [tuya] allow enum for eco id (#11544) Co-authored-by: Samuel Sieb --- esphome/components/tuya/climate/tuya_climate.cpp | 8 +++++++- esphome/components/tuya/climate/tuya_climate.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/components/tuya/climate/tuya_climate.cpp b/esphome/components/tuya/climate/tuya_climate.cpp index 04fb14acff..d3c78104e3 100644 --- a/esphome/components/tuya/climate/tuya_climate.cpp +++ b/esphome/components/tuya/climate/tuya_climate.cpp @@ -67,7 +67,9 @@ void TuyaClimate::setup() { } if (this->eco_id_.has_value()) { this->parent_->register_listener(*this->eco_id_, [this](const TuyaDatapoint &datapoint) { + // Whether data type is BOOL or ENUM, it will still be a 1 or a 0, so the functions below are valid in both cases this->eco_ = datapoint.value_bool; + this->eco_type_ = datapoint.type; ESP_LOGV(TAG, "MCU reported eco is: %s", ONOFF(this->eco_)); this->compute_preset_(); this->compute_target_temperature_(); @@ -176,7 +178,11 @@ void TuyaClimate::control(const climate::ClimateCall &call) { if (this->eco_id_.has_value()) { const bool eco = preset == climate::CLIMATE_PRESET_ECO; ESP_LOGV(TAG, "Setting eco: %s", ONOFF(eco)); - this->parent_->set_boolean_datapoint_value(*this->eco_id_, eco); + if (this->eco_type_ == TuyaDatapointType::ENUM) { + this->parent_->set_enum_datapoint_value(*this->eco_id_, eco); + } else { + this->parent_->set_boolean_datapoint_value(*this->eco_id_, eco); + } } if (this->sleep_id_.has_value()) { const bool sleep = preset == climate::CLIMATE_PRESET_SLEEP; diff --git a/esphome/components/tuya/climate/tuya_climate.h b/esphome/components/tuya/climate/tuya_climate.h index d6258c21e1..31bef57639 100644 --- a/esphome/components/tuya/climate/tuya_climate.h +++ b/esphome/components/tuya/climate/tuya_climate.h @@ -104,6 +104,7 @@ class TuyaClimate : public climate::Climate, public Component { optional eco_id_{}; optional sleep_id_{}; optional eco_temperature_{}; + TuyaDatapointType eco_type_{}; uint8_t active_state_; uint8_t fan_state_; optional swing_vertical_id_{}; From 5647f36900ed455f90d827bb0154dfebca660494 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:21:17 +0100 Subject: [PATCH 274/526] [nextion] Remove TFT upload baud rate validation to reduce flash usage (#11012) --- esphome/components/nextion/nextion_upload_arduino.cpp | 5 ----- esphome/components/nextion/nextion_upload_idf.cpp | 5 ----- 2 files changed, 10 deletions(-) diff --git a/esphome/components/nextion/nextion_upload_arduino.cpp b/esphome/components/nextion/nextion_upload_arduino.cpp index b0e5d121dd..b4d217d7aa 100644 --- a/esphome/components/nextion/nextion_upload_arduino.cpp +++ b/esphome/components/nextion/nextion_upload_arduino.cpp @@ -174,11 +174,6 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { // Check if baud rate is supported this->original_baud_rate_ = this->parent_->get_baud_rate(); - static const std::vector SUPPORTED_BAUD_RATES = {2400, 4800, 9600, 19200, 31250, 38400, 57600, - 115200, 230400, 250000, 256000, 512000, 921600}; - if (std::find(SUPPORTED_BAUD_RATES.begin(), SUPPORTED_BAUD_RATES.end(), baud_rate) == SUPPORTED_BAUD_RATES.end()) { - baud_rate = this->original_baud_rate_; - } ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate); // Define the configuration for the HTTP client diff --git a/esphome/components/nextion/nextion_upload_idf.cpp b/esphome/components/nextion/nextion_upload_idf.cpp index 78a47f9e2c..3b0d65643d 100644 --- a/esphome/components/nextion/nextion_upload_idf.cpp +++ b/esphome/components/nextion/nextion_upload_idf.cpp @@ -177,11 +177,6 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { // Check if baud rate is supported this->original_baud_rate_ = this->parent_->get_baud_rate(); - static const std::vector SUPPORTED_BAUD_RATES = {2400, 4800, 9600, 19200, 31250, 38400, 57600, - 115200, 230400, 250000, 256000, 512000, 921600}; - if (std::find(SUPPORTED_BAUD_RATES.begin(), SUPPORTED_BAUD_RATES.end(), baud_rate) == SUPPORTED_BAUD_RATES.end()) { - baud_rate = this->original_baud_rate_; - } ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate); // Define the configuration for the HTTP client From 285e006637a3bdf6011b3d68a98bf0c19b290f6a Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:22:28 +0100 Subject: [PATCH 275/526] [nextion] Add `set_component_visibility()` method for dynamic visibility control (#11530) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- esphome/components/nextion/nextion.h | 17 +++++++++++++++++ esphome/components/nextion/nextion_base.h | 1 + esphome/components/nextion/nextion_commands.cpp | 10 +++++----- .../components/nextion/nextion_component.cpp | 8 +++----- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index e2c4faa1d0..c078ab9d56 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -540,6 +540,23 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ void goto_page(uint8_t page); + /** + * Set the visibility of a component. + * + * @param component The component name. + * @param show True to show the component, false to hide it. + * + * @see show_component() + * @see hide_component() + * + * Example: + * ```cpp + * it.set_component_visibility("textview", true); // Equivalent to show_component("textview") + * it.set_component_visibility("textview", false); // Equivalent to hide_component("textview") + * ``` + */ + void set_component_visibility(const char *component, bool show) override; + /** * Hide a component. * @param component The component name. diff --git a/esphome/components/nextion/nextion_base.h b/esphome/components/nextion/nextion_base.h index b88dd399f8..d46cd9a185 100644 --- a/esphome/components/nextion/nextion_base.h +++ b/esphome/components/nextion/nextion_base.h @@ -45,6 +45,7 @@ class NextionBase { virtual void set_component_pressed_font_color(const char *component, Color color) = 0; virtual void set_component_font(const char *component, uint8_t font_id) = 0; + virtual void set_component_visibility(const char *component, bool show) = 0; virtual void show_component(const char *component) = 0; virtual void hide_component(const char *component) = 0; diff --git a/esphome/components/nextion/nextion_commands.cpp b/esphome/components/nextion/nextion_commands.cpp index f3a282717b..cfaae7e3e0 100644 --- a/esphome/components/nextion/nextion_commands.cpp +++ b/esphome/components/nextion/nextion_commands.cpp @@ -201,13 +201,13 @@ void Nextion::set_component_font(const char *component, uint8_t font_id) { this->add_no_result_to_queue_with_printf_("set_component_font", "%s.font=%" PRIu8, component, font_id); } -void Nextion::hide_component(const char *component) { - this->add_no_result_to_queue_with_printf_("hide_component", "vis %s,0", component); +void Nextion::set_component_visibility(const char *component, bool show) { + this->add_no_result_to_queue_with_printf_("set_component_visibility", "vis %s,%d", component, show ? 1 : 0); } -void Nextion::show_component(const char *component) { - this->add_no_result_to_queue_with_printf_("show_component", "vis %s,1", component); -} +void Nextion::hide_component(const char *component) { this->set_component_visibility(component, false); } + +void Nextion::show_component(const char *component) { this->set_component_visibility(component, true); } void Nextion::enable_component_touch(const char *component) { this->add_no_result_to_queue_with_printf_("enable_component_touch", "tsw %s,1", component); diff --git a/esphome/components/nextion/nextion_component.cpp b/esphome/components/nextion/nextion_component.cpp index 32929d6845..324ad87372 100644 --- a/esphome/components/nextion/nextion_component.cpp +++ b/esphome/components/nextion/nextion_component.cpp @@ -81,13 +81,11 @@ void NextionComponent::update_component_settings(bool force_update) { this->component_flags_.visible_needs_update = false; - if (this->component_flags_.visible) { - this->nextion_->show_component(name_to_send.c_str()); - this->send_state_to_nextion(); - } else { - this->nextion_->hide_component(name_to_send.c_str()); + this->nextion_->set_component_visibility(name_to_send.c_str(), this->component_flags_.visible); + if (!this->component_flags_.visible) { return; } + this->send_state_to_nextion(); } if (this->component_flags_.bco_needs_update || (force_update && this->component_flags_.bco2_is_set)) { From 85205a28d283583d72c2681f89f37c0b2d28f8fa Mon Sep 17 00:00:00 2001 From: aanban Date: Tue, 28 Oct 2025 03:49:16 +0100 Subject: [PATCH 276/526] [remote_base] add support for Dyson cool AM07 tower fan (#10163) Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/remote_base/__init__.py | 44 ++++++++++++ .../components/remote_base/dyson_protocol.cpp | 71 +++++++++++++++++++ .../components/remote_base/dyson_protocol.h | 46 ++++++++++++ .../remote_receiver/common-actions.yaml | 5 ++ .../remote_transmitter/common-buttons.yaml | 7 ++ 5 files changed, 173 insertions(+) create mode 100644 esphome/components/remote_base/dyson_protocol.cpp create mode 100644 esphome/components/remote_base/dyson_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index ccf16a8beb..8d735ea563 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -17,6 +17,7 @@ from esphome.const import ( CONF_FAMILY, CONF_GROUP, CONF_ID, + CONF_INDEX, CONF_INVERTED, CONF_LEVEL, CONF_MAGNITUDE, @@ -616,6 +617,49 @@ async def dooya_action(var, config, args): cg.add(var.set_check(template_)) +# Dyson +DysonData, DysonBinarySensor, DysonTrigger, DysonAction, DysonDumper = declare_protocol( + "Dyson" +) +DYSON_SCHEMA = cv.Schema( + { + cv.Required(CONF_CODE): cv.hex_uint16_t, + cv.Optional(CONF_INDEX, default=0xFF): cv.hex_uint8_t, + } +) + + +@register_binary_sensor("dyson", DysonBinarySensor, DYSON_SCHEMA) +def dyson_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + DysonData, + ("code", config[CONF_CODE]), + ("index", config[CONF_INDEX]), + ) + ) + ) + + +@register_trigger("dyson", DysonTrigger, DysonData) +def dyson_trigger(var, config): + pass + + +@register_dumper("dyson", DysonDumper) +def dyson_dumper(var, config): + pass + + +@register_action("dyson", DysonAction, DYSON_SCHEMA) +async def dyson_action(var, config, args): + template_ = await cg.templatable(config[CONF_CODE], args, cg.uint16) + cg.add(var.set_code(template_)) + template_ = await cg.templatable(config[CONF_INDEX], args, cg.uint8) + cg.add(var.set_index(template_)) + + # JVC JVCData, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol("JVC") JVC_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t}) diff --git a/esphome/components/remote_base/dyson_protocol.cpp b/esphome/components/remote_base/dyson_protocol.cpp new file mode 100644 index 0000000000..db4e1135f4 --- /dev/null +++ b/esphome/components/remote_base/dyson_protocol.cpp @@ -0,0 +1,71 @@ +#include "dyson_protocol.h" +#include "esphome/core/log.h" + +#include + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.dyson"; + +// pulsewidth [µs] +constexpr uint32_t PW_MARK_US = 780; +constexpr uint32_t PW_SHORT_US = 720; +constexpr uint32_t PW_LONG_US = 1500; +constexpr uint32_t PW_START_US = 2280; + +// MSB of 15 bit dyson code +constexpr uint16_t MSB_DYSON = (1 << 14); + +// required symbols in transmit buffer = (start_symbol + 15 data_symbols) +constexpr uint32_t N_SYMBOLS_REQ = 2u * (1 + 15); + +void DysonProtocol::encode(RemoteTransmitData *dst, const DysonData &data) { + uint32_t raw_code = (data.code << 2) + (data.index & 3); + dst->set_carrier_frequency(36000); + dst->reserve(N_SYMBOLS_REQ + 1); + dst->item(PW_START_US, PW_SHORT_US); + for (uint16_t mask = MSB_DYSON; mask != 0; mask >>= 1) { + if (mask == (mask & raw_code)) { + dst->item(PW_MARK_US, PW_LONG_US); + } else { + dst->item(PW_MARK_US, PW_SHORT_US); + } + } + dst->mark(PW_MARK_US); // final carrier pulse +} + +optional DysonProtocol::decode(RemoteReceiveData src) { + uint32_t n_received = static_cast(src.size()); + uint16_t raw_code = 0; + DysonData data{ + .code = 0, + .index = 0, + }; + if (n_received < N_SYMBOLS_REQ) + return {}; // invalid frame length + if (!src.expect_item(PW_START_US, PW_SHORT_US)) + return {}; // start not found + for (uint16_t mask = MSB_DYSON; mask != 0; mask >>= 1) { + if (src.expect_item(PW_MARK_US, PW_SHORT_US)) { + raw_code &= ~mask; // zero detected + } else if (src.expect_item(PW_MARK_US, PW_LONG_US)) { + raw_code |= mask; // one detected + } else { + return {}; // invalid data item + } + } + data.code = raw_code >> 2; // extract button code + data.index = raw_code & 3; // extract rolling index + if (src.expect_mark(PW_MARK_US)) { // check total length + return data; + } + return {}; // frame not complete +} + +void DysonProtocol::dump(const DysonData &data) { + ESP_LOGI(TAG, "Dyson: code=0x%x rolling index=%d", data.code, data.index); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/dyson_protocol.h b/esphome/components/remote_base/dyson_protocol.h new file mode 100644 index 0000000000..d1c08fefba --- /dev/null +++ b/esphome/components/remote_base/dyson_protocol.h @@ -0,0 +1,46 @@ +#pragma once + +#include "remote_base.h" + +#include + +namespace esphome { +namespace remote_base { + +static constexpr uint8_t IGNORE_INDEX = 0xFF; + +struct DysonData { + uint16_t code; // the button, e.g. power, swing, fan++, ... + uint8_t index; // the rolling index counter + bool operator==(const DysonData &rhs) const { + if (IGNORE_INDEX == index || IGNORE_INDEX == rhs.index) { + return code == rhs.code; + } + return code == rhs.code && index == rhs.index; + } +}; + +class DysonProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const DysonData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const DysonData &data) override; +}; + +DECLARE_REMOTE_PROTOCOL(Dyson) + +template class DysonAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint16_t, code) + TEMPLATABLE_VALUE(uint8_t, index) + + void encode(RemoteTransmitData *dst, Ts... x) override { + DysonData data{}; + data.code = this->code_.value(x...); + data.index = this->index_.value(x...); + DysonProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome diff --git a/tests/components/remote_receiver/common-actions.yaml b/tests/components/remote_receiver/common-actions.yaml index c2dc2f0c29..de01fa3602 100644 --- a/tests/components/remote_receiver/common-actions.yaml +++ b/tests/components/remote_receiver/common-actions.yaml @@ -48,6 +48,11 @@ on_drayton: - logger.log: format: "on_drayton: %u %u %u" args: ["x.address", "x.channel", "x.command"] +on_dyson: + then: + - logger.log: + format: "on_dyson: %u %u" + args: ["x.code", "x.index"] on_gobox: then: - logger.log: diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml index 58127d1ab4..e9593cc97c 100644 --- a/tests/components/remote_transmitter/common-buttons.yaml +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -6,6 +6,13 @@ button: remote_transmitter.transmit_beo4: source: 0x01 command: 0x0C + - platform: template + name: Dyson fan up + id: dyson_fan_up + on_press: + remote_transmitter.transmit_dyson: + code: 0x1215 + index: 0x0 - platform: template name: JVC Off id: living_room_lights_on From aba72809d3b280f62aa12a4129e2fbce48b304f7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 22:43:10 -0500 Subject: [PATCH 277/526] Additional tests for ble_client lambdas (#11565) --- tests/components/ble_client/common.yaml | 49 +++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/components/ble_client/common.yaml b/tests/components/ble_client/common.yaml index b5272d01f0..aa4b639463 100644 --- a/tests/components/ble_client/common.yaml +++ b/tests/components/ble_client/common.yaml @@ -3,3 +3,52 @@ esp32_ble_tracker: ble_client: - mac_address: 01:02:03:04:05:06 id: test_blec + on_connect: + - ble_client.ble_write: + id: test_blec + service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678" + characteristic_uuid: "abcd1235-abcd-1234-abcd-abcd12345678" + value: !lambda |- + return std::vector{0x01, 0x02, 0x03}; + - ble_client.ble_write: + id: test_blec + service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678" + characteristic_uuid: "abcd1235-abcd-1234-abcd-abcd12345678" + value: [0x04, 0x05, 0x06] + on_passkey_request: + - ble_client.passkey_reply: + id: test_blec + passkey: !lambda |- + return 123456; + - ble_client.passkey_reply: + id: test_blec + passkey: 654321 + on_numeric_comparison_request: + - ble_client.numeric_comparison_reply: + id: test_blec + accept: !lambda |- + return true; + - ble_client.numeric_comparison_reply: + id: test_blec + accept: false + +sensor: + - platform: ble_client + ble_client_id: test_blec + type: characteristic + id: test_sensor_lambda + name: "BLE Sensor with Lambda" + service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678" + characteristic_uuid: "abcd1236-abcd-1234-abcd-abcd12345678" + lambda: |- + if (x.size() >= 2) { + return (float)(x[0] | (x[1] << 8)) / 100.0; + } + return NAN; + - platform: ble_client + ble_client_id: test_blec + type: characteristic + id: test_sensor_no_lambda + name: "BLE Sensor without Lambda" + service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678" + characteristic_uuid: "abcd1237-abcd-1234-abcd-abcd12345678" From f3b69383fdf25f8a473343790108575c182f8d36 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Oct 2025 22:43:16 -0500 Subject: [PATCH 278/526] Add additional modbus compile tests (#11567) --- .../components/modbus_controller/common.yaml | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/tests/components/modbus_controller/common.yaml b/tests/components/modbus_controller/common.yaml index ae5520e57d..ffaa1491c5 100644 --- a/tests/components/modbus_controller/common.yaml +++ b/tests/components/modbus_controller/common.yaml @@ -56,6 +56,14 @@ binary_sensor: register_type: read address: 0x3200 bitmask: 0x80 + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_binary_sensor2 + name: Test Binary Sensor with Lambda + register_type: read + address: 0x3201 + lambda: |- + return x; number: - platform: modbus_controller @@ -65,6 +73,16 @@ number: address: 0x9001 value_type: U_WORD multiply: 1.0 + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_number2 + name: Test Number with Lambda + address: 0x9002 + value_type: U_WORD + lambda: |- + return x * 2.0; + write_lambda: |- + return x / 2.0; output: - platform: modbus_controller @@ -74,6 +92,14 @@ output: register_type: holding value_type: U_WORD multiply: 1000 + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_output2 + address: 2049 + register_type: holding + value_type: U_WORD + write_lambda: |- + return x * 100.0; select: - platform: modbus_controller @@ -87,6 +113,34 @@ select: "One": 1 "Two": 2 "Three": 3 + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_select2 + name: Test Select with Lambda + address: 1001 + value_type: U_WORD + optionsmap: + "Off": 0 + "On": 1 + "Two": 2 + lambda: |- + ESP_LOGD("Reg1001", "Received value %lld", x); + if (x > 1) { + return std::string("Two"); + } else if (x == 1) { + return std::string("On"); + } + return std::string("Off"); + write_lambda: |- + ESP_LOGD("Reg1001", "Set option to %s (%lld)", x.c_str(), value); + if (x == "On") { + return 1; + } + if (x == "Two") { + payload.push_back(0x0002); + return 0; + } + return value; sensor: - platform: modbus_controller @@ -97,6 +151,15 @@ sensor: address: 0x9001 unit_of_measurement: "AH" value_type: U_WORD + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_sensor2 + name: Test Sensor with Lambda + register_type: holding + address: 0x9002 + value_type: U_WORD + lambda: |- + return x / 10.0; switch: - platform: modbus_controller @@ -106,6 +169,16 @@ switch: register_type: coil address: 0x15 bitmask: 1 + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_switch2 + name: Test Switch with Lambda + register_type: coil + address: 0x16 + lambda: |- + return !x; + write_lambda: |- + return !x; text_sensor: - platform: modbus_controller @@ -117,3 +190,13 @@ text_sensor: register_count: 3 raw_encode: HEXBYTES response_size: 6 + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_text_sensor2 + name: Test Text Sensor with Lambda + register_type: holding + address: 0x9014 + register_count: 2 + response_size: 4 + lambda: |- + return "Modified: " + x; From f5e32d03d01d2a32008f8fdb331e15febf5dd8ea Mon Sep 17 00:00:00 2001 From: rwrozelle Date: Tue, 28 Oct 2025 12:41:48 -0400 Subject: [PATCH 279/526] [http_request] update timeout to be uint32_t (#11577) --- esphome/components/http_request/http_request.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 40c85d51ed..5010cf47a0 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -124,7 +124,7 @@ class HttpRequestComponent : public Component { float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } void set_useragent(const char *useragent) { this->useragent_ = useragent; } - void set_timeout(uint16_t timeout) { this->timeout_ = timeout; } + void set_timeout(uint32_t timeout) { this->timeout_ = timeout; } void set_watchdog_timeout(uint32_t watchdog_timeout) { this->watchdog_timeout_ = watchdog_timeout; } uint32_t get_watchdog_timeout() const { return this->watchdog_timeout_; } void set_follow_redirects(bool follow_redirects) { this->follow_redirects_ = follow_redirects; } @@ -173,7 +173,7 @@ class HttpRequestComponent : public Component { const char *useragent_{nullptr}; bool follow_redirects_{}; uint16_t redirect_limit_{}; - uint16_t timeout_{4500}; + uint32_t timeout_{4500}; uint32_t watchdog_timeout_{0}; }; From da19673f51682a0898e5fe167e52eb9d3f9f6ed4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 28 Oct 2025 14:03:09 -0500 Subject: [PATCH 280/526] Add additional uart test coverage (#11571) --- tests/components/uart/test.esp32-idf.yaml | 38 +++++++++++++++++++++ tests/components/uart/test.esp8266-ard.yaml | 18 ++++++++++ 2 files changed, 56 insertions(+) diff --git a/tests/components/uart/test.esp32-idf.yaml b/tests/components/uart/test.esp32-idf.yaml index 5634c5c6f6..9744a48409 100644 --- a/tests/components/uart/test.esp32-idf.yaml +++ b/tests/components/uart/test.esp32-idf.yaml @@ -19,3 +19,41 @@ uart: packet_transport: - platform: uart + +switch: + # Test uart switch with single state (array) + - platform: uart + name: "UART Switch Single Array" + uart_id: uart_uart + data: [0x01, 0x02, 0x03] + # Test uart switch with single state (string) + - platform: uart + name: "UART Switch Single String" + uart_id: uart_uart + data: "ON" + # Test uart switch with turn_on/turn_off (arrays) + - platform: uart + name: "UART Switch Dual Array" + uart_id: uart_uart + data: + turn_on: [0xA0, 0xA1, 0xA2] + turn_off: [0xB0, 0xB1, 0xB2] + # Test uart switch with turn_on/turn_off (strings) + - platform: uart + name: "UART Switch Dual String" + uart_id: uart_uart + data: + turn_on: "TURN_ON" + turn_off: "TURN_OFF" + +button: + # Test uart button with array data + - platform: uart + name: "UART Button Array" + uart_id: uart_uart + data: [0xFF, 0xEE, 0xDD] + # Test uart button with string data + - platform: uart + name: "UART Button String" + uart_id: uart_uart + data: "BUTTON_PRESS" diff --git a/tests/components/uart/test.esp8266-ard.yaml b/tests/components/uart/test.esp8266-ard.yaml index 09178f1663..566038ee3e 100644 --- a/tests/components/uart/test.esp8266-ard.yaml +++ b/tests/components/uart/test.esp8266-ard.yaml @@ -13,3 +13,21 @@ uart: rx_buffer_size: 512 parity: EVEN stop_bits: 2 + +switch: + - platform: uart + name: "UART Switch Array" + uart_id: uart_uart + data: [0x01, 0x02, 0x03] + - platform: uart + name: "UART Switch Dual" + uart_id: uart_uart + data: + turn_on: [0xA0, 0xA1] + turn_off: [0xB0, 0xB1] + +button: + - platform: uart + name: "UART Button" + uart_id: uart_uart + data: [0xFF, 0xEE] From 7dd829cfcaf07bd98512c97f6523bc3c22063b92 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 28 Oct 2025 14:05:12 -0500 Subject: [PATCH 281/526] [esp32_ble_server][esp32_improv] Eliminate unnecessary heap allocations (#11569) --- esphome/components/esp32_ble_server/__init__.py | 4 +++- .../esp32_ble_server/ble_characteristic.cpp | 11 ++++++++--- .../components/esp32_ble_server/ble_characteristic.h | 3 ++- .../components/esp32_ble_server/ble_descriptor.cpp | 8 +++++--- esphome/components/esp32_ble_server/ble_descriptor.h | 5 ++++- .../esp32_improv/esp32_improv_component.cpp | 10 ++++------ .../components/esp32_improv/esp32_improv_component.h | 2 +- 7 files changed, 27 insertions(+), 16 deletions(-) diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index 55310f3275..a7e2522fac 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -461,7 +461,9 @@ async def parse_value(value_config, args): if isinstance(value, str): value = list(value.encode(value_config[CONF_STRING_ENCODING])) if isinstance(value, list): - return cg.std_vector.template(cg.uint8)(value) + # Generate initializer list {1, 2, 3} instead of std::vector({1, 2, 3}) + # This calls the set_value(std::initializer_list) overload + return cg.ArrayInitializer(*value) val = cg.RawExpression(f"{value_config[CONF_TYPE]}({cg.safe_exp(value)})") return ByteBuffer_ns.wrap(val, value_config[CONF_ENDIANNESS]) diff --git a/esphome/components/esp32_ble_server/ble_characteristic.cpp b/esphome/components/esp32_ble_server/ble_characteristic.cpp index 87f562a250..7627a58338 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_server/ble_characteristic.cpp @@ -35,13 +35,18 @@ BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties) void BLECharacteristic::set_value(ByteBuffer buffer) { this->set_value(buffer.get_data()); } -void BLECharacteristic::set_value(const std::vector &buffer) { +void BLECharacteristic::set_value(std::vector &&buffer) { xSemaphoreTake(this->set_value_lock_, 0L); - this->value_ = buffer; + this->value_ = std::move(buffer); xSemaphoreGive(this->set_value_lock_); } + +void BLECharacteristic::set_value(std::initializer_list data) { + this->set_value(std::vector(data)); // Delegate to move overload +} + void BLECharacteristic::set_value(const std::string &buffer) { - this->set_value(std::vector(buffer.begin(), buffer.end())); + this->set_value(std::vector(buffer.begin(), buffer.end())); // Delegate to move overload } void BLECharacteristic::notify() { diff --git a/esphome/components/esp32_ble_server/ble_characteristic.h b/esphome/components/esp32_ble_server/ble_characteristic.h index 7cceec0ef1..b913915789 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.h +++ b/esphome/components/esp32_ble_server/ble_characteristic.h @@ -33,7 +33,8 @@ class BLECharacteristic { ~BLECharacteristic(); void set_value(ByteBuffer buffer); - void set_value(const std::vector &buffer); + void set_value(std::vector &&buffer); + void set_value(std::initializer_list data); void set_value(const std::string &buffer); void set_broadcast_property(bool value); diff --git a/esphome/components/esp32_ble_server/ble_descriptor.cpp b/esphome/components/esp32_ble_server/ble_descriptor.cpp index 16941cca0f..2d053c09bd 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.cpp +++ b/esphome/components/esp32_ble_server/ble_descriptor.cpp @@ -46,15 +46,17 @@ void BLEDescriptor::do_create(BLECharacteristic *characteristic) { this->state_ = CREATING; } -void BLEDescriptor::set_value(std::vector buffer) { - size_t length = buffer.size(); +void BLEDescriptor::set_value(std::vector &&buffer) { this->set_value_impl_(buffer.data(), buffer.size()); } +void BLEDescriptor::set_value(std::initializer_list data) { this->set_value_impl_(data.begin(), data.size()); } + +void BLEDescriptor::set_value_impl_(const uint8_t *data, size_t length) { if (length > this->value_.attr_max_len) { ESP_LOGE(TAG, "Size %d too large, must be no bigger than %d", length, this->value_.attr_max_len); return; } this->value_.attr_len = length; - memcpy(this->value_.attr_value, buffer.data(), length); + memcpy(this->value_.attr_value, data, length); } void BLEDescriptor::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, diff --git a/esphome/components/esp32_ble_server/ble_descriptor.h b/esphome/components/esp32_ble_server/ble_descriptor.h index 425462a316..5f4f146d6f 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.h +++ b/esphome/components/esp32_ble_server/ble_descriptor.h @@ -27,7 +27,8 @@ class BLEDescriptor { void do_create(BLECharacteristic *characteristic); ESPBTUUID get_uuid() const { return this->uuid_; } - void set_value(std::vector buffer); + void set_value(std::vector &&buffer); + void set_value(std::initializer_list data); void set_value(ByteBuffer buffer) { this->set_value(buffer.get_data()); } void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); @@ -42,6 +43,8 @@ class BLEDescriptor { } protected: + void set_value_impl_(const uint8_t *data, size_t length); + BLECharacteristic *characteristic_{nullptr}; ESPBTUUID uuid_; uint16_t handle_{0xFFFF}; diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 56436b9d3d..2fa9d8f523 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -270,8 +270,8 @@ void ESP32ImprovComponent::set_error_(improv::Error error) { } } -void ESP32ImprovComponent::send_response_(std::vector &response) { - this->rpc_response_->set_value(ByteBuffer::wrap(response)); +void ESP32ImprovComponent::send_response_(std::vector &&response) { + this->rpc_response_->set_value(std::move(response)); if (this->state_ != improv::STATE_STOPPED) this->rpc_response_->notify(); } @@ -409,10 +409,8 @@ void ESP32ImprovComponent::check_wifi_connection_() { } } #endif - // Pass to build_rpc_response using vector constructor from iterators to avoid extra copies - std::vector data = improv::build_rpc_response( - improv::WIFI_SETTINGS, std::vector(url_strings, url_strings + url_count)); - this->send_response_(data); + this->send_response_(improv::build_rpc_response(improv::WIFI_SETTINGS, + std::vector(url_strings, url_strings + url_count))); } else if (this->is_active() && this->state_ != improv::STATE_PROVISIONED) { ESP_LOGD(TAG, "WiFi provisioned externally"); } diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index fd3b2b861d..989552ea56 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -109,7 +109,7 @@ class ESP32ImprovComponent : public Component, public improv_base::ImprovBase { void set_state_(improv::State state, bool update_advertising = true); void set_error_(improv::Error error); improv::State get_initial_state_() const; - void send_response_(std::vector &response); + void send_response_(std::vector &&response); void process_incoming_data_(); void on_wifi_connect_timeout_(); void check_wifi_connection_(); From c3f40de844516c7eeca2eab48c866cd3092f8967 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 28 Oct 2025 14:06:13 -0500 Subject: [PATCH 282/526] [modbus_controller] Optimize lambdas to use function pointers instead of std::function (#11566) --- .../binary_sensor/modbus_binarysensor.h | 4 ++-- .../modbus_controller/number/modbus_number.h | 8 ++++---- .../modbus_controller/output/modbus_output.h | 8 ++++---- .../modbus_controller/select/modbus_select.h | 11 +++++------ .../modbus_controller/sensor/modbus_sensor.h | 4 ++-- .../modbus_controller/switch/modbus_switch.h | 8 ++++---- .../modbus_controller/text_sensor/modbus_textsensor.h | 5 ++--- 7 files changed, 23 insertions(+), 25 deletions(-) diff --git a/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h index 3a017c6f88..119f4fdd5a 100644 --- a/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h +++ b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h @@ -33,8 +33,8 @@ class ModbusBinarySensor : public Component, public binary_sensor::BinarySensor, void dump_config() override; - using transform_func_t = std::function(ModbusBinarySensor *, bool, const std::vector &)>; - void set_template(transform_func_t &&f) { this->transform_func_ = f; } + using transform_func_t = optional (*)(ModbusBinarySensor *, bool, const std::vector &); + void set_template(transform_func_t f) { this->transform_func_ = f; } protected: optional transform_func_{nullopt}; diff --git a/esphome/components/modbus_controller/number/modbus_number.h b/esphome/components/modbus_controller/number/modbus_number.h index 8f77b2e014..169f85ff36 100644 --- a/esphome/components/modbus_controller/number/modbus_number.h +++ b/esphome/components/modbus_controller/number/modbus_number.h @@ -31,10 +31,10 @@ class ModbusNumber : public number::Number, public Component, public SensorItem void set_parent(ModbusController *parent) { this->parent_ = parent; } void set_write_multiply(float factor) { this->multiply_by_ = factor; } - using transform_func_t = std::function(ModbusNumber *, float, const std::vector &)>; - using write_transform_func_t = std::function(ModbusNumber *, float, std::vector &)>; - void set_template(transform_func_t &&f) { this->transform_func_ = f; } - void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + using transform_func_t = optional (*)(ModbusNumber *, float, const std::vector &); + using write_transform_func_t = optional (*)(ModbusNumber *, float, std::vector &); + void set_template(transform_func_t f) { this->transform_func_ = f; } + void set_write_template(write_transform_func_t f) { this->write_transform_func_ = f; } void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; } protected: diff --git a/esphome/components/modbus_controller/output/modbus_output.h b/esphome/components/modbus_controller/output/modbus_output.h index bceb97affb..0fb4bb89ea 100644 --- a/esphome/components/modbus_controller/output/modbus_output.h +++ b/esphome/components/modbus_controller/output/modbus_output.h @@ -29,8 +29,8 @@ class ModbusFloatOutput : public output::FloatOutput, public Component, public S // Do nothing void parse_and_publish(const std::vector &data) override{}; - using write_transform_func_t = std::function(ModbusFloatOutput *, float, std::vector &)>; - void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + using write_transform_func_t = optional (*)(ModbusFloatOutput *, float, std::vector &); + void set_write_template(write_transform_func_t f) { this->write_transform_func_ = f; } void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; } protected: @@ -60,8 +60,8 @@ class ModbusBinaryOutput : public output::BinaryOutput, public Component, public // Do nothing void parse_and_publish(const std::vector &data) override{}; - using write_transform_func_t = std::function(ModbusBinaryOutput *, bool, std::vector &)>; - void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + using write_transform_func_t = optional (*)(ModbusBinaryOutput *, bool, std::vector &); + void set_write_template(write_transform_func_t f) { this->write_transform_func_ = f; } void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; } protected: diff --git a/esphome/components/modbus_controller/select/modbus_select.h b/esphome/components/modbus_controller/select/modbus_select.h index 55fb2107dd..e6b98aead2 100644 --- a/esphome/components/modbus_controller/select/modbus_select.h +++ b/esphome/components/modbus_controller/select/modbus_select.h @@ -26,16 +26,15 @@ class ModbusSelect : public Component, public select::Select, public SensorItem this->mapping_ = std::move(mapping); } - using transform_func_t = - std::function(ModbusSelect *const, int64_t, const std::vector &)>; - using write_transform_func_t = - std::function(ModbusSelect *const, const std::string &, int64_t, std::vector &)>; + using transform_func_t = optional (*)(ModbusSelect *const, int64_t, const std::vector &); + using write_transform_func_t = optional (*)(ModbusSelect *const, const std::string &, int64_t, + std::vector &); void set_parent(ModbusController *const parent) { this->parent_ = parent; } void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; } void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } - void set_template(transform_func_t &&f) { this->transform_func_ = f; } - void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + void set_template(transform_func_t f) { this->transform_func_ = f; } + void set_write_template(write_transform_func_t f) { this->write_transform_func_ = f; } void dump_config() override; void parse_and_publish(const std::vector &data) override; diff --git a/esphome/components/modbus_controller/sensor/modbus_sensor.h b/esphome/components/modbus_controller/sensor/modbus_sensor.h index 65eb487c1c..ba943c873c 100644 --- a/esphome/components/modbus_controller/sensor/modbus_sensor.h +++ b/esphome/components/modbus_controller/sensor/modbus_sensor.h @@ -25,9 +25,9 @@ class ModbusSensor : public Component, public sensor::Sensor, public SensorItem void parse_and_publish(const std::vector &data) override; void dump_config() override; - using transform_func_t = std::function(ModbusSensor *, float, const std::vector &)>; + using transform_func_t = optional (*)(ModbusSensor *, float, const std::vector &); - void set_template(transform_func_t &&f) { this->transform_func_ = f; } + void set_template(transform_func_t f) { this->transform_func_ = f; } protected: optional transform_func_{nullopt}; diff --git a/esphome/components/modbus_controller/switch/modbus_switch.h b/esphome/components/modbus_controller/switch/modbus_switch.h index 0098076ef4..301c2bf548 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.h +++ b/esphome/components/modbus_controller/switch/modbus_switch.h @@ -34,10 +34,10 @@ class ModbusSwitch : public Component, public switch_::Switch, public SensorItem void parse_and_publish(const std::vector &data) override; void set_parent(ModbusController *parent) { this->parent_ = parent; } - using transform_func_t = std::function(ModbusSwitch *, bool, const std::vector &)>; - using write_transform_func_t = std::function(ModbusSwitch *, bool, std::vector &)>; - void set_template(transform_func_t &&f) { this->publish_transform_func_ = f; } - void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + using transform_func_t = optional (*)(ModbusSwitch *, bool, const std::vector &); + using write_transform_func_t = optional (*)(ModbusSwitch *, bool, std::vector &); + void set_template(transform_func_t f) { this->publish_transform_func_ = f; } + void set_write_template(write_transform_func_t f) { this->write_transform_func_ = f; } void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; } protected: diff --git a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h index d6eb5fd230..6666aea976 100644 --- a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h +++ b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h @@ -30,9 +30,8 @@ class ModbusTextSensor : public Component, public text_sensor::TextSensor, publi void dump_config() override; void parse_and_publish(const std::vector &data) override; - using transform_func_t = - std::function(ModbusTextSensor *, std::string, const std::vector &)>; - void set_template(transform_func_t &&f) { this->transform_func_ = f; } + using transform_func_t = optional (*)(ModbusTextSensor *, std::string, const std::vector &); + void set_template(transform_func_t f) { this->transform_func_ = f; } protected: optional transform_func_{nullopt}; From 0119e17f0425bdb8655fe2ad171ca2cd29eb805c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 28 Oct 2025 14:08:13 -0500 Subject: [PATCH 283/526] [ci] Remove base bus components exclusion from memory impact analysis (#11572) --- script/determine-jobs.py | 17 ++++++++------- tests/script/test_determine_jobs.py | 32 ++++++++++++++++++----------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index ac384d74f1..21eb529f33 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -48,7 +48,6 @@ import sys from typing import Any from helpers import ( - BASE_BUS_COMPONENTS, CPP_FILE_EXTENSIONS, PYTHON_FILE_EXTENSIONS, changed_files, @@ -453,7 +452,7 @@ def detect_memory_impact_config( # Get actually changed files (not dependencies) files = changed_files(branch) - # Find all changed components (excluding core and base bus components) + # Find all changed components (excluding core) # Also collect platform hints from platform-specific filenames changed_component_set: set[str] = set() has_core_cpp_changes = False @@ -462,13 +461,13 @@ def detect_memory_impact_config( for file in files: component = get_component_from_path(file) if component: - # Skip base bus components as they're used across many builds - if component not in BASE_BUS_COMPONENTS: - changed_component_set.add(component) - # Check if this is a platform-specific file - platform_hint = _detect_platform_hint_from_filename(file) - if platform_hint: - platform_hints.append(platform_hint) + # Add all changed components, including base bus components + # Base bus components (uart, i2c, spi, etc.) should still be analyzed + # when directly changed, even though they're also used as dependencies + changed_component_set.add(component) + # Check if this is a platform-specific file + if platform_hint := _detect_platform_hint_from_filename(file): + platform_hints.append(platform_hint) elif file.startswith("esphome/") and file.endswith(CPP_FILE_EXTENSIONS): # Core ESPHome C++ files changed (not component-specific) # Only C++ files affect memory usage diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index c9ccf53252..c8ef76184f 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -849,39 +849,47 @@ def test_detect_memory_impact_config_no_components_with_tests(tmp_path: Path) -> assert result["should_run"] == "false" -def test_detect_memory_impact_config_skips_base_bus_components(tmp_path: Path) -> None: - """Test that base bus components (i2c, spi, uart) are skipped.""" +def test_detect_memory_impact_config_includes_base_bus_components( + tmp_path: Path, +) -> None: + """Test that base bus components (i2c, spi, uart) are included when directly changed. + + Base bus components should be analyzed for memory impact when they are directly + changed, even though they are often used as dependencies. This ensures that + optimizations to base components (like using move semantics or initializer_list) + are properly measured. + """ # Create test directory structure tests_dir = tmp_path / "tests" / "components" - # i2c component (should be skipped as it's a base bus component) - i2c_dir = tests_dir / "i2c" - i2c_dir.mkdir(parents=True) - (i2c_dir / "test.esp32-idf.yaml").write_text("test: i2c") + # uart component (base bus component that should be included) + uart_dir = tests_dir / "uart" + uart_dir.mkdir(parents=True) + (uart_dir / "test.esp32-idf.yaml").write_text("test: uart") - # wifi component (should not be skipped) + # wifi component (regular component) wifi_dir = tests_dir / "wifi" wifi_dir.mkdir(parents=True) (wifi_dir / "test.esp32-idf.yaml").write_text("test: wifi") - # Mock changed_files to return both i2c and wifi + # Mock changed_files to return both uart and wifi with ( patch.object(determine_jobs, "root_path", str(tmp_path)), patch.object(helpers, "root_path", str(tmp_path)), patch.object(determine_jobs, "changed_files") as mock_changed_files, ): mock_changed_files.return_value = [ - "esphome/components/i2c/i2c.cpp", + "esphome/components/uart/automation.h", # Header file with inline code "esphome/components/wifi/wifi.cpp", ] determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() - # Should only include wifi, not i2c + # Should include both uart and wifi assert result["should_run"] == "true" - assert result["components"] == ["wifi"] - assert "i2c" not in result["components"] + assert set(result["components"]) == {"uart", "wifi"} + assert result["platform"] == "esp32-idf" # Common platform def test_detect_memory_impact_config_with_variant_tests(tmp_path: Path) -> None: From 08b845455503cd5bc9b6b73c154bffb2255ee44b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 28 Oct 2025 14:10:32 -0500 Subject: [PATCH 284/526] [ble_client] Use function pointers for lambda actions and sensors (#11564) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/ble_client/automation.h | 75 ++++++++++++------- .../ble_client/sensor/ble_sensor.cpp | 4 +- .../components/ble_client/sensor/ble_sensor.h | 10 ++- 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index a5c661e2f5..55f1cb2f46 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -96,8 +96,11 @@ template class BLEClientWriteAction : public Action, publ BLEClientWriteAction(BLEClient *ble_client) { ble_client->register_ble_node(this); ble_client_ = ble_client; + this->construct_simple_value_(); } + ~BLEClientWriteAction() { this->destroy_simple_value_(); } + void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); } void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } @@ -106,14 +109,18 @@ template class BLEClientWriteAction : public Action, publ void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } - void set_value_template(std::function(Ts...)> func) { - this->value_template_ = std::move(func); - has_simple_value_ = false; + void set_value_template(std::vector (*func)(Ts...)) { + this->destroy_simple_value_(); + this->value_.template_func = func; + this->has_simple_value_ = false; } void set_value_simple(const std::vector &value) { - this->value_simple_ = value; - has_simple_value_ = true; + if (!this->has_simple_value_) { + this->construct_simple_value_(); + } + this->value_.simple = value; + this->has_simple_value_ = true; } void play(Ts... x) override {} @@ -121,7 +128,7 @@ template class BLEClientWriteAction : public Action, publ void play_complex(Ts... x) override { this->num_running_++; this->var_ = std::make_tuple(x...); - auto value = this->has_simple_value_ ? this->value_simple_ : this->value_template_(x...); + auto value = this->has_simple_value_ ? this->value_.simple : this->value_.template_func(x...); // on write failure, continue the automation chain rather than stopping so that e.g. disconnect can work. if (!write(value)) this->play_next_(x...); @@ -194,10 +201,22 @@ template class BLEClientWriteAction : public Action, publ } private: + void construct_simple_value_() { new (&this->value_.simple) std::vector(); } + + void destroy_simple_value_() { + if (this->has_simple_value_) { + this->value_.simple.~vector(); + } + } + BLEClient *ble_client_; bool has_simple_value_ = true; - std::vector value_simple_; - std::function(Ts...)> value_template_{}; + union Value { + std::vector simple; + std::vector (*template_func)(Ts...); + Value() {} // trivial constructor + ~Value() {} // trivial destructor - we manage lifetime via discriminator + } value_; espbt::ESPBTUUID service_uuid_; espbt::ESPBTUUID char_uuid_; std::tuple var_{}; @@ -213,9 +232,9 @@ template class BLEClientPasskeyReplyAction : public Actionvalue_simple_; + passkey = this->value_.simple; } else { - passkey = this->value_template_(x...); + passkey = this->value_.template_func(x...); } if (passkey > 999999) return; @@ -224,21 +243,23 @@ template class BLEClientPasskeyReplyAction : public Action func) { - this->value_template_ = std::move(func); - has_simple_value_ = false; + void set_value_template(uint32_t (*func)(Ts...)) { + this->value_.template_func = func; + this->has_simple_value_ = false; } void set_value_simple(const uint32_t &value) { - this->value_simple_ = value; - has_simple_value_ = true; + this->value_.simple = value; + this->has_simple_value_ = true; } private: BLEClient *parent_{nullptr}; bool has_simple_value_ = true; - uint32_t value_simple_{0}; - std::function value_template_{}; + union { + uint32_t simple; + uint32_t (*template_func)(Ts...); + } value_{.simple = 0}; }; template class BLEClientNumericComparisonReplyAction : public Action { @@ -249,27 +270,29 @@ template class BLEClientNumericComparisonReplyAction : public Ac esp_bd_addr_t remote_bda; memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t)); if (has_simple_value_) { - esp_ble_confirm_reply(remote_bda, this->value_simple_); + esp_ble_confirm_reply(remote_bda, this->value_.simple); } else { - esp_ble_confirm_reply(remote_bda, this->value_template_(x...)); + esp_ble_confirm_reply(remote_bda, this->value_.template_func(x...)); } } - void set_value_template(std::function func) { - this->value_template_ = std::move(func); - has_simple_value_ = false; + void set_value_template(bool (*func)(Ts...)) { + this->value_.template_func = func; + this->has_simple_value_ = false; } void set_value_simple(const bool &value) { - this->value_simple_ = value; - has_simple_value_ = true; + this->value_.simple = value; + this->has_simple_value_ = true; } private: BLEClient *parent_{nullptr}; bool has_simple_value_ = true; - bool value_simple_{false}; - std::function value_template_{}; + union { + bool simple; + bool (*template_func)(Ts...); + } value_{.simple = false}; }; template class BLEClientRemoveBondAction : public Action { diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index d0ccfe1f2e..6d293528c6 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -117,9 +117,9 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga } float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) { - if (this->data_to_value_func_.has_value()) { + if (this->has_data_to_value_) { std::vector data(value, value + value_len); - return (*this->data_to_value_func_)(data); + return this->data_to_value_func_(data); } else { return value[0]; } diff --git a/esphome/components/ble_client/sensor/ble_sensor.h b/esphome/components/ble_client/sensor/ble_sensor.h index 24d1ed2fd2..c6335d5836 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.h +++ b/esphome/components/ble_client/sensor/ble_sensor.h @@ -15,8 +15,6 @@ namespace ble_client { namespace espbt = esphome::esp32_ble_tracker; -using data_to_value_t = std::function)>; - class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClientNode { public: void loop() override; @@ -33,13 +31,17 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); } void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } - void set_data_to_value(data_to_value_t &&lambda) { this->data_to_value_func_ = lambda; } + void set_data_to_value(float (*lambda)(const std::vector &)) { + this->data_to_value_func_ = lambda; + this->has_data_to_value_ = true; + } void set_enable_notify(bool notify) { this->notify_ = notify; } uint16_t handle; protected: float parse_data_(uint8_t *value, uint16_t value_len); - optional data_to_value_func_{}; + bool has_data_to_value_{false}; + float (*data_to_value_func_)(const std::vector &){}; bool notify_; espbt::ESPBTUUID service_uuid_; espbt::ESPBTUUID char_uuid_; From 7ed7e7ad262853dcd553b36fbc9844212f703d6a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 28 Oct 2025 14:46:44 -0500 Subject: [PATCH 285/526] [climate] Replace std::set with FiniteSetMask for trait storage (#11466) --- esphome/components/api/api.proto | 12 +- esphome/components/api/api_connection.cpp | 12 +- esphome/components/api/api_pb2.h | 12 +- esphome/components/bedjet/bedjet_const.h | 3 +- .../bedjet/climate/bedjet_climate.h | 2 +- esphome/components/climate/climate.cpp | 4 +- esphome/components/climate/climate_mode.h | 12 +- esphome/components/climate/climate_traits.h | 103 ++++++++++-------- esphome/components/climate_ir/climate_ir.h | 18 +-- esphome/components/haier/haier_base.cpp | 6 +- esphome/components/haier/haier_base.h | 7 +- esphome/components/haier/hon_climate.cpp | 10 +- esphome/components/heatpumpir/heatpumpir.h | 11 +- esphome/components/midea/air_conditioner.h | 23 ++-- .../thermostat/thermostat_climate.h | 4 + esphome/components/toshiba/toshiba.cpp | 2 +- esphome/components/toshiba/toshiba.h | 6 +- .../components/tuya/climate/tuya_climate.cpp | 14 +-- tests/integration/state_utils.py | 6 + .../test_host_mode_climate_basic_state.py | 34 +++--- 20 files changed, 160 insertions(+), 141 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index d202486cfa..fae0f2e75a 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -989,7 +989,7 @@ message ListEntitiesClimateResponse { bool supports_current_temperature = 5; // Deprecated: use feature_flags bool supports_two_point_target_temperature = 6; // Deprecated: use feature_flags - repeated ClimateMode supported_modes = 7 [(container_pointer) = "std::set"]; + repeated ClimateMode supported_modes = 7 [(container_pointer_no_template) = "climate::ClimateModeMask"]; float visual_min_temperature = 8; float visual_max_temperature = 9; float visual_target_temperature_step = 10; @@ -998,11 +998,11 @@ message ListEntitiesClimateResponse { // Deprecated in API version 1.5 bool legacy_supports_away = 11 [deprecated=true]; bool supports_action = 12; // Deprecated: use feature_flags - repeated ClimateFanMode supported_fan_modes = 13 [(container_pointer) = "std::set"]; - repeated ClimateSwingMode supported_swing_modes = 14 [(container_pointer) = "std::set"]; - repeated string supported_custom_fan_modes = 15 [(container_pointer) = "std::set"]; - repeated ClimatePreset supported_presets = 16 [(container_pointer) = "std::set"]; - repeated string supported_custom_presets = 17 [(container_pointer) = "std::set"]; + repeated ClimateFanMode supported_fan_modes = 13 [(container_pointer_no_template) = "climate::ClimateFanModeMask"]; + repeated ClimateSwingMode supported_swing_modes = 14 [(container_pointer_no_template) = "climate::ClimateSwingModeMask"]; + repeated string supported_custom_fan_modes = 15 [(container_pointer) = "std::vector"]; + repeated ClimatePreset supported_presets = 16 [(container_pointer_no_template) = "climate::ClimatePresetMask"]; + repeated string supported_custom_presets = 17 [(container_pointer) = "std::vector"]; bool disabled_by_default = 18; string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON"]; EntityCategory entity_category = 20; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index f76080253d..382c4acc16 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -669,18 +669,18 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection msg.supports_action = traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION); // Current feature flags and other supported parameters msg.feature_flags = traits.get_feature_flags(); - msg.supported_modes = &traits.get_supported_modes_for_api_(); + msg.supported_modes = &traits.get_supported_modes(); msg.visual_min_temperature = traits.get_visual_min_temperature(); msg.visual_max_temperature = traits.get_visual_max_temperature(); msg.visual_target_temperature_step = traits.get_visual_target_temperature_step(); msg.visual_current_temperature_step = traits.get_visual_current_temperature_step(); msg.visual_min_humidity = traits.get_visual_min_humidity(); msg.visual_max_humidity = traits.get_visual_max_humidity(); - msg.supported_fan_modes = &traits.get_supported_fan_modes_for_api_(); - msg.supported_custom_fan_modes = &traits.get_supported_custom_fan_modes_for_api_(); - msg.supported_presets = &traits.get_supported_presets_for_api_(); - msg.supported_custom_presets = &traits.get_supported_custom_presets_for_api_(); - msg.supported_swing_modes = &traits.get_supported_swing_modes_for_api_(); + msg.supported_fan_modes = &traits.get_supported_fan_modes(); + msg.supported_custom_fan_modes = &traits.get_supported_custom_fan_modes(); + msg.supported_presets = &traits.get_supported_presets(); + msg.supported_custom_presets = &traits.get_supported_custom_presets(); + msg.supported_swing_modes = &traits.get_supported_swing_modes(); return fill_and_encode_entity_info(climate, msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index ed49498176..3e9a10c1f7 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1377,16 +1377,16 @@ class ListEntitiesClimateResponse final : public InfoResponseProtoMessage { #endif bool supports_current_temperature{false}; bool supports_two_point_target_temperature{false}; - const std::set *supported_modes{}; + const climate::ClimateModeMask *supported_modes{}; float visual_min_temperature{0.0f}; float visual_max_temperature{0.0f}; float visual_target_temperature_step{0.0f}; bool supports_action{false}; - const std::set *supported_fan_modes{}; - const std::set *supported_swing_modes{}; - const std::set *supported_custom_fan_modes{}; - const std::set *supported_presets{}; - const std::set *supported_custom_presets{}; + const climate::ClimateFanModeMask *supported_fan_modes{}; + const climate::ClimateSwingModeMask *supported_swing_modes{}; + const std::vector *supported_custom_fan_modes{}; + const climate::ClimatePresetMask *supported_presets{}; + const std::vector *supported_custom_presets{}; float visual_current_temperature_step{0.0f}; bool supports_current_humidity{false}; bool supports_target_humidity{false}; diff --git a/esphome/components/bedjet/bedjet_const.h b/esphome/components/bedjet/bedjet_const.h index 7cac1b61ff..0693be1092 100644 --- a/esphome/components/bedjet/bedjet_const.h +++ b/esphome/components/bedjet/bedjet_const.h @@ -99,9 +99,8 @@ enum BedjetCommand : uint8_t { static const uint8_t BEDJET_FAN_SPEED_COUNT = 20; -static const char *const BEDJET_FAN_STEP_NAMES[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_; +static constexpr const char *const BEDJET_FAN_STEP_NAMES[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_; static const std::string BEDJET_FAN_STEP_NAME_STRINGS[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_; -static const std::set BEDJET_FAN_STEP_NAMES_SET BEDJET_FAN_STEP_NAMES_; } // namespace bedjet } // namespace esphome diff --git a/esphome/components/bedjet/climate/bedjet_climate.h b/esphome/components/bedjet/climate/bedjet_climate.h index 963f2e585a..dbbb73aeae 100644 --- a/esphome/components/bedjet/climate/bedjet_climate.h +++ b/esphome/components/bedjet/climate/bedjet_climate.h @@ -43,7 +43,7 @@ class BedJetClimate : public climate::Climate, public BedJetClient, public Polli }); // It would be better if we had a slider for the fan modes. - traits.set_supported_custom_fan_modes(BEDJET_FAN_STEP_NAMES_SET); + traits.set_supported_custom_fan_modes(BEDJET_FAN_STEP_NAMES); traits.set_supported_presets({ // If we support NONE, then have to decide what happens if the user switches to it (turn off?) // climate::CLIMATE_PRESET_NONE, diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 19fe241729..944934edbf 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -385,7 +385,7 @@ void Climate::save_state_() { if (!traits.get_supported_custom_fan_modes().empty() && custom_fan_mode.has_value()) { state.uses_custom_fan_mode = true; const auto &supported = traits.get_supported_custom_fan_modes(); - // std::set has consistent order (lexicographic for strings) + // std::vector maintains insertion order size_t i = 0; for (const auto &mode : supported) { if (mode == custom_fan_mode) { @@ -402,7 +402,7 @@ void Climate::save_state_() { if (!traits.get_supported_custom_presets().empty() && custom_preset.has_value()) { state.uses_custom_preset = true; const auto &supported = traits.get_supported_custom_presets(); - // std::set has consistent order (lexicographic for strings) + // std::vector maintains insertion order size_t i = 0; for (const auto &preset : supported) { if (preset == custom_preset) { diff --git a/esphome/components/climate/climate_mode.h b/esphome/components/climate/climate_mode.h index faec5d2537..44423d2f22 100644 --- a/esphome/components/climate/climate_mode.h +++ b/esphome/components/climate/climate_mode.h @@ -7,6 +7,7 @@ namespace esphome { namespace climate { /// Enum for all modes a climate device can be in. +/// NOTE: If adding values, update ClimateModeMask in climate_traits.h to use the new last value enum ClimateMode : uint8_t { /// The climate device is off CLIMATE_MODE_OFF = 0, @@ -24,7 +25,7 @@ enum ClimateMode : uint8_t { * For example, the target temperature can be adjusted based on a schedule, or learned behavior. * The target temperature can't be adjusted when in this mode. */ - CLIMATE_MODE_AUTO = 6 + CLIMATE_MODE_AUTO = 6 // Update ClimateModeMask in climate_traits.h if adding values after this }; /// Enum for the current action of the climate device. Values match those of ClimateMode. @@ -43,6 +44,7 @@ enum ClimateAction : uint8_t { CLIMATE_ACTION_FAN = 6, }; +/// NOTE: If adding values, update ClimateFanModeMask in climate_traits.h to use the new last value enum ClimateFanMode : uint8_t { /// The fan mode is set to On CLIMATE_FAN_ON = 0, @@ -63,10 +65,11 @@ enum ClimateFanMode : uint8_t { /// The fan mode is set to Diffuse CLIMATE_FAN_DIFFUSE = 8, /// The fan mode is set to Quiet - CLIMATE_FAN_QUIET = 9, + CLIMATE_FAN_QUIET = 9, // Update ClimateFanModeMask in climate_traits.h if adding values after this }; /// Enum for all modes a climate swing can be in +/// NOTE: If adding values, update ClimateSwingModeMask in climate_traits.h to use the new last value enum ClimateSwingMode : uint8_t { /// The swing mode is set to Off CLIMATE_SWING_OFF = 0, @@ -75,10 +78,11 @@ enum ClimateSwingMode : uint8_t { /// The fan mode is set to Vertical CLIMATE_SWING_VERTICAL = 2, /// The fan mode is set to Horizontal - CLIMATE_SWING_HORIZONTAL = 3, + CLIMATE_SWING_HORIZONTAL = 3, // Update ClimateSwingModeMask in climate_traits.h if adding values after this }; /// Enum for all preset modes +/// NOTE: If adding values, update ClimatePresetMask in climate_traits.h to use the new last value enum ClimatePreset : uint8_t { /// No preset is active CLIMATE_PRESET_NONE = 0, @@ -95,7 +99,7 @@ enum ClimatePreset : uint8_t { /// Device is prepared for sleep CLIMATE_PRESET_SLEEP = 6, /// Device is reacting to activity (e.g., movement sensors) - CLIMATE_PRESET_ACTIVITY = 7, + CLIMATE_PRESET_ACTIVITY = 7, // Update ClimatePresetMask in climate_traits.h if adding values after this }; enum ClimateFeature : uint32_t { diff --git a/esphome/components/climate/climate_traits.h b/esphome/components/climate/climate_traits.h index 2962a147d7..1161a54f4e 100644 --- a/esphome/components/climate/climate_traits.h +++ b/esphome/components/climate/climate_traits.h @@ -1,19 +1,33 @@ #pragma once -#include +#include #include "climate_mode.h" +#include "esphome/core/finite_set_mask.h" #include "esphome/core/helpers.h" namespace esphome { - -#ifdef USE_API -namespace api { -class APIConnection; -} // namespace api -#endif - namespace climate { +// Type aliases for climate enum bitmasks +// These replace std::set to eliminate red-black tree overhead +// For contiguous enums starting at 0, DefaultBitPolicy provides 1:1 mapping (enum value = bit position) +// Bitmask size is automatically calculated from the last enum value +using ClimateModeMask = FiniteSetMask>; +using ClimateFanModeMask = FiniteSetMask>; +using ClimateSwingModeMask = + FiniteSetMask>; +using ClimatePresetMask = FiniteSetMask>; + +// Lightweight linear search for small vectors (1-20 items) +// Avoids std::find template overhead +template inline bool vector_contains(const std::vector &vec, const T &value) { + for (const auto &item : vec) { + if (item == value) + return true; + } + return false; +} + /** This class contains all static data for climate devices. * * All climate devices must support these features: @@ -107,48 +121,60 @@ class ClimateTraits { } } - void set_supported_modes(std::set modes) { this->supported_modes_ = std::move(modes); } + void set_supported_modes(ClimateModeMask modes) { this->supported_modes_ = modes; } void add_supported_mode(ClimateMode mode) { this->supported_modes_.insert(mode); } bool supports_mode(ClimateMode mode) const { return this->supported_modes_.count(mode); } - const std::set &get_supported_modes() const { return this->supported_modes_; } + const ClimateModeMask &get_supported_modes() const { return this->supported_modes_; } - void set_supported_fan_modes(std::set modes) { this->supported_fan_modes_ = std::move(modes); } + void set_supported_fan_modes(ClimateFanModeMask modes) { this->supported_fan_modes_ = modes; } void add_supported_fan_mode(ClimateFanMode mode) { this->supported_fan_modes_.insert(mode); } - void add_supported_custom_fan_mode(const std::string &mode) { this->supported_custom_fan_modes_.insert(mode); } + void add_supported_custom_fan_mode(const std::string &mode) { this->supported_custom_fan_modes_.push_back(mode); } bool supports_fan_mode(ClimateFanMode fan_mode) const { return this->supported_fan_modes_.count(fan_mode); } bool get_supports_fan_modes() const { return !this->supported_fan_modes_.empty() || !this->supported_custom_fan_modes_.empty(); } - const std::set &get_supported_fan_modes() const { return this->supported_fan_modes_; } + const ClimateFanModeMask &get_supported_fan_modes() const { return this->supported_fan_modes_; } - void set_supported_custom_fan_modes(std::set supported_custom_fan_modes) { + void set_supported_custom_fan_modes(std::vector supported_custom_fan_modes) { this->supported_custom_fan_modes_ = std::move(supported_custom_fan_modes); } - const std::set &get_supported_custom_fan_modes() const { return this->supported_custom_fan_modes_; } + void set_supported_custom_fan_modes(std::initializer_list modes) { + this->supported_custom_fan_modes_ = modes; + } + template void set_supported_custom_fan_modes(const char *const (&modes)[N]) { + this->supported_custom_fan_modes_.assign(modes, modes + N); + } + const std::vector &get_supported_custom_fan_modes() const { return this->supported_custom_fan_modes_; } bool supports_custom_fan_mode(const std::string &custom_fan_mode) const { - return this->supported_custom_fan_modes_.count(custom_fan_mode); + return vector_contains(this->supported_custom_fan_modes_, custom_fan_mode); } - void set_supported_presets(std::set presets) { this->supported_presets_ = std::move(presets); } + void set_supported_presets(ClimatePresetMask presets) { this->supported_presets_ = presets; } void add_supported_preset(ClimatePreset preset) { this->supported_presets_.insert(preset); } - void add_supported_custom_preset(const std::string &preset) { this->supported_custom_presets_.insert(preset); } + void add_supported_custom_preset(const std::string &preset) { this->supported_custom_presets_.push_back(preset); } bool supports_preset(ClimatePreset preset) const { return this->supported_presets_.count(preset); } bool get_supports_presets() const { return !this->supported_presets_.empty(); } - const std::set &get_supported_presets() const { return this->supported_presets_; } + const ClimatePresetMask &get_supported_presets() const { return this->supported_presets_; } - void set_supported_custom_presets(std::set supported_custom_presets) { + void set_supported_custom_presets(std::vector supported_custom_presets) { this->supported_custom_presets_ = std::move(supported_custom_presets); } - const std::set &get_supported_custom_presets() const { return this->supported_custom_presets_; } + void set_supported_custom_presets(std::initializer_list presets) { + this->supported_custom_presets_ = presets; + } + template void set_supported_custom_presets(const char *const (&presets)[N]) { + this->supported_custom_presets_.assign(presets, presets + N); + } + const std::vector &get_supported_custom_presets() const { return this->supported_custom_presets_; } bool supports_custom_preset(const std::string &custom_preset) const { - return this->supported_custom_presets_.count(custom_preset); + return vector_contains(this->supported_custom_presets_, custom_preset); } - void set_supported_swing_modes(std::set modes) { this->supported_swing_modes_ = std::move(modes); } + void set_supported_swing_modes(ClimateSwingModeMask modes) { this->supported_swing_modes_ = modes; } void add_supported_swing_mode(ClimateSwingMode mode) { this->supported_swing_modes_.insert(mode); } bool supports_swing_mode(ClimateSwingMode swing_mode) const { return this->supported_swing_modes_.count(swing_mode); } bool get_supports_swing_modes() const { return !this->supported_swing_modes_.empty(); } - const std::set &get_supported_swing_modes() const { return this->supported_swing_modes_; } + const ClimateSwingModeMask &get_supported_swing_modes() const { return this->supported_swing_modes_; } float get_visual_min_temperature() const { return this->visual_min_temperature_; } void set_visual_min_temperature(float visual_min_temperature) { @@ -179,23 +205,6 @@ class ClimateTraits { void set_visual_max_humidity(float visual_max_humidity) { this->visual_max_humidity_ = visual_max_humidity; } protected: -#ifdef USE_API - // The API connection is a friend class to access internal methods - friend class api::APIConnection; - // These methods return references to internal data structures. - // They are used by the API to avoid copying data when encoding messages. - // Warning: Do not use these methods outside of the API connection code. - // They return references to internal data that can be invalidated. - const std::set &get_supported_modes_for_api_() const { return this->supported_modes_; } - const std::set &get_supported_fan_modes_for_api_() const { return this->supported_fan_modes_; } - const std::set &get_supported_custom_fan_modes_for_api_() const { - return this->supported_custom_fan_modes_; - } - const std::set &get_supported_presets_for_api_() const { return this->supported_presets_; } - const std::set &get_supported_custom_presets_for_api_() const { return this->supported_custom_presets_; } - const std::set &get_supported_swing_modes_for_api_() const { return this->supported_swing_modes_; } -#endif - void set_mode_support_(climate::ClimateMode mode, bool supported) { if (supported) { this->supported_modes_.insert(mode); @@ -226,12 +235,12 @@ class ClimateTraits { float visual_min_humidity_{30}; float visual_max_humidity_{99}; - std::set supported_modes_ = {climate::CLIMATE_MODE_OFF}; - std::set supported_fan_modes_; - std::set supported_swing_modes_; - std::set supported_presets_; - std::set supported_custom_fan_modes_; - std::set supported_custom_presets_; + climate::ClimateModeMask supported_modes_{climate::CLIMATE_MODE_OFF}; + climate::ClimateFanModeMask supported_fan_modes_; + climate::ClimateSwingModeMask supported_swing_modes_; + climate::ClimatePresetMask supported_presets_; + std::vector supported_custom_fan_modes_; + std::vector supported_custom_presets_; }; } // namespace climate diff --git a/esphome/components/climate_ir/climate_ir.h b/esphome/components/climate_ir/climate_ir.h index ea0656121f..62a43f0b2d 100644 --- a/esphome/components/climate_ir/climate_ir.h +++ b/esphome/components/climate_ir/climate_ir.h @@ -24,16 +24,18 @@ class ClimateIR : public Component, public remote_base::RemoteTransmittable { public: ClimateIR(float minimum_temperature, float maximum_temperature, float temperature_step = 1.0f, - bool supports_dry = false, bool supports_fan_only = false, std::set fan_modes = {}, - std::set swing_modes = {}, std::set presets = {}) { + bool supports_dry = false, bool supports_fan_only = false, + climate::ClimateFanModeMask fan_modes = climate::ClimateFanModeMask(), + climate::ClimateSwingModeMask swing_modes = climate::ClimateSwingModeMask(), + climate::ClimatePresetMask presets = climate::ClimatePresetMask()) { this->minimum_temperature_ = minimum_temperature; this->maximum_temperature_ = maximum_temperature; this->temperature_step_ = temperature_step; this->supports_dry_ = supports_dry; this->supports_fan_only_ = supports_fan_only; - this->fan_modes_ = std::move(fan_modes); - this->swing_modes_ = std::move(swing_modes); - this->presets_ = std::move(presets); + this->fan_modes_ = fan_modes; + this->swing_modes_ = swing_modes; + this->presets_ = presets; } void setup() override; @@ -60,9 +62,9 @@ class ClimateIR : public Component, bool supports_heat_{true}; bool supports_dry_{false}; bool supports_fan_only_{false}; - std::set fan_modes_ = {}; - std::set swing_modes_ = {}; - std::set presets_ = {}; + climate::ClimateFanModeMask fan_modes_{}; + climate::ClimateSwingModeMask swing_modes_{}; + climate::ClimatePresetMask presets_{}; sensor::Sensor *sensor_{nullptr}; }; diff --git a/esphome/components/haier/haier_base.cpp b/esphome/components/haier/haier_base.cpp index 5709b8e9b5..cd2673a272 100644 --- a/esphome/components/haier/haier_base.cpp +++ b/esphome/components/haier/haier_base.cpp @@ -171,7 +171,7 @@ void HaierClimateBase::toggle_power() { PendingAction({ActionRequest::TOGGLE_POWER, esphome::optional()}); } -void HaierClimateBase::set_supported_swing_modes(const std::set &modes) { +void HaierClimateBase::set_supported_swing_modes(climate::ClimateSwingModeMask modes) { this->traits_.set_supported_swing_modes(modes); if (!modes.empty()) this->traits_.add_supported_swing_mode(climate::CLIMATE_SWING_OFF); @@ -179,13 +179,13 @@ void HaierClimateBase::set_supported_swing_modes(const std::sethaier_protocol_.set_answer_timeout(timeout); } -void HaierClimateBase::set_supported_modes(const std::set &modes) { +void HaierClimateBase::set_supported_modes(climate::ClimateModeMask modes) { this->traits_.set_supported_modes(modes); this->traits_.add_supported_mode(climate::CLIMATE_MODE_OFF); // Always available this->traits_.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); // Always available } -void HaierClimateBase::set_supported_presets(const std::set &presets) { +void HaierClimateBase::set_supported_presets(climate::ClimatePresetMask presets) { this->traits_.set_supported_presets(presets); if (!presets.empty()) this->traits_.add_supported_preset(climate::CLIMATE_PRESET_NONE); diff --git a/esphome/components/haier/haier_base.h b/esphome/components/haier/haier_base.h index f0597c49ff..e24217bfd9 100644 --- a/esphome/components/haier/haier_base.h +++ b/esphome/components/haier/haier_base.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include "esphome/components/climate/climate.h" #include "esphome/components/uart/uart.h" #include "esphome/core/automation.h" @@ -60,9 +59,9 @@ class HaierClimateBase : public esphome::Component, void send_power_off_command(); void toggle_power(); void reset_protocol() { this->reset_protocol_request_ = true; }; - void set_supported_modes(const std::set &modes); - void set_supported_swing_modes(const std::set &modes); - void set_supported_presets(const std::set &presets); + void set_supported_modes(esphome::climate::ClimateModeMask modes); + void set_supported_swing_modes(esphome::climate::ClimateSwingModeMask modes); + void set_supported_presets(esphome::climate::ClimatePresetMask presets); bool valid_connection() const { return this->protocol_phase_ >= ProtocolPhases::IDLE; }; size_t available() noexcept override { return esphome::uart::UARTDevice::available(); }; size_t read_array(uint8_t *data, size_t len) noexcept override { diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index 76558f2ebb..23d28bfd47 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -1033,9 +1033,9 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * { // Swing mode ClimateSwingMode old_swing_mode = this->swing_mode; - const std::set &swing_modes = traits_.get_supported_swing_modes(); - bool vertical_swing_supported = swing_modes.find(CLIMATE_SWING_VERTICAL) != swing_modes.end(); - bool horizontal_swing_supported = swing_modes.find(CLIMATE_SWING_HORIZONTAL) != swing_modes.end(); + const auto &swing_modes = traits_.get_supported_swing_modes(); + bool vertical_swing_supported = swing_modes.count(CLIMATE_SWING_VERTICAL); + bool horizontal_swing_supported = swing_modes.count(CLIMATE_SWING_HORIZONTAL); if (horizontal_swing_supported && (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO)) { if (vertical_swing_supported && @@ -1218,13 +1218,13 @@ void HonClimate::fill_control_messages_queue_() { (uint8_t) hon_protocol::DataParameters::QUIET_MODE, quiet_mode_buf, 2); } - if ((fast_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_BOOST) != presets.end()))) { + if ((fast_mode_buf[1] != 0xFF) && presets.count(climate::ClimatePreset::CLIMATE_PRESET_BOOST)) { this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint8_t) hon_protocol::DataParameters::FAST_MODE, fast_mode_buf, 2); } - if ((away_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_AWAY) != presets.end()))) { + if ((away_mode_buf[1] != 0xFF) && presets.count(climate::ClimatePreset::CLIMATE_PRESET_AWAY)) { this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint8_t) hon_protocol::DataParameters::TEN_DEGREE, diff --git a/esphome/components/heatpumpir/heatpumpir.h b/esphome/components/heatpumpir/heatpumpir.h index 3e14c11861..ed43ffdc83 100644 --- a/esphome/components/heatpumpir/heatpumpir.h +++ b/esphome/components/heatpumpir/heatpumpir.h @@ -97,12 +97,11 @@ const float TEMP_MAX = 100; // Celsius class HeatpumpIRClimate : public climate_ir::ClimateIR { public: HeatpumpIRClimate() - : climate_ir::ClimateIR( - TEMP_MIN, TEMP_MAX, 1.0f, true, true, - std::set{climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, - climate::CLIMATE_FAN_HIGH, climate::CLIMATE_FAN_AUTO}, - std::set{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_HORIZONTAL, - climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_BOTH}) {} + : climate_ir::ClimateIR(TEMP_MIN, TEMP_MAX, 1.0f, true, true, + {climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH, + climate::CLIMATE_FAN_AUTO}, + {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_HORIZONTAL, + climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_BOTH}) {} void setup() override; void set_protocol(Protocol protocol) { this->protocol_ = protocol; } void set_horizontal_default(HorizontalDirection horizontal_direction) { diff --git a/esphome/components/midea/air_conditioner.h b/esphome/components/midea/air_conditioner.h index e70bd34e71..6c2401efe7 100644 --- a/esphome/components/midea/air_conditioner.h +++ b/esphome/components/midea/air_conditioner.h @@ -19,6 +19,9 @@ using climate::ClimateTraits; using climate::ClimateMode; using climate::ClimateSwingMode; using climate::ClimateFanMode; +using climate::ClimateModeMask; +using climate::ClimateSwingModeMask; +using climate::ClimatePresetMask; class AirConditioner : public ApplianceBase, public climate::Climate { public: @@ -40,20 +43,20 @@ class AirConditioner : public ApplianceBase, void do_power_on() { this->base_.setPowerState(true); } void do_power_off() { this->base_.setPowerState(false); } void do_power_toggle() { this->base_.setPowerState(this->mode == ClimateMode::CLIMATE_MODE_OFF); } - void set_supported_modes(const std::set &modes) { this->supported_modes_ = modes; } - void set_supported_swing_modes(const std::set &modes) { this->supported_swing_modes_ = modes; } - void set_supported_presets(const std::set &presets) { this->supported_presets_ = presets; } - void set_custom_presets(const std::set &presets) { this->supported_custom_presets_ = presets; } - void set_custom_fan_modes(const std::set &modes) { this->supported_custom_fan_modes_ = modes; } + void set_supported_modes(ClimateModeMask modes) { this->supported_modes_ = modes; } + void set_supported_swing_modes(ClimateSwingModeMask modes) { this->supported_swing_modes_ = modes; } + void set_supported_presets(ClimatePresetMask presets) { this->supported_presets_ = presets; } + void set_custom_presets(const std::vector &presets) { this->supported_custom_presets_ = presets; } + void set_custom_fan_modes(const std::vector &modes) { this->supported_custom_fan_modes_ = modes; } protected: void control(const ClimateCall &call) override; ClimateTraits traits() override; - std::set supported_modes_{}; - std::set supported_swing_modes_{}; - std::set supported_presets_{}; - std::set supported_custom_presets_{}; - std::set supported_custom_fan_modes_{}; + ClimateModeMask supported_modes_{}; + ClimateSwingModeMask supported_swing_modes_{}; + ClimatePresetMask supported_presets_{}; + std::vector supported_custom_presets_{}; + std::vector supported_custom_fan_modes_{}; Sensor *outdoor_sensor_{nullptr}; Sensor *humidity_sensor_{nullptr}; Sensor *power_sensor_{nullptr}; diff --git a/esphome/components/thermostat/thermostat_climate.h b/esphome/components/thermostat/thermostat_climate.h index 363d2b09fc..42adab7751 100644 --- a/esphome/components/thermostat/thermostat_climate.h +++ b/esphome/components/thermostat/thermostat_climate.h @@ -40,6 +40,10 @@ enum OnBootRestoreFrom : uint8_t { }; struct ThermostatClimateTimer { + ThermostatClimateTimer() = default; + ThermostatClimateTimer(bool active, uint32_t time, uint32_t started, std::function func) + : active(active), time(time), started(started), func(std::move(func)) {} + bool active; uint32_t time; uint32_t started; diff --git a/esphome/components/toshiba/toshiba.cpp b/esphome/components/toshiba/toshiba.cpp index 36e5a21ffa..5efa70d6b4 100644 --- a/esphome/components/toshiba/toshiba.cpp +++ b/esphome/components/toshiba/toshiba.cpp @@ -405,7 +405,7 @@ void ToshibaClimate::setup() { this->swing_modes_ = this->toshiba_swing_modes_(); // Ensure swing mode is always initialized to a valid value - if (this->swing_modes_.empty() || this->swing_modes_.find(this->swing_mode) == this->swing_modes_.end()) { + if (this->swing_modes_.empty() || !this->swing_modes_.count(this->swing_mode)) { // No swing support for this model or current swing mode not supported, reset to OFF this->swing_mode = climate::CLIMATE_SWING_OFF; } diff --git a/esphome/components/toshiba/toshiba.h b/esphome/components/toshiba/toshiba.h index d76833f406..ee1dec5cc9 100644 --- a/esphome/components/toshiba/toshiba.h +++ b/esphome/components/toshiba/toshiba.h @@ -71,10 +71,10 @@ class ToshibaClimate : public climate_ir::ClimateIR { return TOSHIBA_RAS_2819T_TEMP_C_MAX; return TOSHIBA_GENERIC_TEMP_C_MAX; // Default to GENERIC for unknown models } - std::set toshiba_swing_modes_() { + climate::ClimateSwingModeMask toshiba_swing_modes_() { return (this->model_ == MODEL_GENERIC) - ? std::set{} - : std::set{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}; + ? climate::ClimateSwingModeMask() + : climate::ClimateSwingModeMask{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}; } void encode_(remote_base::RemoteTransmitData *data, const uint8_t *message, uint8_t nbytes, uint8_t repeat); bool decode_(remote_base::RemoteReceiveData *data, uint8_t *message, uint8_t nbytes); diff --git a/esphome/components/tuya/climate/tuya_climate.cpp b/esphome/components/tuya/climate/tuya_climate.cpp index d3c78104e3..4d8fd4b310 100644 --- a/esphome/components/tuya/climate/tuya_climate.cpp +++ b/esphome/components/tuya/climate/tuya_climate.cpp @@ -312,18 +312,12 @@ climate::ClimateTraits TuyaClimate::traits() { traits.add_supported_preset(climate::CLIMATE_PRESET_NONE); } if (this->swing_vertical_id_.has_value() && this->swing_horizontal_id_.has_value()) { - std::set supported_swing_modes = { - climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL, - climate::CLIMATE_SWING_HORIZONTAL}; - traits.set_supported_swing_modes(std::move(supported_swing_modes)); + traits.set_supported_swing_modes({climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, + climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL}); } else if (this->swing_vertical_id_.has_value()) { - std::set supported_swing_modes = {climate::CLIMATE_SWING_OFF, - climate::CLIMATE_SWING_VERTICAL}; - traits.set_supported_swing_modes(std::move(supported_swing_modes)); + traits.set_supported_swing_modes({climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}); } else if (this->swing_horizontal_id_.has_value()) { - std::set supported_swing_modes = {climate::CLIMATE_SWING_OFF, - climate::CLIMATE_SWING_HORIZONTAL}; - traits.set_supported_swing_modes(std::move(supported_swing_modes)); + traits.set_supported_swing_modes({climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_HORIZONTAL}); } if (fan_speed_id_) { diff --git a/tests/integration/state_utils.py b/tests/integration/state_utils.py index 58d6d2790f..6434a41ddf 100644 --- a/tests/integration/state_utils.py +++ b/tests/integration/state_utils.py @@ -44,6 +44,7 @@ class InitialStateHelper: helper = InitialStateHelper(entities) client.subscribe_states(helper.on_state_wrapper(user_callback)) await helper.wait_for_initial_states() + # Access initial states via helper.initial_states[key] """ def __init__(self, entities: list[EntityInfo]) -> None: @@ -63,6 +64,8 @@ class InitialStateHelper: self._entities_by_id = { (entity.device_id, entity.key): entity for entity in entities } + # Store initial states by key for test access + self.initial_states: dict[int, EntityState] = {} # Log all entities _LOGGER.debug( @@ -127,6 +130,9 @@ class InitialStateHelper: # If this entity is waiting for initial state if entity_id in self._wait_initial_states: + # Store the initial state for test access + self.initial_states[state.key] = state + # Remove from waiting set self._wait_initial_states.discard(entity_id) diff --git a/tests/integration/test_host_mode_climate_basic_state.py b/tests/integration/test_host_mode_climate_basic_state.py index 4697342a99..7d871ed5a8 100644 --- a/tests/integration/test_host_mode_climate_basic_state.py +++ b/tests/integration/test_host_mode_climate_basic_state.py @@ -2,12 +2,11 @@ from __future__ import annotations -import asyncio - import aioesphomeapi -from aioesphomeapi import ClimateAction, ClimateMode, ClimatePreset, EntityState +from aioesphomeapi import ClimateAction, ClimateInfo, ClimateMode, ClimatePreset import pytest +from .state_utils import InitialStateHelper from .types import APIClientConnectedFactory, RunCompiledFunction @@ -18,26 +17,27 @@ async def test_host_mode_climate_basic_state( api_client_connected: APIClientConnectedFactory, ) -> None: """Test basic climate state reporting.""" - loop = asyncio.get_running_loop() async with run_compiled(yaml_config), api_client_connected() as client: - states: dict[int, EntityState] = {} - climate_future: asyncio.Future[EntityState] = loop.create_future() + # Get entities and set up state synchronization + entities, services = await client.list_entities_services() + initial_state_helper = InitialStateHelper(entities) + climate_infos = [e for e in entities if isinstance(e, ClimateInfo)] + assert len(climate_infos) >= 1, "Expected at least 1 climate entity" - def on_state(state: EntityState) -> None: - states[state.key] = state - if ( - isinstance(state, aioesphomeapi.ClimateState) - and not climate_future.done() - ): - climate_future.set_result(state) - - client.subscribe_states(on_state) + # Subscribe with the wrapper (no-op callback since we just want initial states) + client.subscribe_states(initial_state_helper.on_state_wrapper(lambda _: None)) + # Wait for all initial states to be broadcast try: - climate_state = await asyncio.wait_for(climate_future, timeout=5.0) + await initial_state_helper.wait_for_initial_states() except TimeoutError: - pytest.fail("Climate state not received within 5 seconds") + pytest.fail("Timeout waiting for initial states") + # Get the climate entity and its initial state + test_climate = climate_infos[0] + climate_state = initial_state_helper.initial_states.get(test_climate.key) + + assert climate_state is not None, "Climate initial state not found" assert isinstance(climate_state, aioesphomeapi.ClimateState) assert climate_state.mode == ClimateMode.OFF assert climate_state.action == ClimateAction.OFF From f1bce262ed0f9f0e4eeea306b82ba070dbab59de Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 28 Oct 2025 15:48:20 -0500 Subject: [PATCH 286/526] [uart] Optimize UART components to eliminate temporary vector allocations (#11570) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/uart/__init__.py | 2 +- esphome/components/uart/automation.h | 8 ++++++-- esphome/components/uart/button/__init__.py | 2 +- esphome/components/uart/button/uart_button.h | 3 ++- esphome/components/uart/switch/__init__.py | 6 +++--- esphome/components/uart/switch/uart_switch.h | 6 ++++-- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index f8f927d469..eb911ed007 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -446,7 +446,7 @@ async def uart_write_to_code(config, action_id, template_arg, args): templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8)) cg.add(var.set_data_template(templ)) else: - cg.add(var.set_data_static(data)) + cg.add(var.set_data_static(cg.ArrayInitializer(*data))) return var diff --git a/esphome/components/uart/automation.h b/esphome/components/uart/automation.h index b6a50ea22d..9c599253de 100644 --- a/esphome/components/uart/automation.h +++ b/esphome/components/uart/automation.h @@ -14,8 +14,12 @@ template class UARTWriteAction : public Action, public Pa this->data_func_ = func; this->static_ = false; } - void set_data_static(const std::vector &data) { - this->data_static_ = data; + void set_data_static(std::vector &&data) { + this->data_static_ = std::move(data); + this->static_ = true; + } + void set_data_static(std::initializer_list data) { + this->data_static_ = std::vector(data); this->static_ = true; } diff --git a/esphome/components/uart/button/__init__.py b/esphome/components/uart/button/__init__.py index 5b811de07d..95fe21271d 100644 --- a/esphome/components/uart/button/__init__.py +++ b/esphome/components/uart/button/__init__.py @@ -33,4 +33,4 @@ async def to_code(config): data = config[CONF_DATA] if isinstance(data, bytes): data = [HexInt(x) for x in data] - cg.add(var.set_data(data)) + cg.add(var.set_data(cg.ArrayInitializer(*data))) diff --git a/esphome/components/uart/button/uart_button.h b/esphome/components/uart/button/uart_button.h index 2d600b199a..8c7d762a05 100644 --- a/esphome/components/uart/button/uart_button.h +++ b/esphome/components/uart/button/uart_button.h @@ -11,7 +11,8 @@ namespace uart { class UARTButton : public button::Button, public UARTDevice, public Component { public: - void set_data(const std::vector &data) { this->data_ = data; } + void set_data(std::vector &&data) { this->data_ = std::move(data); } + void set_data(std::initializer_list data) { this->data_ = std::vector(data); } void dump_config() override; diff --git a/esphome/components/uart/switch/__init__.py b/esphome/components/uart/switch/__init__.py index b25e070461..290bbed5d3 100644 --- a/esphome/components/uart/switch/__init__.py +++ b/esphome/components/uart/switch/__init__.py @@ -44,16 +44,16 @@ async def to_code(config): if data_on := data.get(CONF_TURN_ON): if isinstance(data_on, bytes): data_on = [HexInt(x) for x in data_on] - cg.add(var.set_data_on(data_on)) + cg.add(var.set_data_on(cg.ArrayInitializer(*data_on))) if data_off := data.get(CONF_TURN_OFF): if isinstance(data_off, bytes): data_off = [HexInt(x) for x in data_off] - cg.add(var.set_data_off(data_off)) + cg.add(var.set_data_off(cg.ArrayInitializer(*data_off))) else: data = config[CONF_DATA] if isinstance(data, bytes): data = [HexInt(x) for x in data] - cg.add(var.set_data_on(data)) + cg.add(var.set_data_on(cg.ArrayInitializer(*data))) cg.add(var.set_single_state(True)) if CONF_SEND_EVERY in config: cg.add(var.set_send_every(config[CONF_SEND_EVERY])) diff --git a/esphome/components/uart/switch/uart_switch.h b/esphome/components/uart/switch/uart_switch.h index 4ef5b6da4b..909307d57e 100644 --- a/esphome/components/uart/switch/uart_switch.h +++ b/esphome/components/uart/switch/uart_switch.h @@ -14,8 +14,10 @@ class UARTSwitch : public switch_::Switch, public UARTDevice, public Component { public: void loop() override; - void set_data_on(const std::vector &data) { this->data_on_ = data; } - void set_data_off(const std::vector &data) { this->data_off_ = data; } + void set_data_on(std::vector &&data) { this->data_on_ = std::move(data); } + void set_data_on(std::initializer_list data) { this->data_on_ = std::vector(data); } + void set_data_off(std::vector &&data) { this->data_off_ = std::move(data); } + void set_data_off(std::initializer_list data) { this->data_off_ = std::vector(data); } void set_send_every(uint32_t send_every) { this->send_every_ = send_every; } void set_single_state(bool single) { this->single_state_ = single; } From e46221750048ccd5242d720fe6ce50109a5c7feb Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Tue, 28 Oct 2025 23:18:47 +0100 Subject: [PATCH 287/526] [packages] Tighten package validation (#11584) --- esphome/components/packages/__init__.py | 2 +- .../component_tests/packages/test_packages.py | 46 ++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/esphome/components/packages/__init__.py b/esphome/components/packages/__init__.py index fdc75d995a..04057c07f2 100644 --- a/esphome/components/packages/__init__.py +++ b/esphome/components/packages/__init__.py @@ -102,7 +102,7 @@ CONFIG_SCHEMA = cv.Any( str: PACKAGE_SCHEMA, } ), - cv.ensure_list(PACKAGE_SCHEMA), + [PACKAGE_SCHEMA], ) diff --git a/tests/component_tests/packages/test_packages.py b/tests/component_tests/packages/test_packages.py index d66ca58a69..1c4c91aa52 100644 --- a/tests/component_tests/packages/test_packages.py +++ b/tests/component_tests/packages/test_packages.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock, patch import pytest -from esphome.components.packages import do_packages_pass +from esphome.components.packages import CONFIG_SCHEMA, do_packages_pass from esphome.config import resolve_extend_remove from esphome.config_helpers import Extend, Remove import esphome.config_validation as cv @@ -94,6 +94,50 @@ def test_package_invalid_dict(basic_esphome, basic_wifi): packages_pass(config) +@pytest.mark.parametrize( + "package", + [ + {"package1": "github://esphome/non-existant-repo/file1.yml@main"}, + {"package2": "github://esphome/non-existant-repo/file1.yml"}, + {"package3": "github://esphome/non-existant-repo/other-folder/file1.yml"}, + [ + "github://esphome/non-existant-repo/file1.yml@main", + "github://esphome/non-existant-repo/file1.yml", + "github://esphome/non-existant-repo/other-folder/file1.yml", + ], + ], +) +def test_package_shorthand(package): + CONFIG_SCHEMA(package) + + +@pytest.mark.parametrize( + "package", + [ + # not github + {"package1": "someplace://esphome/non-existant-repo/file1.yml@main"}, + # missing repo + {"package2": "github://esphome/file1.yml"}, + # missing file + {"package3": "github://esphome/non-existant-repo/@main"}, + {"a": "invalid string, not shorthand"}, + "some string", + 3, + False, + {"a": 8}, + ["someplace://esphome/non-existant-repo/file1.yml@main"], + ["github://esphome/file1.yml"], + ["github://esphome/non-existant-repo/@main"], + ["some string"], + [True], + [3], + ], +) +def test_package_invalid(package): + with pytest.raises(cv.Invalid): + CONFIG_SCHEMA(package) + + def test_package_include(basic_wifi, basic_esphome): """ Tests the simple case where an independent config present in a package is added to the top-level config as is. From 466d4522bc05a0274f371ed72e5ea15f3147b148 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:17:16 +1300 Subject: [PATCH 288/526] [http_request] Pass trigger variables into on_response/on_error (#11464) --- esphome/components/http_request/__init__.py | 55 ++++++++++--------- .../components/http_request/http_request.h | 53 +++++++++++------- esphome/core/defines.h | 4 ++ tests/components/http_request/common.yaml | 45 --------------- .../components/http_request/http_request.yaml | 14 +++++ 5 files changed, 79 insertions(+), 92 deletions(-) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index e428838c83..f4fa448c5b 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -12,7 +12,6 @@ from esphome.const import ( CONF_ON_ERROR, CONF_ON_RESPONSE, CONF_TIMEOUT, - CONF_TRIGGER_ID, CONF_URL, CONF_WATCHDOG_TIMEOUT, PLATFORM_HOST, @@ -216,16 +215,8 @@ HTTP_REQUEST_ACTION_SCHEMA = cv.Schema( f"{CONF_VERIFY_SSL} has moved to the base component configuration." ), cv.Optional(CONF_CAPTURE_RESPONSE, default=False): cv.boolean, - cv.Optional(CONF_ON_RESPONSE): automation.validate_automation( - {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(HttpRequestResponseTrigger)} - ), - cv.Optional(CONF_ON_ERROR): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - automation.Trigger.template() - ) - } - ), + cv.Optional(CONF_ON_RESPONSE): automation.validate_automation(single=True), + cv.Optional(CONF_ON_ERROR): automation.validate_automation(single=True), cv.Optional(CONF_MAX_RESPONSE_BUFFER_SIZE, default="1kB"): cv.validate_bytes, } ) @@ -280,7 +271,12 @@ async def http_request_action_to_code(config, action_id, template_arg, args): template_ = await cg.templatable(config[CONF_URL], args, cg.std_string) cg.add(var.set_url(template_)) cg.add(var.set_method(config[CONF_METHOD])) - cg.add(var.set_capture_response(config[CONF_CAPTURE_RESPONSE])) + + capture_response = config[CONF_CAPTURE_RESPONSE] + if capture_response: + cg.add(var.set_capture_response(capture_response)) + cg.add_define("USE_HTTP_REQUEST_RESPONSE") + cg.add(var.set_max_response_buffer_size(config[CONF_MAX_RESPONSE_BUFFER_SIZE])) if CONF_BODY in config: @@ -303,21 +299,26 @@ async def http_request_action_to_code(config, action_id, template_arg, args): for value in config.get(CONF_COLLECT_HEADERS, []): cg.add(var.add_collect_header(value)) - for conf in config.get(CONF_ON_RESPONSE, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) - cg.add(var.register_response_trigger(trigger)) - await automation.build_automation( - trigger, - [ - (cg.std_shared_ptr.template(HttpContainer), "response"), - (cg.std_string_ref, "body"), - ], - conf, - ) - for conf in config.get(CONF_ON_ERROR, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) - cg.add(var.register_error_trigger(trigger)) - await automation.build_automation(trigger, [], conf) + if response_conf := config.get(CONF_ON_RESPONSE): + if capture_response: + await automation.build_automation( + var.get_success_trigger_with_response(), + [ + (cg.std_shared_ptr.template(HttpContainer), "response"), + (cg.std_string_ref, "body"), + *args, + ], + response_conf, + ) + else: + await automation.build_automation( + var.get_success_trigger(), + [(cg.std_shared_ptr.template(HttpContainer), "response"), *args], + response_conf, + ) + + if error_conf := config.get(CONF_ON_ERROR): + await automation.build_automation(var.get_error_trigger(), args, error_conf) return var diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 5010cf47a0..482cd2da44 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -183,7 +183,9 @@ template class HttpRequestSendAction : public Action { TEMPLATABLE_VALUE(std::string, url) TEMPLATABLE_VALUE(const char *, method) TEMPLATABLE_VALUE(std::string, body) +#ifdef USE_HTTP_REQUEST_RESPONSE TEMPLATABLE_VALUE(bool, capture_response) +#endif void add_request_header(const char *key, TemplatableValue value) { this->request_headers_.insert({key, value}); @@ -195,9 +197,14 @@ template class HttpRequestSendAction : public Action { void set_json(std::function json_func) { this->json_func_ = json_func; } - void register_response_trigger(HttpRequestResponseTrigger *trigger) { this->response_triggers_.push_back(trigger); } +#ifdef USE_HTTP_REQUEST_RESPONSE + Trigger, std::string &, Ts...> *get_success_trigger_with_response() const { + return this->success_trigger_with_response_; + } +#endif + Trigger, Ts...> *get_success_trigger() const { return this->success_trigger_; } - void register_error_trigger(Trigger<> *trigger) { this->error_triggers_.push_back(trigger); } + Trigger *get_error_trigger() const { return this->error_trigger_; } void set_max_response_buffer_size(size_t max_response_buffer_size) { this->max_response_buffer_size_ = max_response_buffer_size; @@ -228,17 +235,20 @@ template class HttpRequestSendAction : public Action { auto container = this->parent_->start(this->url_.value(x...), this->method_.value(x...), body, request_headers, this->collect_headers_); + auto captured_args = std::make_tuple(x...); + if (container == nullptr) { - for (auto *trigger : this->error_triggers_) - trigger->trigger(); + std::apply([this](Ts... captured_args_inner) { this->error_trigger_->trigger(captured_args_inner...); }, + captured_args); return; } size_t content_length = container->content_length; size_t max_length = std::min(content_length, this->max_response_buffer_size_); - std::string response_body; +#ifdef USE_HTTP_REQUEST_RESPONSE if (this->capture_response_.value(x...)) { + std::string response_body; RAMAllocator allocator; uint8_t *buf = allocator.allocate(max_length); if (buf != nullptr) { @@ -253,19 +263,17 @@ template class HttpRequestSendAction : public Action { response_body.assign((char *) buf, read_index); allocator.deallocate(buf, max_length); } - } - - if (this->response_triggers_.size() == 1) { - // if there is only one trigger, no need to copy the response body - this->response_triggers_[0]->process(container, response_body); - } else { - for (auto *trigger : this->response_triggers_) { - // with multiple triggers, pass a copy of the response body to each - // one so that modifications made in one trigger are not visible to - // the others - auto response_body_copy = std::string(response_body); - trigger->process(container, response_body_copy); - } + std::apply( + [this, &container, &response_body](Ts... captured_args_inner) { + this->success_trigger_with_response_->trigger(container, response_body, captured_args_inner...); + }, + captured_args); + } else +#endif + { + std::apply([this, &container]( + Ts... captured_args_inner) { this->success_trigger_->trigger(container, captured_args_inner...); }, + captured_args); } container->end(); } @@ -283,8 +291,13 @@ template class HttpRequestSendAction : public Action { std::set collect_headers_{"content-type", "content-length"}; std::map> json_{}; std::function json_func_{nullptr}; - std::vector response_triggers_{}; - std::vector *> error_triggers_{}; +#ifdef USE_HTTP_REQUEST_RESPONSE + Trigger, std::string &, Ts...> *success_trigger_with_response_ = + new Trigger, std::string &, Ts...>(); +#endif + Trigger, Ts...> *success_trigger_ = + new Trigger, Ts...>(); + Trigger *error_trigger_ = new Trigger(); size_t max_response_buffer_size_{SIZE_MAX}; }; diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 97e766455a..868df6e254 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -187,6 +187,7 @@ #define ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT 1 #define ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT 2 #define USE_ESP32_CAMERA_JPEG_ENCODER +#define USE_HTTP_REQUEST_RESPONSE #define USE_I2C #define USE_IMPROV #define USE_ESP32_IMPROV_NEXT_URL @@ -237,6 +238,7 @@ #define USE_CAPTIVE_PORTAL #define USE_ESP8266_PREFERENCES_FLASH #define USE_HTTP_REQUEST_ESP8266_HTTPS +#define USE_HTTP_REQUEST_RESPONSE #define USE_I2C #define USE_SOCKET_IMPL_LWIP_TCP @@ -257,6 +259,7 @@ #ifdef USE_RP2040 #define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 3, 0) +#define USE_HTTP_REQUEST_RESPONSE #define USE_I2C #define USE_LOGGER_USB_CDC #define USE_SOCKET_IMPL_LWIP_TCP @@ -273,6 +276,7 @@ #endif #ifdef USE_HOST +#define USE_HTTP_REQUEST_RESPONSE #define USE_SOCKET_IMPL_BSD_SOCKETS #define USE_SOCKET_SELECT_SUPPORT #endif diff --git a/tests/components/http_request/common.yaml b/tests/components/http_request/common.yaml index 9ff9f9fb67..62d0a7941a 100644 --- a/tests/components/http_request/common.yaml +++ b/tests/components/http_request/common.yaml @@ -4,51 +4,6 @@ wifi: ssid: MySSID password: password1 -esphome: - on_boot: - then: - - http_request.get: - url: https://esphome.io - request_headers: - Content-Type: application/json - collect_headers: - - age - on_error: - logger.log: "Request failed" - on_response: - then: - - logger.log: - format: "Response status: %d, Duration: %lu ms, age: %s" - args: - - response->status_code - - (long) response->duration_ms - - response->get_response_header("age").c_str() - - http_request.post: - url: https://esphome.io - request_headers: - Content-Type: application/json - json: - key: value - - http_request.send: - method: PUT - url: https://esphome.io - request_headers: - Content-Type: application/json - body: "Some data" - -http_request: - useragent: esphome/tagreader - timeout: 10s - verify_ssl: ${verify_ssl} - -script: - - id: does_not_compile - parameters: - api_url: string - then: - - http_request.get: - url: "http://google.com" - ota: - platform: http_request id: http_request_ota diff --git a/tests/components/http_request/http_request.yaml b/tests/components/http_request/http_request.yaml index ea7f6bf5a7..13ca5ceba0 100644 --- a/tests/components/http_request/http_request.yaml +++ b/tests/components/http_request/http_request.yaml @@ -31,6 +31,20 @@ esphome: request_headers: Content-Type: application/json body: "Some data" + - http_request.post: + url: https://esphome.io + request_headers: + Content-Type: application/json + json: + key: value + capture_response: true + on_response: + then: + - logger.log: + format: "Captured response status: %d, Body: %s" + args: + - response->status_code + - body.c_str() http_request: useragent: esphome/tagreader From 78d780105bf9a9a25a5b3f570f178427f4d2c783 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 28 Oct 2025 19:24:37 -0500 Subject: [PATCH 289/526] [ci] Change upper Python version being tested to 3.13 (#11587) --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb04f6bf8d..655e28e3b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,7 +114,7 @@ jobs: matrix: python-version: - "3.11" - - "3.14" + - "3.13" os: - ubuntu-latest - macOS-latest @@ -123,9 +123,9 @@ jobs: # Minimize CI resource usage # by only running the Python version # version used for docker images on Windows and macOS - - python-version: "3.14" + - python-version: "3.13" os: windows-latest - - python-version: "3.14" + - python-version: "3.13" os: macOS-latest runs-on: ${{ matrix.os }} needs: From 249cd7415badfc720894e4dd9a64d0c4625428bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 00:32:41 +0000 Subject: [PATCH 290/526] Bump aioesphomeapi from 42.3.0 to 42.4.0 (#11586) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4a64bd39cc..b0d7d62c36 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==42.3.0 +aioesphomeapi==42.4.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.16 # dashboard_import From 4f2d54be4edafbe87996c20529b938f8eef8e93b Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 29 Oct 2025 08:48:26 +0800 Subject: [PATCH 291/526] template_alarm_control_panel cleanups (#11469) --- .../template_alarm_control_panel.cpp | 68 ++++++++----------- .../template_alarm_control_panel.h | 8 +-- 2 files changed, 33 insertions(+), 43 deletions(-) diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp index eac0629480..d1562ee82f 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp @@ -80,19 +80,12 @@ void TemplateAlarmControlPanel::dump_config() { } void TemplateAlarmControlPanel::setup() { - switch (this->restore_mode_) { - case ALARM_CONTROL_PANEL_ALWAYS_DISARMED: - this->current_state_ = ACP_STATE_DISARMED; - break; - case ALARM_CONTROL_PANEL_RESTORE_DEFAULT_DISARMED: { - uint8_t value; - this->pref_ = global_preferences->make_preference(this->get_preference_hash()); - if (this->pref_.load(&value)) { - this->current_state_ = static_cast(value); - } else { - this->current_state_ = ACP_STATE_DISARMED; - } - break; + this->current_state_ = ACP_STATE_DISARMED; + if (this->restore_mode_ == ALARM_CONTROL_PANEL_RESTORE_DEFAULT_DISARMED) { + uint8_t value; + this->pref_ = global_preferences->make_preference(this->get_preference_hash()); + if (this->pref_.load(&value)) { + this->current_state_ = static_cast(value); } } this->desired_state_ = this->current_state_; @@ -119,15 +112,15 @@ void TemplateAlarmControlPanel::loop() { this->publish_state(ACP_STATE_TRIGGERED); return; } - auto future_state = this->current_state_; + auto next_state = this->current_state_; // reset triggered if all clear if (this->current_state_ == ACP_STATE_TRIGGERED && this->trigger_time_ > 0 && (millis() - this->last_update_) > this->trigger_time_) { - future_state = this->desired_state_; + next_state = this->desired_state_; } - bool delayed_sensor_not_ready = false; - bool instant_sensor_not_ready = false; + bool delayed_sensor_faulted = false; + bool instant_sensor_faulted = false; #ifdef USE_BINARY_SENSOR // Test all of the sensors in the list regardless of the alarm panel state @@ -144,7 +137,7 @@ void TemplateAlarmControlPanel::loop() { // Record the sensor state change this->sensor_data_[sensor_info.second.store_index].last_chime_state = sensor_info.first->state; } - // Check for triggered sensors + // Check for faulted sensors if (sensor_info.first->state) { // Sensor triggered? // Skip if auto bypassed if (std::count(this->bypassed_sensor_indicies_.begin(), this->bypassed_sensor_indicies_.end(), @@ -163,42 +156,41 @@ void TemplateAlarmControlPanel::loop() { } switch (sensor_info.second.type) { - case ALARM_SENSOR_TYPE_INSTANT: - instant_sensor_not_ready = true; - break; case ALARM_SENSOR_TYPE_INSTANT_ALWAYS: - instant_sensor_not_ready = true; - future_state = ACP_STATE_TRIGGERED; + next_state = ACP_STATE_TRIGGERED; + [[fallthrough]]; + case ALARM_SENSOR_TYPE_INSTANT: + instant_sensor_faulted = true; break; case ALARM_SENSOR_TYPE_DELAYED_FOLLOWER: // Look to see if we are in the pending state if (this->current_state_ == ACP_STATE_PENDING) { - delayed_sensor_not_ready = true; + delayed_sensor_faulted = true; } else { - instant_sensor_not_ready = true; + instant_sensor_faulted = true; } break; case ALARM_SENSOR_TYPE_DELAYED: default: - delayed_sensor_not_ready = true; + delayed_sensor_faulted = true; } } } - // Update all sensors not ready flag - this->sensors_ready_ = ((!instant_sensor_not_ready) && (!delayed_sensor_not_ready)); + // Update all sensors ready flag + bool sensors_ready = !(instant_sensor_faulted || delayed_sensor_faulted); // Call the ready state change callback if there was a change - if (this->sensors_ready_ != this->sensors_ready_last_) { + if (this->sensors_ready_ != sensors_ready) { + this->sensors_ready_ = sensors_ready; this->ready_callback_.call(); - this->sensors_ready_last_ = this->sensors_ready_; } #endif - if (this->is_state_armed(future_state) && (!this->sensors_ready_)) { + if (this->is_state_armed(next_state) && (!this->sensors_ready_)) { // Instant sensors - if (instant_sensor_not_ready) { + if (instant_sensor_faulted) { this->publish_state(ACP_STATE_TRIGGERED); - } else if (delayed_sensor_not_ready) { + } else if (delayed_sensor_faulted) { // Delayed sensors if ((this->pending_time_ > 0) && (this->current_state_ != ACP_STATE_TRIGGERED)) { this->publish_state(ACP_STATE_PENDING); @@ -206,8 +198,8 @@ void TemplateAlarmControlPanel::loop() { this->publish_state(ACP_STATE_TRIGGERED); } } - } else if (future_state != this->current_state_) { - this->publish_state(future_state); + } else if (next_state != this->current_state_) { + this->publish_state(next_state); } } @@ -234,8 +226,6 @@ uint32_t TemplateAlarmControlPanel::get_supported_features() const { return features; } -bool TemplateAlarmControlPanel::get_requires_code() const { return !this->codes_.empty(); } - void TemplateAlarmControlPanel::arm_(optional code, alarm_control_panel::AlarmControlPanelState state, uint32_t delay) { if (this->current_state_ != ACP_STATE_DISARMED) { @@ -258,9 +248,9 @@ void TemplateAlarmControlPanel::arm_(optional code, alarm_control_p void TemplateAlarmControlPanel::bypass_before_arming() { #ifdef USE_BINARY_SENSOR for (auto sensor_info : this->sensor_map_) { - // Check for sensors left on and set to bypass automatically and remove them from monitoring + // Check for faulted bypass_auto sensors and remove them from monitoring if ((sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor_info.first->state)) { - ESP_LOGW(TAG, "'%s' is left on and will be automatically bypassed", sensor_info.first->get_name().c_str()); + ESP_LOGW(TAG, "'%s' is faulted and will be automatically bypassed", sensor_info.first->get_name().c_str()); this->bypassed_sensor_indicies_.push_back(sensor_info.second.store_index); } } diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h index c3b28e8efa..40a79004da 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h @@ -56,7 +56,7 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, void setup() override; void loop() override; uint32_t get_supported_features() const override; - bool get_requires_code() const override; + bool get_requires_code() const override { return !this->codes_.empty(); } bool get_requires_code_to_arm() const override { return this->requires_code_to_arm_; } bool get_all_sensors_ready() { return this->sensors_ready_; }; void set_restore_mode(TemplateAlarmControlPanelRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } @@ -66,7 +66,8 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, /** Add a binary_sensor to the alarm_panel. * * @param sensor The BinarySensor instance. - * @param ignore_when_home if this should be ignored when armed_home mode + * @param flags The OR of BinarySensorFlags for the sensor. + * @param type The sensor type which determines its triggering behaviour. */ void add_sensor(binary_sensor::BinarySensor *sensor, uint16_t flags = 0, AlarmSensorType type = ALARM_SENSOR_TYPE_DELAYED); @@ -121,7 +122,7 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, protected: void control(const alarm_control_panel::AlarmControlPanelCall &call) override; #ifdef USE_BINARY_SENSOR - // This maps a binary sensor to its type and attribute bits + // This maps a binary sensor to its alarm specific info std::map sensor_map_; // a list of automatically bypassed sensors std::vector bypassed_sensor_indicies_; @@ -147,7 +148,6 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, bool supports_arm_home_ = false; bool supports_arm_night_ = false; bool sensors_ready_ = false; - bool sensors_ready_last_ = false; uint8_t next_store_index_ = 0; // check if the code is valid bool is_code_valid_(optional code); From 25e4aafd7146a43883213eede281193ce75745b8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 29 Oct 2025 14:28:29 +1300 Subject: [PATCH 292/526] [ci] Fix auto labeller workflow with wrong comment for too-big with labels (#11592) --- .github/workflows/auto-label-pr.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/auto-label-pr.yml b/.github/workflows/auto-label-pr.yml index 4e2f086f47..dd1bc29d83 100644 --- a/.github/workflows/auto-label-pr.yml +++ b/.github/workflows/auto-label-pr.yml @@ -416,7 +416,7 @@ jobs: } // Generate review messages - function generateReviewMessages(finalLabels) { + function generateReviewMessages(finalLabels, originalLabelCount) { const messages = []; const prAuthor = context.payload.pull_request.user.login; @@ -430,15 +430,15 @@ jobs: .reduce((sum, file) => sum + (file.deletions || 0), 0); const nonTestChanges = (totalAdditions - testAdditions) - (totalDeletions - testDeletions); - const tooManyLabels = finalLabels.length > MAX_LABELS; + const tooManyLabels = originalLabelCount > MAX_LABELS; const tooManyChanges = nonTestChanges > TOO_BIG_THRESHOLD; let message = `${TOO_BIG_MARKER}\n### 📦 Pull Request Size\n\n`; if (tooManyLabels && tooManyChanges) { - message += `This PR is too large with ${nonTestChanges} line changes (excluding tests) and affects ${finalLabels.length} different components/areas.`; + message += `This PR is too large with ${nonTestChanges} line changes (excluding tests) and affects ${originalLabelCount} different components/areas.`; } else if (tooManyLabels) { - message += `This PR affects ${finalLabels.length} different components/areas.`; + message += `This PR affects ${originalLabelCount} different components/areas.`; } else { message += `This PR is too large with ${nonTestChanges} line changes (excluding tests).`; } @@ -466,8 +466,8 @@ jobs: } // Handle reviews - async function handleReviews(finalLabels) { - const reviewMessages = generateReviewMessages(finalLabels); + async function handleReviews(finalLabels, originalLabelCount) { + const reviewMessages = generateReviewMessages(finalLabels, originalLabelCount); const hasReviewableLabels = finalLabels.some(label => ['too-big', 'needs-codeowners'].includes(label) ); @@ -627,6 +627,7 @@ jobs: // Handle too many labels (only for non-mega PRs) const tooManyLabels = finalLabels.length > MAX_LABELS; + const originalLabelCount = finalLabels.length; if (tooManyLabels && !isMegaPR && !finalLabels.includes('too-big')) { finalLabels = ['too-big']; @@ -635,7 +636,7 @@ jobs: console.log('Computed labels:', finalLabels.join(', ')); // Handle reviews - await handleReviews(finalLabels); + await handleReviews(finalLabels, originalLabelCount); // Apply labels if (finalLabels.length > 0) { From 99f48ae51c79d0159188d679f5b8659be488c7af Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 29 Oct 2025 14:29:40 +1300 Subject: [PATCH 293/526] [logger] Improve level validation errors (#11589) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/logger/__init__.py | 35 +++++++++++++++++++++------ 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 22bf3d2f4c..cf78e6ae63 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -173,14 +173,34 @@ def uart_selection(value): raise NotImplementedError -def validate_local_no_higher_than_global(value): - global_level = LOG_LEVEL_SEVERITY.index(value[CONF_LEVEL]) - for tag, level in value.get(CONF_LOGS, {}).items(): - if LOG_LEVEL_SEVERITY.index(level) > global_level: - raise cv.Invalid( - f"The configured log level for {tag} ({level}) must be no more severe than the global log level {value[CONF_LEVEL]}." +def validate_local_no_higher_than_global(config): + global_level = config[CONF_LEVEL] + global_level_index = LOG_LEVEL_SEVERITY.index(global_level) + errs = [] + for tag, level in config.get(CONF_LOGS, {}).items(): + if LOG_LEVEL_SEVERITY.index(level) > global_level_index: + errs.append( + cv.Invalid( + f"The configured log level for {tag} ({level}) must not be less severe than the global log level ({global_level})", + [CONF_LOGS, tag], + ) ) - return value + if errs: + raise cv.MultipleInvalid(errs) + return config + + +def validate_initial_no_higher_than_global(config): + if initial_level := config.get(CONF_INITIAL_LEVEL): + global_level = config[CONF_LEVEL] + if LOG_LEVEL_SEVERITY.index(initial_level) > LOG_LEVEL_SEVERITY.index( + global_level + ): + raise cv.Invalid( + f"The initial log level ({initial_level}) must not be less severe than the global log level ({global_level})", + [CONF_INITIAL_LEVEL], + ) + return config Logger = logger_ns.class_("Logger", cg.Component) @@ -263,6 +283,7 @@ CONFIG_SCHEMA = cv.All( } ).extend(cv.COMPONENT_SCHEMA), validate_local_no_higher_than_global, + validate_initial_no_higher_than_global, ) From 0d805355f5bc9753dc7cfe3e9bcd844424aeb746 Mon Sep 17 00:00:00 2001 From: Anton Sergunov Date: Wed, 29 Oct 2025 07:33:16 +0600 Subject: [PATCH 294/526] Fix the LiberTiny bug with UART pin setup (#11518) --- .../uart/uart_component_libretiny.cpp | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/esphome/components/uart/uart_component_libretiny.cpp b/esphome/components/uart/uart_component_libretiny.cpp index 9c065fe5df..8d1d28fce4 100644 --- a/esphome/components/uart/uart_component_libretiny.cpp +++ b/esphome/components/uart/uart_component_libretiny.cpp @@ -46,40 +46,58 @@ uint16_t LibreTinyUARTComponent::get_config() { } void LibreTinyUARTComponent::setup() { - if (this->rx_pin_) { - this->rx_pin_->setup(); - } - if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) { - this->tx_pin_->setup(); - } - int8_t tx_pin = tx_pin_ == nullptr ? -1 : tx_pin_->get_pin(); int8_t rx_pin = rx_pin_ == nullptr ? -1 : rx_pin_->get_pin(); bool tx_inverted = tx_pin_ != nullptr && tx_pin_->is_inverted(); bool rx_inverted = rx_pin_ != nullptr && rx_pin_->is_inverted(); + auto shouldFallbackToSoftwareSerial = [&]() -> bool { + auto hasFlags = [](InternalGPIOPin *pin, const gpio::Flags mask) -> bool { + return pin && pin->get_flags() & mask != gpio::Flags::FLAG_NONE; + }; + if (hasFlags(this->tx_pin_, gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN) || + hasFlags(this->rx_pin_, gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN)) { +#if LT_ARD_HAS_SOFTSERIAL + ESP_LOGI(TAG, "Pins has flags set. Using Software Serial"); + return true; +#else + ESP_LOGW(TAG, "Pin flags are set but not supported for hardware serial. Ignoring"); +#endif + } + return false; + }; + if (false) return; #if LT_HW_UART0 - else if ((tx_pin == -1 || tx_pin == PIN_SERIAL0_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL0_RX)) { + else if ((tx_pin == -1 || tx_pin == PIN_SERIAL0_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL0_RX) && + !shouldFallbackToSoftwareSerial()) { this->serial_ = &Serial0; this->hardware_idx_ = 0; } #endif #if LT_HW_UART1 - else if ((tx_pin == -1 || tx_pin == PIN_SERIAL1_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL1_RX)) { + else if ((tx_pin == -1 || tx_pin == PIN_SERIAL1_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL1_RX) && + !shouldFallbackToSoftwareSerial()) { this->serial_ = &Serial1; this->hardware_idx_ = 1; } #endif #if LT_HW_UART2 - else if ((tx_pin == -1 || tx_pin == PIN_SERIAL2_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL2_RX)) { + else if ((tx_pin == -1 || tx_pin == PIN_SERIAL2_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL2_RX) && + !shouldFallbackToSoftwareSerial()) { this->serial_ = &Serial2; this->hardware_idx_ = 2; } #endif else { #if LT_ARD_HAS_SOFTSERIAL + if (this->rx_pin_) { + this->rx_pin_->setup(); + } + if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) { + this->tx_pin_->setup(); + } this->serial_ = new SoftwareSerial(rx_pin, tx_pin, rx_inverted || tx_inverted); #else this->serial_ = &Serial; From 5528c3c765f434b061df0a08269886de6e8ba2d6 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:37:14 +1000 Subject: [PATCH 295/526] [mipi_rgb] Fix rotation with custom model (#11585) --- esphome/components/mipi/__init__.py | 12 ++++++++ esphome/components/mipi_rgb/display.py | 38 ++++++++++++++------------ esphome/components/mipi_spi/display.py | 15 +--------- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/esphome/components/mipi/__init__.py b/esphome/components/mipi/__init__.py index 4dff1af62a..93d1750cd6 100644 --- a/esphome/components/mipi/__init__.py +++ b/esphome/components/mipi/__init__.py @@ -384,6 +384,18 @@ class DriverChip: transform[CONF_TRANSFORM] = True return transform + def swap_xy_schema(self): + uses_swap = self.get_default(CONF_SWAP_XY, None) != cv.UNDEFINED + + def validator(value): + if value: + raise cv.Invalid("Axis swapping not supported by this model") + return cv.boolean(value) + + if uses_swap: + return {cv.Required(CONF_SWAP_XY): cv.boolean} + return {cv.Optional(CONF_SWAP_XY, default=False): validator} + def add_madctl(self, sequence: list, config: dict): # Add the MADCTL command to the sequence based on the configuration. use_flip = config.get(CONF_USE_AXIS_FLIPS) diff --git a/esphome/components/mipi_rgb/display.py b/esphome/components/mipi_rgb/display.py index 3001d33980..9d6b1fa729 100644 --- a/esphome/components/mipi_rgb/display.py +++ b/esphome/components/mipi_rgb/display.py @@ -46,6 +46,7 @@ from esphome.const import ( CONF_DATA_RATE, CONF_DC_PIN, CONF_DIMENSIONS, + CONF_DISABLED, CONF_ENABLE_PIN, CONF_GREEN, CONF_HSYNC_PIN, @@ -117,16 +118,16 @@ def data_pin_set(length): def model_schema(config): model = MODELS[config[CONF_MODEL].upper()] - if transforms := model.transforms: - transform = cv.Schema({cv.Required(x): cv.boolean for x in transforms}) - for x in (CONF_SWAP_XY, CONF_MIRROR_X, CONF_MIRROR_Y): - if x not in transforms: - transform = transform.extend( - {cv.Optional(x): cv.invalid(f"{x} not supported by this model")} - ) - else: - transform = cv.invalid("This model does not support transforms") - + transform = cv.Any( + cv.Schema( + { + cv.Required(CONF_MIRROR_X): cv.boolean, + cv.Required(CONF_MIRROR_Y): cv.boolean, + **model.swap_xy_schema(), + } + ), + cv.one_of(CONF_DISABLED, lower=True), + ) # RPI model does not use an init sequence, indicates with empty list if model.initsequence is None: # Custom model requires an init sequence @@ -135,12 +136,16 @@ def model_schema(config): else: iseqconf = cv.Optional(CONF_INIT_SEQUENCE) uses_spi = CONF_INIT_SEQUENCE in config or len(model.initsequence) != 0 - swap_xy = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY, False) - - # Dimensions are optional if the model has a default width and the swap_xy transform is not overridden - cv_dimensions = ( - cv.Optional if model.get_default(CONF_WIDTH) and not swap_xy else cv.Required + # Dimensions are optional if the model has a default width and the x-y transform is not overridden + transform_config = config.get(CONF_TRANSFORM, {}) + is_swapped = ( + isinstance(transform_config, dict) + and transform_config.get(CONF_SWAP_XY, False) is True ) + cv_dimensions = ( + cv.Optional if model.get_default(CONF_WIDTH) and not is_swapped else cv.Required + ) + pixel_modes = (PIXEL_MODE_16BIT, PIXEL_MODE_18BIT, "16", "18") schema = display.FULL_DISPLAY_SCHEMA.extend( { @@ -157,7 +162,7 @@ def model_schema(config): model.option(CONF_PIXEL_MODE, PIXEL_MODE_16BIT): cv.one_of( *pixel_modes, lower=True ), - model.option(CONF_TRANSFORM, cv.UNDEFINED): transform, + cv.Optional(CONF_TRANSFORM): transform, cv.Required(CONF_MODEL): cv.one_of(model.name, upper=True), model.option(CONF_INVERT_COLORS, False): cv.boolean, model.option(CONF_USE_AXIS_FLIPS, True): cv.boolean, @@ -270,7 +275,6 @@ async def to_code(config): cg.add(var.set_vsync_front_porch(config[CONF_VSYNC_FRONT_PORCH])) cg.add(var.set_pclk_inverted(config[CONF_PCLK_INVERTED])) cg.add(var.set_pclk_frequency(config[CONF_PCLK_FREQUENCY])) - index = 0 dpins = [] if CONF_RED in config[CONF_DATA_PINS]: red_pins = config[CONF_DATA_PINS][CONF_RED] diff --git a/esphome/components/mipi_spi/display.py b/esphome/components/mipi_spi/display.py index 891c8b42ff..50ea826eab 100644 --- a/esphome/components/mipi_spi/display.py +++ b/esphome/components/mipi_spi/display.py @@ -131,19 +131,6 @@ def denominator(config): ) from StopIteration -def swap_xy_schema(model): - uses_swap = model.get_default(CONF_SWAP_XY, None) != cv.UNDEFINED - - def validator(value): - if value: - raise cv.Invalid("Axis swapping not supported by this model") - return cv.boolean(value) - - if uses_swap: - return {cv.Required(CONF_SWAP_XY): cv.boolean} - return {cv.Optional(CONF_SWAP_XY, default=False): validator} - - def model_schema(config): model = MODELS[config[CONF_MODEL]] bus_mode = config[CONF_BUS_MODE] @@ -152,7 +139,7 @@ def model_schema(config): { cv.Required(CONF_MIRROR_X): cv.boolean, cv.Required(CONF_MIRROR_Y): cv.boolean, - **swap_xy_schema(model), + **model.swap_xy_schema(), } ), cv.one_of(CONF_DISABLED, lower=True), From a609343cb665bbb2411204e795ace686b70168c8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 29 Oct 2025 15:06:30 +1300 Subject: [PATCH 296/526] [fan] Remove deprecated `set_speed` function (#11590) --- esphome/components/fan/fan.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/esphome/components/fan/fan.h b/esphome/components/fan/fan.h index b74187eb4a..3739de29a2 100644 --- a/esphome/components/fan/fan.h +++ b/esphome/components/fan/fan.h @@ -60,8 +60,6 @@ class FanCall { this->speed_ = speed; return *this; } - ESPDEPRECATED("set_speed() with string argument is deprecated, use integer argument instead.", "2021.9") - FanCall &set_speed(const char *legacy_speed); optional get_speed() const { return this->speed_; } FanCall &set_direction(FanDirection direction) { this->direction_ = direction; From f3634edc22a9c9e939c5bede697bad5aa89fef4d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 28 Oct 2025 21:28:16 -0500 Subject: [PATCH 297/526] [select] Store options in flash to reduce RAM usage (#11514) --- esphome/components/api/api.proto | 2 +- esphome/components/api/api_pb2.cpp | 8 +-- esphome/components/api/api_pb2.h | 2 +- esphome/components/api/api_pb2_dump.cpp | 6 +++ esphome/components/lvgl/lvgl_esphome.h | 2 +- esphome/components/lvgl/select/lvgl_select.h | 12 ++++- .../select/modbus_select.cpp | 12 +++-- esphome/components/select/select.cpp | 7 ++- esphome/components/select/select.h | 3 ++ esphome/components/select/select_traits.cpp | 11 +++- esphome/components/select/select_traits.h | 11 ++-- .../template/select/template_select.cpp | 9 ++-- .../components/tuya/select/tuya_select.cpp | 5 +- esphome/core/helpers.h | 10 ++++ script/api_protobuf/api_protobuf.py | 51 +++++++++++++++---- 15 files changed, 111 insertions(+), 40 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index fae0f2e75a..f50944ffa4 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1143,7 +1143,7 @@ message ListEntitiesSelectResponse { reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; - repeated string options = 6 [(container_pointer) = "std::vector"]; + repeated string options = 6 [(container_pointer_no_template) = "FixedVector"]; bool disabled_by_default = 7; EntityCategory entity_category = 8; uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"]; diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 37bcf5d8a0..3472707d3c 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -1475,8 +1475,8 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const { #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon_ref_); #endif - for (const auto &it : *this->options) { - buffer.encode_string(6, it, true); + for (const char *it : *this->options) { + buffer.encode_string(6, it, strlen(it), true); } buffer.encode_bool(7, this->disabled_by_default); buffer.encode_uint32(8, static_cast(this->entity_category)); @@ -1492,8 +1492,8 @@ void ListEntitiesSelectResponse::calculate_size(ProtoSize &size) const { size.add_length(1, this->icon_ref_.size()); #endif if (!this->options->empty()) { - for (const auto &it : *this->options) { - size.add_length_force(1, it.size()); + for (const char *it : *this->options) { + size.add_length_force(1, strlen(it)); } } size.add_bool(1, this->disabled_by_default); diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 3e9a10c1f7..aa5c031ac7 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1534,7 +1534,7 @@ class ListEntitiesSelectResponse final : public InfoResponseProtoMessage { #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_select_response"; } #endif - const std::vector *options{}; + const FixedVector *options{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(ProtoSize &size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP diff --git a/esphome/components/api/api_pb2_dump.cpp b/esphome/components/api/api_pb2_dump.cpp index e803125f53..d94ceaaa9c 100644 --- a/esphome/components/api/api_pb2_dump.cpp +++ b/esphome/components/api/api_pb2_dump.cpp @@ -88,6 +88,12 @@ static void dump_field(std::string &out, const char *field_name, StringRef value out.append("\n"); } +static void dump_field(std::string &out, const char *field_name, const char *value, int indent = 2) { + append_field_prefix(out, field_name, indent); + out.append("'").append(value).append("'"); + out.append("\n"); +} + template static void dump_field(std::string &out, const char *field_name, T value, int indent = 2) { append_field_prefix(out, field_name, indent); out.append(proto_enum_to_string(value)); diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 3ae67e8a0b..d3dc8fac5a 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -358,7 +358,7 @@ class LvSelectable : public LvCompound { virtual void set_selected_index(size_t index, lv_anim_enable_t anim) = 0; void set_selected_text(const std::string &text, lv_anim_enable_t anim); std::string get_selected_text(); - std::vector get_options() { return this->options_; } + const std::vector &get_options() { return this->options_; } void set_options(std::vector options); protected: diff --git a/esphome/components/lvgl/select/lvgl_select.h b/esphome/components/lvgl/select/lvgl_select.h index a0e60295a6..3b1fd67d68 100644 --- a/esphome/components/lvgl/select/lvgl_select.h +++ b/esphome/components/lvgl/select/lvgl_select.h @@ -53,7 +53,17 @@ class LVGLSelect : public select::Select, public Component { this->widget_->set_selected_text(value, this->anim_); this->publish(); } - void set_options_() { this->traits.set_options(this->widget_->get_options()); } + void set_options_() { + // Widget uses std::vector, SelectTraits uses FixedVector + // Convert by extracting c_str() pointers + const auto &opts = this->widget_->get_options(); + FixedVector opt_ptrs; + opt_ptrs.init(opts.size()); + for (size_t i = 0; i < opts.size(); i++) { + opt_ptrs[i] = opts[i].c_str(); + } + this->traits.set_options(opt_ptrs); + } LvSelectable *widget_; lv_anim_enable_t anim_; diff --git a/esphome/components/modbus_controller/select/modbus_select.cpp b/esphome/components/modbus_controller/select/modbus_select.cpp index 56b8c783ed..48bf2835f2 100644 --- a/esphome/components/modbus_controller/select/modbus_select.cpp +++ b/esphome/components/modbus_controller/select/modbus_select.cpp @@ -28,7 +28,7 @@ void ModbusSelect::parse_and_publish(const std::vector &data) { if (map_it != this->mapping_.cend()) { size_t idx = std::distance(this->mapping_.cbegin(), map_it); - new_state = this->traits.get_options()[idx]; + new_state = std::string(this->option_at(idx)); ESP_LOGV(TAG, "Found option %s for value %lld", new_state->c_str(), value); } else { ESP_LOGE(TAG, "No option found for mapping %lld", value); @@ -41,10 +41,12 @@ void ModbusSelect::parse_and_publish(const std::vector &data) { } void ModbusSelect::control(const std::string &value) { - auto options = this->traits.get_options(); - auto opt_it = std::find(options.cbegin(), options.cend(), value); - size_t idx = std::distance(options.cbegin(), opt_it); - optional mapval = this->mapping_[idx]; + auto idx = this->index_of(value); + if (!idx.has_value()) { + ESP_LOGW(TAG, "Invalid option '%s'", value.c_str()); + return; + } + optional mapval = this->mapping_[idx.value()]; ESP_LOGD(TAG, "Found value %lld for option '%s'", *mapval, value.c_str()); std::vector data; diff --git a/esphome/components/select/select.cpp b/esphome/components/select/select.cpp index 16e8288ca1..5e30be3c13 100644 --- a/esphome/components/select/select.cpp +++ b/esphome/components/select/select.cpp @@ -1,5 +1,6 @@ #include "select.h" #include "esphome/core/log.h" +#include namespace esphome { namespace select { @@ -35,7 +36,7 @@ size_t Select::size() const { optional Select::index_of(const std::string &option) const { const auto &options = traits.get_options(); for (size_t i = 0; i < options.size(); i++) { - if (options[i] == option) { + if (strcmp(options[i], option.c_str()) == 0) { return i; } } @@ -53,11 +54,13 @@ optional Select::active_index() const { optional Select::at(size_t index) const { if (this->has_index(index)) { const auto &options = traits.get_options(); - return options.at(index); + return std::string(options.at(index)); } else { return {}; } } +const char *Select::option_at(size_t index) const { return traits.get_options().at(index); } + } // namespace select } // namespace esphome diff --git a/esphome/components/select/select.h b/esphome/components/select/select.h index 902b8a78ce..eabb39898b 100644 --- a/esphome/components/select/select.h +++ b/esphome/components/select/select.h @@ -56,6 +56,9 @@ class Select : public EntityBase { /// Return the (optional) option value at the provided index offset. optional at(size_t index) const; + /// Return the option value at the provided index offset (as const char* from flash). + const char *option_at(size_t index) const; + void add_on_state_callback(std::function &&callback); protected: diff --git a/esphome/components/select/select_traits.cpp b/esphome/components/select/select_traits.cpp index a8cd4290c8..c6ded98ebf 100644 --- a/esphome/components/select/select_traits.cpp +++ b/esphome/components/select/select_traits.cpp @@ -3,9 +3,16 @@ namespace esphome { namespace select { -void SelectTraits::set_options(std::vector options) { this->options_ = std::move(options); } +void SelectTraits::set_options(const std::initializer_list &options) { this->options_ = options; } -const std::vector &SelectTraits::get_options() const { return this->options_; } +void SelectTraits::set_options(const FixedVector &options) { + this->options_.init(options.size()); + for (size_t i = 0; i < options.size(); i++) { + this->options_[i] = options[i]; + } +} + +const FixedVector &SelectTraits::get_options() const { return this->options_; } } // namespace select } // namespace esphome diff --git a/esphome/components/select/select_traits.h b/esphome/components/select/select_traits.h index 128066dd6b..ee59a030ad 100644 --- a/esphome/components/select/select_traits.h +++ b/esphome/components/select/select_traits.h @@ -1,18 +1,19 @@ #pragma once -#include -#include +#include "esphome/core/helpers.h" +#include namespace esphome { namespace select { class SelectTraits { public: - void set_options(std::vector options); - const std::vector &get_options() const; + void set_options(const std::initializer_list &options); + void set_options(const FixedVector &options); + const FixedVector &get_options() const; protected: - std::vector options_; + FixedVector options_; }; } // namespace select diff --git a/esphome/components/template/select/template_select.cpp b/esphome/components/template/select/template_select.cpp index 3765cf02bf..c7a1d8a344 100644 --- a/esphome/components/template/select/template_select.cpp +++ b/esphome/components/template/select/template_select.cpp @@ -16,12 +16,12 @@ void TemplateSelect::setup() { size_t restored_index; if (this->pref_.load(&restored_index) && this->has_index(restored_index)) { index = restored_index; - ESP_LOGD(TAG, "State from restore: %s", this->at(index).value().c_str()); + ESP_LOGD(TAG, "State from restore: %s", this->option_at(index)); } else { - ESP_LOGD(TAG, "State from initial (could not load or invalid stored index): %s", this->at(index).value().c_str()); + ESP_LOGD(TAG, "State from initial (could not load or invalid stored index): %s", this->option_at(index)); } } else { - ESP_LOGD(TAG, "State from initial: %s", this->at(index).value().c_str()); + ESP_LOGD(TAG, "State from initial: %s", this->option_at(index)); } this->publish_state(this->at(index).value()); @@ -64,8 +64,7 @@ void TemplateSelect::dump_config() { " Optimistic: %s\n" " Initial Option: %s\n" " Restore Value: %s", - YESNO(this->optimistic_), this->at(this->initial_option_index_).value().c_str(), - YESNO(this->restore_value_)); + YESNO(this->optimistic_), this->option_at(this->initial_option_index_), YESNO(this->restore_value_)); } } // namespace template_ diff --git a/esphome/components/tuya/select/tuya_select.cpp b/esphome/components/tuya/select/tuya_select.cpp index 91ddbc77ec..7c1cd09d06 100644 --- a/esphome/components/tuya/select/tuya_select.cpp +++ b/esphome/components/tuya/select/tuya_select.cpp @@ -10,7 +10,6 @@ void TuyaSelect::setup() { this->parent_->register_listener(this->select_id_, [this](const TuyaDatapoint &datapoint) { uint8_t enum_value = datapoint.value_enum; ESP_LOGV(TAG, "MCU reported select %u value %u", this->select_id_, enum_value); - auto options = this->traits.get_options(); auto mappings = this->mappings_; auto it = std::find(mappings.cbegin(), mappings.cend(), enum_value); if (it == mappings.end()) { @@ -49,9 +48,9 @@ void TuyaSelect::dump_config() { " Data type: %s\n" " Options are:", this->select_id_, this->is_int_ ? "int" : "enum"); - auto options = this->traits.get_options(); + const auto &options = this->traits.get_options(); for (size_t i = 0; i < this->mappings_.size(); i++) { - ESP_LOGCONFIG(TAG, " %i: %s", this->mappings_.at(i), options.at(i).c_str()); + ESP_LOGCONFIG(TAG, " %i: %s", this->mappings_.at(i), options.at(i)); } } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 9b0591c9c5..cf21ddc16d 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -304,6 +304,11 @@ template class FixedVector { return data_[size_ - 1]; } + /// Access first element (no bounds checking - matches std::vector behavior) + /// Caller must ensure vector is not empty (size() > 0) + T &front() { return data_[0]; } + const T &front() const { return data_[0]; } + /// Access last element (no bounds checking - matches std::vector behavior) /// Caller must ensure vector is not empty (size() > 0) T &back() { return data_[size_ - 1]; } @@ -317,6 +322,11 @@ template class FixedVector { T &operator[](size_t i) { return data_[i]; } const T &operator[](size_t i) const { return data_[i]; } + /// Access element with bounds checking (matches std::vector behavior) + /// Note: No exception thrown on out of bounds - caller must ensure index is valid + T &at(size_t i) { return data_[i]; } + const T &at(size_t i) const { return data_[i]; } + // Iterator support for range-based for loops T *begin() { return data_; } T *end() { return data_ + size_; } diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 2f83b0bd79..394e92b9a7 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -1162,7 +1162,11 @@ class SInt64Type(TypeInfo): def _generate_array_dump_content( - ti, field_name: str, name: str, is_bool: bool = False + ti, + field_name: str, + name: str, + is_bool: bool = False, + is_const_char_ptr: bool = False, ) -> str: """Generate dump content for array types (repeated or fixed array). @@ -1170,7 +1174,10 @@ def _generate_array_dump_content( """ o = f"for (const auto {'' if is_bool else '&'}it : {field_name}) {{\n" # Check if underlying type can use dump_field - if ti.can_use_dump_field(): + if is_const_char_ptr: + # Special case for const char* - use it directly + o += f' dump_field(out, "{name}", it, 4);\n' + elif ti.can_use_dump_field(): # For types that have dump_field overloads, use them with extra indent # std::vector iterators return proxy objects, need explicit cast value_expr = "static_cast(it)" if is_bool else ti.dump_field_value("it") @@ -1533,11 +1540,16 @@ class RepeatedTypeInfo(TypeInfo): def encode_content(self) -> str: if self._use_pointer: # For pointer fields, just dereference (pointer should never be null in our use case) - o = f"for (const auto &it : *this->{self.field_name}) {{\n" - if isinstance(self._ti, EnumType): - o += f" buffer.{self._ti.encode_func}({self.number}, static_cast(it), true);\n" + # Special handling for const char* elements (when container_no_template contains "const char") + if "const char" in self._container_no_template: + o = f"for (const char *it : *this->{self.field_name}) {{\n" + o += f" buffer.{self._ti.encode_func}({self.number}, it, strlen(it), true);\n" else: - o += f" buffer.{self._ti.encode_func}({self.number}, it, true);\n" + o = f"for (const auto &it : *this->{self.field_name}) {{\n" + if isinstance(self._ti, EnumType): + o += f" buffer.{self._ti.encode_func}({self.number}, static_cast(it), true);\n" + else: + o += f" buffer.{self._ti.encode_func}({self.number}, it, true);\n" o += "}" return o o = f"for (auto {'' if self._ti_is_bool else '&'}it : this->{self.field_name}) {{\n" @@ -1550,10 +1562,18 @@ class RepeatedTypeInfo(TypeInfo): @property def dump_content(self) -> str: + # Check if this is const char* elements + is_const_char_ptr = ( + self._use_pointer and "const char" in self._container_no_template + ) if self._use_pointer: # For pointer fields, dereference and use the existing helper return _generate_array_dump_content( - self._ti, f"*this->{self.field_name}", self.name, is_bool=False + self._ti, + f"*this->{self.field_name}", + self.name, + is_bool=False, + is_const_char_ptr=is_const_char_ptr, ) return _generate_array_dump_content( self._ti, f"this->{self.field_name}", self.name, is_bool=self._ti_is_bool @@ -1588,9 +1608,14 @@ class RepeatedTypeInfo(TypeInfo): o += f" size.add_precalculated_size({size_expr} * {bytes_per_element});\n" else: # Other types need the actual value - auto_ref = "" if self._ti_is_bool else "&" - o += f" for (const auto {auto_ref}it : {container_ref}) {{\n" - o += f" {self._ti.get_size_calculation('it', True)}\n" + # Special handling for const char* elements + if self._use_pointer and "const char" in self._container_no_template: + o += f" for (const char *it : {container_ref}) {{\n" + o += " size.add_length_force(1, strlen(it));\n" + else: + auto_ref = "" if self._ti_is_bool else "&" + o += f" for (const auto {auto_ref}it : {container_ref}) {{\n" + o += f" {self._ti.get_size_calculation('it', True)}\n" o += " }\n" o += "}" @@ -2542,6 +2567,12 @@ static void dump_field(std::string &out, const char *field_name, StringRef value out.append("\\n"); } +static void dump_field(std::string &out, const char *field_name, const char *value, int indent = 2) { + append_field_prefix(out, field_name, indent); + out.append("'").append(value).append("'"); + out.append("\\n"); +} + template static void dump_field(std::string &out, const char *field_name, T value, int indent = 2) { append_field_prefix(out, field_name, indent); From f6e4c0cb521392247b858ca81a37fd001a5219e3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 28 Oct 2025 22:22:28 -0500 Subject: [PATCH 298/526] [ci] Fix component tests not running when only test files change (#11580) --- script/helpers.py | 16 +++++++++------- tests/script/test_helpers.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/script/helpers.py b/script/helpers.py index 78c11b427e..447d54fa54 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -90,16 +90,18 @@ def get_component_from_path(file_path: str) -> str | None: """Extract component name from a file path. Args: - file_path: Path to a file (e.g., "esphome/components/wifi/wifi.cpp") + file_path: Path to a file (e.g., "esphome/components/wifi/wifi.cpp" + or "tests/components/uart/test.esp32-idf.yaml") Returns: - Component name if path is in components directory, None otherwise + Component name if path is in components or tests directory, None otherwise """ - if not file_path.startswith(ESPHOME_COMPONENTS_PATH): - return None - parts = file_path.split("/") - if len(parts) >= 3: - return parts[2] + if file_path.startswith(ESPHOME_COMPONENTS_PATH) or file_path.startswith( + ESPHOME_TESTS_COMPONENTS_PATH + ): + parts = file_path.split("/") + if len(parts) >= 3 and parts[2]: + return parts[2] return None diff --git a/tests/script/test_helpers.py b/tests/script/test_helpers.py index 63f1f0e600..1046512a14 100644 --- a/tests/script/test_helpers.py +++ b/tests/script/test_helpers.py @@ -1065,3 +1065,39 @@ def test_parse_list_components_output(output: str, expected: list[str]) -> None: """Test parse_list_components_output function.""" result = helpers.parse_list_components_output(output) assert result == expected + + +@pytest.mark.parametrize( + ("file_path", "expected_component"), + [ + # Component files + ("esphome/components/wifi/wifi.cpp", "wifi"), + ("esphome/components/uart/uart.h", "uart"), + ("esphome/components/api/api_server.cpp", "api"), + ("esphome/components/sensor/sensor.cpp", "sensor"), + # Test files + ("tests/components/uart/test.esp32-idf.yaml", "uart"), + ("tests/components/wifi/test.esp8266-ard.yaml", "wifi"), + ("tests/components/sensor/test.esp32-idf.yaml", "sensor"), + ("tests/components/api/test_api.cpp", "api"), + ("tests/components/uart/common.h", "uart"), + # Non-component files + ("esphome/core/component.cpp", None), + ("esphome/core/helpers.h", None), + ("tests/integration/test_api.py", None), + ("tests/unit_tests/test_helpers.py", None), + ("README.md", None), + ("script/helpers.py", None), + # Edge cases + ("esphome/components/", None), # No component name + ("tests/components/", None), # No component name + ("esphome/components", None), # No trailing slash + ("tests/components", None), # No trailing slash + ], +) +def test_get_component_from_path( + file_path: str, expected_component: str | None +) -> None: + """Test extraction of component names from file paths.""" + result = helpers.get_component_from_path(file_path) + assert result == expected_component From 71695567221ffe1bdd3575a2e8878e456ec70f23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 04:46:47 +0000 Subject: [PATCH 299/526] Bump aioesphomeapi from 42.4.0 to 42.5.0 (#11597) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b0d7d62c36..660b18c933 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==42.4.0 +aioesphomeapi==42.5.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.16 # dashboard_import From b6c9ece0e67230f34dcb60b03ea909baef0d7218 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 29 Oct 2025 13:10:36 +0800 Subject: [PATCH 300/526] template_alarm_control_panel readability improvements (#11593) --- .../template_alarm_control_panel.cpp | 75 +++++++++---------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp index d1562ee82f..f7e9872ce1 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp @@ -25,6 +25,20 @@ void TemplateAlarmControlPanel::add_sensor(binary_sensor::BinarySensor *sensor, this->sensor_data_.push_back(sd); this->sensor_map_[sensor].store_index = this->next_store_index_++; }; + +static const LogString *sensor_type_to_string(AlarmSensorType type) { + switch (type) { + case ALARM_SENSOR_TYPE_INSTANT: + return LOG_STR("instant"); + case ALARM_SENSOR_TYPE_DELAYED_FOLLOWER: + return LOG_STR("delayed_follower"); + case ALARM_SENSOR_TYPE_INSTANT_ALWAYS: + return LOG_STR("instant_always"); + case ALARM_SENSOR_TYPE_DELAYED: + default: + return LOG_STR("delayed"); + } +} #endif void TemplateAlarmControlPanel::dump_config() { @@ -46,35 +60,20 @@ void TemplateAlarmControlPanel::dump_config() { " Supported Features: %" PRIu32, (this->pending_time_ / 1000), (this->trigger_time_ / 1000), this->get_supported_features()); #ifdef USE_BINARY_SENSOR - for (auto sensor_info : this->sensor_map_) { - ESP_LOGCONFIG(TAG, " Binary Sensor:"); + for (auto const &[sensor, info] : this->sensor_map_) { ESP_LOGCONFIG(TAG, + " Binary Sensor:\n" " Name: %s\n" + " Type: %s\n" " Armed home bypass: %s\n" " Armed night bypass: %s\n" " Auto bypass: %s\n" " Chime mode: %s", - sensor_info.first->get_name().c_str(), - TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME), - TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT), - TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO), - TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)); - const char *sensor_type; - switch (sensor_info.second.type) { - case ALARM_SENSOR_TYPE_INSTANT: - sensor_type = "instant"; - break; - case ALARM_SENSOR_TYPE_DELAYED_FOLLOWER: - sensor_type = "delayed_follower"; - break; - case ALARM_SENSOR_TYPE_INSTANT_ALWAYS: - sensor_type = "instant_always"; - break; - case ALARM_SENSOR_TYPE_DELAYED: - default: - sensor_type = "delayed"; - } - ESP_LOGCONFIG(TAG, " Sensor type: %s", sensor_type); + sensor->get_name().c_str(), LOG_STR_ARG(sensor_type_to_string(info.type)), + TRUEFALSE(info.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME), + TRUEFALSE(info.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT), + TRUEFALSE(info.flags & BINARY_SENSOR_MODE_BYPASS_AUTO), + TRUEFALSE(info.flags & BINARY_SENSOR_MODE_CHIME)); } #endif } @@ -123,39 +122,37 @@ void TemplateAlarmControlPanel::loop() { bool instant_sensor_faulted = false; #ifdef USE_BINARY_SENSOR - // Test all of the sensors in the list regardless of the alarm panel state - for (auto sensor_info : this->sensor_map_) { + // Test all of the sensors regardless of the alarm panel state + for (auto const &[sensor, info] : this->sensor_map_) { // Check for chime zones - if ((sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)) { + if (info.flags & BINARY_SENSOR_MODE_CHIME) { // Look for the transition from closed to open - if ((!this->sensor_data_[sensor_info.second.store_index].last_chime_state) && (sensor_info.first->state)) { + if ((!this->sensor_data_[info.store_index].last_chime_state) && (sensor->state)) { // Must be disarmed to chime if (this->current_state_ == ACP_STATE_DISARMED) { this->chime_callback_.call(); } } // Record the sensor state change - this->sensor_data_[sensor_info.second.store_index].last_chime_state = sensor_info.first->state; + this->sensor_data_[info.store_index].last_chime_state = sensor->state; } // Check for faulted sensors - if (sensor_info.first->state) { // Sensor triggered? + if (sensor->state) { // Skip if auto bypassed if (std::count(this->bypassed_sensor_indicies_.begin(), this->bypassed_sensor_indicies_.end(), - sensor_info.second.store_index) == 1) { + info.store_index) == 1) { continue; } // Skip if bypass armed home - if (this->current_state_ == ACP_STATE_ARMED_HOME && - (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)) { + if ((this->current_state_ == ACP_STATE_ARMED_HOME) && (info.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)) { continue; } // Skip if bypass armed night - if (this->current_state_ == ACP_STATE_ARMED_NIGHT && - (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)) { + if ((this->current_state_ == ACP_STATE_ARMED_NIGHT) && (info.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)) { continue; } - switch (sensor_info.second.type) { + switch (info.type) { case ALARM_SENSOR_TYPE_INSTANT_ALWAYS: next_state = ACP_STATE_TRIGGERED; [[fallthrough]]; @@ -247,11 +244,11 @@ void TemplateAlarmControlPanel::arm_(optional code, alarm_control_p void TemplateAlarmControlPanel::bypass_before_arming() { #ifdef USE_BINARY_SENSOR - for (auto sensor_info : this->sensor_map_) { + for (auto const &[sensor, info] : this->sensor_map_) { // Check for faulted bypass_auto sensors and remove them from monitoring - if ((sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor_info.first->state)) { - ESP_LOGW(TAG, "'%s' is faulted and will be automatically bypassed", sensor_info.first->get_name().c_str()); - this->bypassed_sensor_indicies_.push_back(sensor_info.second.store_index); + if ((info.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor->state)) { + ESP_LOGW(TAG, "'%s' is faulted and will be automatically bypassed", sensor->get_name().c_str()); + this->bypassed_sensor_indicies_.push_back(info.store_index); } } #endif From 09d89000ad3787e1419c662d1b66d70918443074 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 29 Oct 2025 18:14:02 +1300 Subject: [PATCH 301/526] [core] Remove deprecated schema constants (#11591) --- .../alarm_control_panel/__init__.py | 6 ----- esphome/components/binary_sensor/__init__.py | 5 ---- esphome/components/button/__init__.py | 5 ---- esphome/components/climate/__init__.py | 5 ---- esphome/components/climate_ir/__init__.py | 23 +------------------ esphome/components/cover/__init__.py | 5 ---- esphome/components/event/__init__.py | 5 ---- esphome/components/fan/__init__.py | 4 ---- esphome/components/lock/__init__.py | 5 ---- esphome/components/media_player/__init__.py | 4 ---- esphome/components/number/__init__.py | 5 ---- esphome/components/select/__init__.py | 5 ---- esphome/components/sensor/__init__.py | 5 ---- esphome/components/switch/__init__.py | 5 ---- esphome/components/text/__init__.py | 5 ---- esphome/components/text_sensor/__init__.py | 5 ---- esphome/components/update/__init__.py | 5 ---- esphome/components/valve/__init__.py | 5 ---- esphome/config_validation.py | 23 ------------------- script/build_language_schema.py | 2 +- 20 files changed, 2 insertions(+), 130 deletions(-) diff --git a/esphome/components/alarm_control_panel/__init__.py b/esphome/components/alarm_control_panel/__init__.py index 174a9d9e0a..b1e2252ce7 100644 --- a/esphome/components/alarm_control_panel/__init__.py +++ b/esphome/components/alarm_control_panel/__init__.py @@ -172,12 +172,6 @@ def alarm_control_panel_schema( return _ALARM_CONTROL_PANEL_SCHEMA.extend(schema) -# Remove before 2025.11.0 -ALARM_CONTROL_PANEL_SCHEMA = alarm_control_panel_schema(AlarmControlPanel) -ALARM_CONTROL_PANEL_SCHEMA.add_extra( - cv.deprecated_schema_constant("alarm_control_panel") -) - ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id( { cv.GenerateID(): cv.use_id(AlarmControlPanel), diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 8892b57e6e..cbf935a501 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -548,11 +548,6 @@ def binary_sensor_schema( return _BINARY_SENSOR_SCHEMA.extend(schema) -# Remove before 2025.11.0 -BINARY_SENSOR_SCHEMA = binary_sensor_schema() -BINARY_SENSOR_SCHEMA.add_extra(cv.deprecated_schema_constant("binary_sensor")) - - async def setup_binary_sensor_core_(var, config): await setup_entity(var, config, "binary_sensor") diff --git a/esphome/components/button/__init__.py b/esphome/components/button/__init__.py index e1ac875cb0..d2f143b97e 100644 --- a/esphome/components/button/__init__.py +++ b/esphome/components/button/__init__.py @@ -84,11 +84,6 @@ def button_schema( return _BUTTON_SCHEMA.extend(schema) -# Remove before 2025.11.0 -BUTTON_SCHEMA = button_schema(Button) -BUTTON_SCHEMA.add_extra(cv.deprecated_schema_constant("button")) - - async def setup_button_core_(var, config): await setup_entity(var, config, "button") diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index c0c33d7242..5824e68141 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -270,11 +270,6 @@ def climate_schema( return _CLIMATE_SCHEMA.extend(schema) -# Remove before 2025.11.0 -CLIMATE_SCHEMA = climate_schema(Climate) -CLIMATE_SCHEMA.add_extra(cv.deprecated_schema_constant("climate")) - - async def setup_climate_core_(var, config): await setup_entity(var, config, "climate") diff --git a/esphome/components/climate_ir/__init__.py b/esphome/components/climate_ir/__init__.py index 312b2ad900..6d66abf4cd 100644 --- a/esphome/components/climate_ir/__init__.py +++ b/esphome/components/climate_ir/__init__.py @@ -1,10 +1,9 @@ import logging -from esphome import core import esphome.codegen as cg from esphome.components import climate, remote_base, sensor import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT +from esphome.const import CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT from esphome.cpp_generator import MockObjClass _LOGGER = logging.getLogger(__name__) @@ -52,26 +51,6 @@ def climate_ir_with_receiver_schema( ) -# Remove before 2025.11.0 -def deprecated_schema_constant(config): - type: str = "unknown" - if (id := config.get(CONF_ID)) is not None and isinstance(id, core.ID): - type = str(id.type).split("::", maxsplit=1)[0] - _LOGGER.warning( - "Using `climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA` is deprecated and will be removed in ESPHome 2025.11.0. " - "Please use `climate_ir.climate_ir_with_receiver_schema(...)` instead. " - "If you are seeing this, report an issue to the external_component author and ask them to update it. " - "https://developers.esphome.io/blog/2025/05/14/_schema-deprecations/. " - "Component using this schema: %s", - type, - ) - return config - - -CLIMATE_IR_WITH_RECEIVER_SCHEMA = climate_ir_with_receiver_schema(ClimateIR) -CLIMATE_IR_WITH_RECEIVER_SCHEMA.add_extra(deprecated_schema_constant) - - async def register_climate_ir(var, config): await cg.register_component(var, config) await remote_base.register_transmittable(var, config) diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index bec6dcbdac..383daee083 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -151,11 +151,6 @@ def cover_schema( return _COVER_SCHEMA.extend(schema) -# Remove before 2025.11.0 -COVER_SCHEMA = cover_schema(Cover) -COVER_SCHEMA.add_extra(cv.deprecated_schema_constant("cover")) - - async def setup_cover_core_(var, config): await setup_entity(var, config, "cover") diff --git a/esphome/components/event/__init__.py b/esphome/components/event/__init__.py index 449cc48625..e2b69ba872 100644 --- a/esphome/components/event/__init__.py +++ b/esphome/components/event/__init__.py @@ -85,11 +85,6 @@ def event_schema( return _EVENT_SCHEMA.extend(schema) -# Remove before 2025.11.0 -EVENT_SCHEMA = event_schema() -EVENT_SCHEMA.add_extra(cv.deprecated_schema_constant("event")) - - async def setup_event_core_(var, config, *, event_types: list[str]): await setup_entity(var, config, "event") diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 245c9f04b4..35a351e8f1 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -189,10 +189,6 @@ def fan_schema( return _FAN_SCHEMA.extend(schema) -# Remove before 2025.11.0 -FAN_SCHEMA = fan_schema(Fan) -FAN_SCHEMA.add_extra(cv.deprecated_schema_constant("fan")) - _PRESET_MODES_SCHEMA = cv.All( cv.ensure_list(cv.string_strict), cv.Length(min=1), diff --git a/esphome/components/lock/__init__.py b/esphome/components/lock/__init__.py index 04c1586ddd..9d893d3ad9 100644 --- a/esphome/components/lock/__init__.py +++ b/esphome/components/lock/__init__.py @@ -91,11 +91,6 @@ def lock_schema( return _LOCK_SCHEMA.extend(schema) -# Remove before 2025.11.0 -LOCK_SCHEMA = lock_schema() -LOCK_SCHEMA.add_extra(cv.deprecated_schema_constant("lock")) - - async def _setup_lock_core(var, config): await setup_entity(var, config, "lock") diff --git a/esphome/components/media_player/__init__.py b/esphome/components/media_player/__init__.py index 70c7cf7a56..c6ffe50d79 100644 --- a/esphome/components/media_player/__init__.py +++ b/esphome/components/media_player/__init__.py @@ -192,10 +192,6 @@ def media_player_schema( return _MEDIA_PLAYER_SCHEMA.extend(schema) -# Remove before 2025.11.0 -MEDIA_PLAYER_SCHEMA = media_player_schema(MediaPlayer) -MEDIA_PLAYER_SCHEMA.add_extra(cv.deprecated_schema_constant("media_player")) - MEDIA_PLAYER_ACTION_SCHEMA = automation.maybe_simple_id( cv.Schema( { diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index ac0329fcc6..368b431d7b 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -238,11 +238,6 @@ def number_schema( return _NUMBER_SCHEMA.extend(schema) -# Remove before 2025.11.0 -NUMBER_SCHEMA = number_schema(Number) -NUMBER_SCHEMA.add_extra(cv.deprecated_schema_constant("number")) - - async def setup_number_core_( var, config, *, min_value: float, max_value: float, step: float ): diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index c7146df9fb..7c50fe02c0 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -86,11 +86,6 @@ def select_schema( return _SELECT_SCHEMA.extend(schema) -# Remove before 2025.11.0 -SELECT_SCHEMA = select_schema(Select) -SELECT_SCHEMA.add_extra(cv.deprecated_schema_constant("select")) - - async def setup_select_core_(var, config, *, options: list[str]): await setup_entity(var, config, "select") diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 41ac3516b9..e8fec222a1 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -369,11 +369,6 @@ def sensor_schema( return _SENSOR_SCHEMA.extend(schema) -# Remove before 2025.11.0 -SENSOR_SCHEMA = sensor_schema() -SENSOR_SCHEMA.add_extra(cv.deprecated_schema_constant("sensor")) - - @FILTER_REGISTRY.register("offset", OffsetFilter, cv.templatable(cv.float_)) async def offset_filter_to_code(config, filter_id): template_ = await cg.templatable(config, [], float) diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index 0e7b35b373..e9473012cf 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -139,11 +139,6 @@ def switch_schema( return _SWITCH_SCHEMA.extend(schema) -# Remove before 2025.11.0 -SWITCH_SCHEMA = switch_schema(Switch) -SWITCH_SCHEMA.add_extra(cv.deprecated_schema_constant("switch")) - - async def setup_switch_core_(var, config): await setup_entity(var, config, "switch") diff --git a/esphome/components/text/__init__.py b/esphome/components/text/__init__.py index 1baacc239f..9ceea0dfdf 100644 --- a/esphome/components/text/__init__.py +++ b/esphome/components/text/__init__.py @@ -84,11 +84,6 @@ def text_schema( return _TEXT_SCHEMA.extend(schema) -# Remove before 2025.11.0 -TEXT_SCHEMA = text_schema() -TEXT_SCHEMA.add_extra(cv.deprecated_schema_constant("text")) - - async def setup_text_core_( var, config, diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index adc8a76fcd..0d22400a8e 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -193,11 +193,6 @@ def text_sensor_schema( return _TEXT_SENSOR_SCHEMA.extend(schema) -# Remove before 2025.11.0 -TEXT_SENSOR_SCHEMA = text_sensor_schema() -TEXT_SENSOR_SCHEMA.add_extra(cv.deprecated_schema_constant("text_sensor")) - - async def build_filters(config): return await cg.build_registry_list(FILTER_REGISTRY, config) diff --git a/esphome/components/update/__init__.py b/esphome/components/update/__init__.py index 35fc4eaf1d..7a381c85a8 100644 --- a/esphome/components/update/__init__.py +++ b/esphome/components/update/__init__.py @@ -84,11 +84,6 @@ def update_schema( return _UPDATE_SCHEMA.extend(schema) -# Remove before 2025.11.0 -UPDATE_SCHEMA = update_schema() -UPDATE_SCHEMA.add_extra(cv.deprecated_schema_constant("update")) - - async def setup_update_core_(var, config): await setup_entity(var, config, "update") diff --git a/esphome/components/valve/__init__.py b/esphome/components/valve/__init__.py index 6f31fc3a20..73e907eb0f 100644 --- a/esphome/components/valve/__init__.py +++ b/esphome/components/valve/__init__.py @@ -129,11 +129,6 @@ def valve_schema( return _VALVE_SCHEMA.extend(schema) -# Remove before 2025.11.0 -VALVE_SCHEMA = valve_schema() -VALVE_SCHEMA.add_extra(cv.deprecated_schema_constant("valve")) - - async def _setup_valve_core(var, config): await setup_entity(var, config, "valve") diff --git a/esphome/config_validation.py b/esphome/config_validation.py index c613a984c4..359b257992 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -2195,26 +2195,3 @@ def rename_key(old_key, new_key): return config return validator - - -# Remove before 2025.11.0 -def deprecated_schema_constant(entity_type: str): - def validator(config): - type: str = "unknown" - if (id := config.get(CONF_ID)) is not None and isinstance(id, core.ID): - type = str(id.type).split("::", maxsplit=1)[0] - _LOGGER.warning( - "Using `%s.%s_SCHEMA` is deprecated and will be removed in ESPHome 2025.11.0. " - "Please use `%s.%s_schema(...)` instead. " - "If you are seeing this, report an issue to the external_component author and ask them to update it. " - "https://developers.esphome.io/blog/2025/05/14/_schema-deprecations/. " - "Component using this schema: %s", - entity_type, - entity_type.upper(), - entity_type, - entity_type, - type, - ) - return config - - return validator diff --git a/script/build_language_schema.py b/script/build_language_schema.py index 1ffe3c2873..c9501cb193 100755 --- a/script/build_language_schema.py +++ b/script/build_language_schema.py @@ -300,7 +300,7 @@ def fix_remote_receiver(): remote_receiver_schema["CONFIG_SCHEMA"] = { "type": "schema", "schema": { - "extends": ["binary_sensor.BINARY_SENSOR_SCHEMA", "core.COMPONENT_SCHEMA"], + "extends": ["binary_sensor._BINARY_SENSOR_SCHEMA", "core.COMPONENT_SCHEMA"], "config_vars": output["remote_base"].pop("binary"), }, } From 59a216bfcbacaffb9cdd10194b657d76b4c8bde0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 00:19:47 -0500 Subject: [PATCH 302/526] Bump github/codeql-action from 4.30.9 to 4.31.0 (#11522) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c24900d378..6b940eed8a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 + uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -86,6 +86,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 + uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 with: category: "/language:${{matrix.language}}" From 33e7a2101bbbcc39111b34740a1563f23fb97bc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 00:20:05 -0500 Subject: [PATCH 303/526] Bump actions/upload-artifact from 4.6.2 to 5.0.0 (#11520) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-api-proto.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-api-proto.yml b/.github/workflows/ci-api-proto.yml index c122859442..400373679f 100644 --- a/.github/workflows/ci-api-proto.yml +++ b/.github/workflows/ci-api-proto.yml @@ -62,7 +62,7 @@ jobs: run: git diff - if: failure() name: Archive artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: generated-proto-files path: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 655e28e3b3..63a7aaa0bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -849,7 +849,7 @@ jobs: fi - name: Upload memory analysis JSON - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: memory-analysis-target path: memory-analysis-target.json @@ -913,7 +913,7 @@ jobs: --platform "$platform" - name: Upload memory analysis JSON - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: memory-analysis-pr path: memory-analysis-pr.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2b3b3bdc1b..92949d72e1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -138,7 +138,7 @@ jobs: # version: ${{ needs.init.outputs.tag }} - name: Upload digests - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: digests-${{ matrix.platform.arch }} path: /tmp/digests From 7549ca4d39d0c735b72f898cbbd670ca3b681307 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 00:20:13 -0500 Subject: [PATCH 304/526] Bump actions/download-artifact from 5.0.0 to 6.0.0 (#11521) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63a7aaa0bf..bd45adb78b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -943,13 +943,13 @@ jobs: python-version: ${{ env.DEFAULT_PYTHON }} cache-key: ${{ needs.common.outputs.cache-key }} - name: Download target analysis JSON - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: memory-analysis-target path: ./memory-analysis continue-on-error: true - name: Download PR analysis JSON - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: memory-analysis-pr path: ./memory-analysis diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 92949d72e1..75d88abf29 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -171,7 +171,7 @@ jobs: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Download digests - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: pattern: digests-* path: /tmp/digests From f29021b5efd43e34c85fe4cd905ebea7d669923b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 05:23:42 +0000 Subject: [PATCH 305/526] Bump ruff from 0.14.1 to 0.14.2 (#11519) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston --- .pre-commit-config.yaml | 2 +- requirements_test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e0e71d388..f7e4a688e0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ ci: repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.14.1 + rev: v0.14.2 hooks: # Run the linter. - id: ruff diff --git a/requirements_test.txt b/requirements_test.txt index 5f94329e3f..a11992b0fd 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ pylint==4.0.2 flake8==7.3.0 # also change in .pre-commit-config.yaml when updating -ruff==0.14.1 # also change in .pre-commit-config.yaml when updating +ruff==0.14.2 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating pre-commit From 66cf7c3a3b30a0441cb2c660bcb19bbb2c2a9943 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 29 Oct 2025 01:07:48 -0500 Subject: [PATCH 306/526] [lvgl] Fix nested lambdas in automations unable to access parameters (#11583) Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/lvgl/defines.py | 22 +++++++++++++-- esphome/components/lvgl/lv_validation.py | 21 ++++++++++++--- esphome/components/lvgl/lvcode.py | 13 ++++++--- esphome/components/lvgl/sensor/__init__.py | 3 +-- tests/components/lvgl/common.yaml | 31 ++++++++++++++++++++++ tests/components/lvgl/lvgl-package.yaml | 23 ++++++++++++++++ 6 files changed, 103 insertions(+), 10 deletions(-) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 6464824c64..7fbb6de071 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -5,6 +5,7 @@ Constants already defined in esphome.const are not duplicated here and must be i """ import logging +from typing import TYPE_CHECKING, Any from esphome import codegen as cg, config_validation as cv from esphome.const import CONF_ITEMS @@ -12,6 +13,7 @@ from esphome.core import ID, Lambda from esphome.cpp_generator import LambdaExpression, MockObj from esphome.cpp_types import uint32 from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor +from esphome.types import Expression, SafeExpType from .helpers import requires_component @@ -42,7 +44,13 @@ def static_cast(type, value): def call_lambda(lamb: LambdaExpression): expr = lamb.content.strip() if expr.startswith("return") and expr.endswith(";"): - return expr[6:][:-1].strip() + return expr[6:-1].strip() + # If lambda has parameters, call it with those parameter names + # Parameter names come from hardcoded component code (like "x", "it", "event") + # not from user input, so they're safe to use directly + if lamb.parameters and lamb.parameters.parameters: + param_names = ", ".join(str(param.id) for param in lamb.parameters.parameters) + return f"{lamb}({param_names})" return f"{lamb}()" @@ -65,10 +73,20 @@ class LValidator: return cv.returning_lambda(value) return self.validator(value) - async def process(self, value, args=()): + async def process( + self, value: Any, args: list[tuple[SafeExpType, str]] | None = None + ) -> Expression: if value is None: return None if isinstance(value, Lambda): + # Local import to avoid circular import + from .lvcode import CodeContext, LambdaContext + + if TYPE_CHECKING: + # CodeContext does not have get_automation_parameters + # so we need to assert the type here + assert isinstance(CodeContext.code_context, LambdaContext) + args = args or CodeContext.code_context.get_automation_parameters() return cg.RawExpression( call_lambda( await cg.process_lambda(value, args, return_type=self.rtype) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index d345ac70f3..6f95a32a18 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING, Any + import esphome.codegen as cg from esphome.components import image from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw @@ -17,6 +19,7 @@ from esphome.cpp_generator import MockObj from esphome.cpp_types import ESPTime, int32, uint32 from esphome.helpers import cpp_string_escape from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor +from esphome.types import Expression, SafeExpType from . import types as ty from .defines import ( @@ -388,11 +391,23 @@ class TextValidator(LValidator): return value return super().__call__(value) - async def process(self, value, args=()): + async def process( + self, value: Any, args: list[tuple[SafeExpType, str]] | None = None + ) -> Expression: + # Local import to avoid circular import at module level + + from .lvcode import CodeContext, LambdaContext + + if TYPE_CHECKING: + # CodeContext does not have get_automation_parameters + # so we need to assert the type here + assert isinstance(CodeContext.code_context, LambdaContext) + args = args or CodeContext.code_context.get_automation_parameters() + if isinstance(value, dict): if format_str := value.get(CONF_FORMAT): - args = [str(x) for x in value[CONF_ARGS]] - arg_expr = cg.RawExpression(",".join(args)) + str_args = [str(x) for x in value[CONF_ARGS]] + arg_expr = cg.RawExpression(",".join(str_args)) format_str = cpp_string_escape(format_str) return literal(f"str_sprintf({format_str}, {arg_expr}).c_str()") if time_format := value.get(CONF_TIME_FORMAT): diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index 7a5c35f896..ea38845c07 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -164,6 +164,9 @@ class LambdaContext(CodeContext): code_text.append(text) return code_text + def get_automation_parameters(self) -> list[tuple[SafeExpType, str]]: + return self.parameters + async def __aenter__(self): await super().__aenter__() add_line_marks(self.where) @@ -178,9 +181,8 @@ class LvContext(LambdaContext): added_lambda_count = 0 - def __init__(self, args=None): - self.args = args or LVGL_COMP_ARG - super().__init__(parameters=self.args) + def __init__(self): + super().__init__(parameters=LVGL_COMP_ARG) async def __aexit__(self, exc_type, exc_val, exc_tb): await super().__aexit__(exc_type, exc_val, exc_tb) @@ -189,6 +191,11 @@ class LvContext(LambdaContext): cg.add(expression) return expression + def get_automation_parameters(self) -> list[tuple[SafeExpType, str]]: + # When generating automations, we don't want the `lv_component` parameter to be passed + # to the lambda. + return [] + def __call__(self, *args): return self.add(*args) diff --git a/esphome/components/lvgl/sensor/__init__.py b/esphome/components/lvgl/sensor/__init__.py index 03b2638ed0..167af9c6e1 100644 --- a/esphome/components/lvgl/sensor/__init__.py +++ b/esphome/components/lvgl/sensor/__init__.py @@ -5,7 +5,6 @@ from ..defines import CONF_WIDGET from ..lvcode import ( API_EVENT, EVENT_ARG, - LVGL_COMP_ARG, UPDATE_EVENT, LambdaContext, LvContext, @@ -30,7 +29,7 @@ async def to_code(config): await wait_for_widgets() async with LambdaContext(EVENT_ARG) as lamb: lv_add(sensor.publish_state(widget.get_value())) - async with LvContext(LVGL_COMP_ARG): + async with LvContext(): lv_add( lvgl_static.add_event_cb( widget.obj, diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index d9b7013a1e..c70dd7568d 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -52,6 +52,19 @@ number: widget: spinbox_id id: lvgl_spinbox_number name: LVGL Spinbox Number + - platform: template + id: test_brightness + name: "Test Brightness" + min_value: 0 + max_value: 255 + step: 1 + optimistic: true + # Test lambda in automation accessing x parameter directly + # This is a real-world pattern from user configs + on_value: + - lambda: !lambda |- + // Direct use of x parameter in automation + ESP_LOGD("test", "Brightness: %.0f", x); light: - platform: lvgl @@ -110,3 +123,21 @@ text: platform: lvgl widget: hello_label mode: text + +text_sensor: + - platform: template + id: test_text_sensor + name: "Test Text Sensor" + # Test nested lambdas in LVGL actions can access automation parameters + on_value: + - lvgl.label.update: + id: hello_label + text: !lambda return x.c_str(); + - lvgl.label.update: + id: hello_label + text: !lambda |- + // Test complex lambda with conditionals accessing x parameter + if (x == "*") { + return "WILDCARD"; + } + return x.c_str(); diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 582531e943..14241a1669 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -257,7 +257,30 @@ lvgl: text: "Hello shiny day" text_color: 0xFFFFFF align: bottom_mid + - label: + id: setup_lambda_label + # Test lambda in widget property during setup (LvContext) + # Should NOT receive lv_component parameter + text: !lambda |- + char buf[32]; + snprintf(buf, sizeof(buf), "Setup: %d", 42); + return std::string(buf); + align: top_mid text_font: space16 + - label: + id: chip_info_label + # Test complex setup lambda (real-world pattern) + # Should NOT receive lv_component parameter + text: !lambda |- + // Test conditional compilation and string formatting + char buf[64]; + #ifdef USE_ESP_IDF + snprintf(buf, sizeof(buf), "IDF: v%d.%d", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR); + #else + snprintf(buf, sizeof(buf), "Arduino"); + #endif + return std::string(buf); + align: top_left - obj: align: center arc_opa: COVER From 6fb490f49b35d4610677e2f91b2230f5e5a012c4 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:40:22 -0400 Subject: [PATCH 307/526] [remote_transmitter] Add non-blocking mode (#11524) --- .../components/remote_transmitter/__init__.py | 20 +++++++++++++ .../remote_transmitter/remote_transmitter.h | 3 ++ .../remote_transmitter_esp32.cpp | 30 +++++++++++++++---- .../remote_transmitter/esp32-common.yaml | 1 + 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index cb98c017f1..faa6c827f7 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -1,3 +1,5 @@ +import logging + from esphome import automation, pins import esphome.codegen as cg from esphome.components import esp32, esp32_rmt, remote_base @@ -18,9 +20,12 @@ from esphome.const import ( ) from esphome.core import CORE +_LOGGER = logging.getLogger(__name__) + AUTO_LOAD = ["remote_base"] CONF_EOT_LEVEL = "eot_level" +CONF_NON_BLOCKING = "non_blocking" CONF_ON_TRANSMIT = "on_transmit" CONF_ON_COMPLETE = "on_complete" CONF_TRANSMITTER_ID = remote_base.CONF_TRANSMITTER_ID @@ -65,11 +70,25 @@ CONFIG_SCHEMA = cv.Schema( esp32_c6=48, esp32_h2=48, ): cv.All(cv.only_on_esp32, cv.int_range(min=2)), + cv.Optional(CONF_NON_BLOCKING): cv.All(cv.only_on_esp32, cv.boolean), cv.Optional(CONF_ON_TRANSMIT): automation.validate_automation(single=True), cv.Optional(CONF_ON_COMPLETE): automation.validate_automation(single=True), } ).extend(cv.COMPONENT_SCHEMA) + +def _validate_non_blocking(config): + if CORE.is_esp32 and CONF_NON_BLOCKING not in config: + _LOGGER.warning( + "'non_blocking' is not set for 'remote_transmitter' and will default to 'true'.\n" + "The default behavior changed in 2025.11.0; previously blocking mode was used.\n" + "To silence this warning, explicitly set 'non_blocking: true' (or 'false')." + ) + config[CONF_NON_BLOCKING] = True + + +FINAL_VALIDATE_SCHEMA = _validate_non_blocking + DIGITAL_WRITE_ACTION_SCHEMA = cv.maybe_simple_value( { cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterComponent), @@ -95,6 +114,7 @@ async def to_code(config): if CORE.is_esp32: var = cg.new_Pvariable(config[CONF_ID], pin) cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) + cg.add(var.set_non_blocking(config[CONF_NON_BLOCKING])) if CONF_CLOCK_RESOLUTION in config: cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) if CONF_USE_DMA in config: diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index b5d8e8d83f..cc3b82ad61 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -54,6 +54,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #if defined(USE_ESP32) void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; } void set_eot_level(bool eot_level) { this->eot_level_ = eot_level; } + void set_non_blocking(bool non_blocking) { this->non_blocking_ = non_blocking; } #endif Trigger<> *get_transmit_trigger() const { return this->transmit_trigger_; }; @@ -74,6 +75,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #ifdef USE_ESP32 void configure_rmt_(); + void wait_for_rmt_(); #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) RemoteTransmitterComponentStore store_{}; @@ -90,6 +92,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, esp_err_t error_code_{ESP_OK}; std::string error_string_{""}; bool inverted_{false}; + bool non_blocking_{false}; #endif uint8_t carrier_duty_percent_; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index 27bbf3c210..59c85c99a8 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -196,12 +196,29 @@ void RemoteTransmitterComponent::configure_rmt_() { } } +void RemoteTransmitterComponent::wait_for_rmt_() { + esp_err_t error = rmt_tx_wait_all_done(this->channel_, -1); + if (error != ESP_OK) { + ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error)); + this->status_set_warning(); + } + + this->complete_trigger_->trigger(); +} + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1) void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { + uint64_t total_duration = 0; + if (this->is_failed()) { return; } + // if the timeout was cancelled, block until the tx is complete + if (this->non_blocking_ && this->cancel_timeout("complete")) { + this->wait_for_rmt_(); + } + if (this->current_carrier_frequency_ != this->temp_.get_carrier_frequency()) { this->current_carrier_frequency_ = this->temp_.get_carrier_frequency(); this->configure_rmt_(); @@ -212,6 +229,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen // encode any delay at the start of the buffer to simplify the encoder callback // this will be skipped the first time around + total_duration += send_wait * (send_times - 1); send_wait = this->from_microseconds_(static_cast(send_wait)); while (send_wait > 0) { int32_t duration = std::min(send_wait, uint32_t(RMT_SYMBOL_DURATION_MAX)); @@ -229,6 +247,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen if (!level) { value = -value; } + total_duration += value * send_times; value = this->from_microseconds_(static_cast(value)); while (value > 0) { int32_t duration = std::min(value, int32_t(RMT_SYMBOL_DURATION_MAX)); @@ -260,13 +279,12 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen } else { this->status_clear_warning(); } - error = rmt_tx_wait_all_done(this->channel_, -1); - if (error != ESP_OK) { - ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error)); - this->status_set_warning(); - } - this->complete_trigger_->trigger(); + if (this->non_blocking_) { + this->set_timeout("complete", total_duration / 1000, [this]() { this->wait_for_rmt_(); }); + } else { + this->wait_for_rmt_(); + } } #else void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { diff --git a/tests/components/remote_transmitter/esp32-common.yaml b/tests/components/remote_transmitter/esp32-common.yaml index 8b26c45149..79fd47ae21 100644 --- a/tests/components/remote_transmitter/esp32-common.yaml +++ b/tests/components/remote_transmitter/esp32-common.yaml @@ -2,6 +2,7 @@ remote_transmitter: - id: xmitr pin: ${pin} carrier_duty_percent: 50% + non_blocking: true clock_resolution: ${clock_resolution} rmt_symbols: ${rmt_symbols} From f18c70a256328ecb9037ad45ff405a67f796565c Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Wed, 29 Oct 2025 19:06:55 +0100 Subject: [PATCH 308/526] [core] Fix substitution id redefinition false positive (#11603) --- esphome/config.py | 3 +++ tests/unit_tests/core/test_config.py | 11 +++++++++++ .../core/config/id_collision_with_substitution.yaml | 12 ++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 tests/unit_tests/fixtures/core/config/id_collision_with_substitution.yaml diff --git a/esphome/config.py b/esphome/config.py index 634dba8dad..e508ca585b 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -319,6 +319,9 @@ def iter_ids(config, path=None): yield from iter_ids(item, path + [i]) elif isinstance(config, dict): for key, value in config.items(): + if len(path) == 0 and key == CONF_SUBSTITUTIONS: + # Ignore IDs in substitution definitions. + continue if isinstance(key, core.ID): yield key, path yield from iter_ids(value, path + [key]) diff --git a/tests/unit_tests/core/test_config.py b/tests/unit_tests/core/test_config.py index a1e4627dc9..90b2f5edba 100644 --- a/tests/unit_tests/core/test_config.py +++ b/tests/unit_tests/core/test_config.py @@ -261,6 +261,17 @@ def test_device_duplicate_id( assert "ID duplicate_device redefined!" in captured.out +def test_substitution_with_id( + yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str] +) -> None: + """Test that a ids coming from substitutions do not cause false positive ID redefinition.""" + load_config_from_fixture( + yaml_file, "id_collision_with_substitution.yaml", FIXTURES_DIR + ) + captured = capsys.readouterr() + assert "ID some_switch_id redefined!" not in captured.out + + def test_add_platform_defines_priority() -> None: """Test that _add_platform_defines runs after globals. diff --git a/tests/unit_tests/fixtures/core/config/id_collision_with_substitution.yaml b/tests/unit_tests/fixtures/core/config/id_collision_with_substitution.yaml new file mode 100644 index 0000000000..840d9ac925 --- /dev/null +++ b/tests/unit_tests/fixtures/core/config/id_collision_with_substitution.yaml @@ -0,0 +1,12 @@ +esphome: + name: test + +host: + +substitutions: + support_switches: + - platform: gpio + id: some_switch_id + pin: 12 + +switch: $support_switches From 287f65cbaf4ab31fcab7a0502ff5ddbeee51d0e4 Mon Sep 17 00:00:00 2001 From: Stuart Parmenter Date: Wed, 29 Oct 2025 13:27:31 -0700 Subject: [PATCH 309/526] [lvgl] fix typo from previous refactor (#11596) --- esphome/components/image/image.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/image/image.cpp b/esphome/components/image/image.cpp index 7b65c4d0cb..90e021467f 100644 --- a/esphome/components/image/image.cpp +++ b/esphome/components/image/image.cpp @@ -125,7 +125,7 @@ lv_img_dsc_t *Image::get_lv_img_dsc() { case IMAGE_TYPE_RGB: #if LV_COLOR_DEPTH == 32 - switch (this->transparent_) { + switch (this->transparency_) { case TRANSPARENCY_ALPHA_CHANNEL: this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; break; @@ -156,7 +156,8 @@ lv_img_dsc_t *Image::get_lv_img_dsc() { break; } #else - this->dsc_.header.cf = this->transparent_ == TRANSPARENCY_ALPHA_CHANNEL ? LV_IMG_CF_RGB565A8 : LV_IMG_CF_RGB565; + this->dsc_.header.cf = + this->transparency_ == TRANSPARENCY_ALPHA_CHANNEL ? LV_IMG_CF_RGB565A8 : LV_IMG_CF_RGB565; #endif break; } From 918650f15adb10d87ae6d0a4f04accdb27b97b73 Mon Sep 17 00:00:00 2001 From: Stuart Parmenter Date: Wed, 29 Oct 2025 14:06:45 -0700 Subject: [PATCH 310/526] [lvgl] memset canvas buffer to prevent display of random garbage (#11582) Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/lvgl/widgets/canvas.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/esphome/components/lvgl/widgets/canvas.py b/esphome/components/lvgl/widgets/canvas.py index 217e8935f1..f0a9cd35ba 100644 --- a/esphome/components/lvgl/widgets/canvas.py +++ b/esphome/components/lvgl/widgets/canvas.py @@ -33,7 +33,7 @@ from ..lv_validation import ( pixels, size, ) -from ..lvcode import LocalVariable, lv, lv_assign +from ..lvcode import LocalVariable, lv, lv_assign, lv_expr from ..schemas import STYLE_PROPS, STYLE_REMAP, TEXT_SCHEMA, point_schema from ..types import LvType, ObjUpdateAction, WidgetType from . import Widget, get_widgets @@ -70,15 +70,18 @@ class CanvasType(WidgetType): width = config[CONF_WIDTH] height = config[CONF_HEIGHT] use_alpha = "_ALPHA" if config[CONF_TRANSPARENT] else "" - lv.canvas_set_buffer( - w.obj, - lv.custom_mem_alloc( - literal(f"LV_CANVAS_BUF_SIZE_TRUE_COLOR{use_alpha}({width}, {height})") - ), - width, - height, - literal(f"LV_IMG_CF_TRUE_COLOR{use_alpha}"), + buf_size = literal( + f"LV_CANVAS_BUF_SIZE_TRUE_COLOR{use_alpha}({width}, {height})" ) + with LocalVariable("buf", cg.void, lv_expr.custom_mem_alloc(buf_size)) as buf: + cg.add(cg.RawExpression(f"memset({buf}, 0, {buf_size});")) + lv.canvas_set_buffer( + w.obj, + buf, + width, + height, + literal(f"LV_IMG_CF_TRUE_COLOR{use_alpha}"), + ) canvas_spec = CanvasType() From 03fd1143710d58243a93f5901dc49e677413b981 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 29 Oct 2025 20:26:37 -0500 Subject: [PATCH 311/526] [ci] Restore parallel execution for clang-tidy split mode (#11613) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd45adb78b..a2b1f84ca8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -458,7 +458,7 @@ jobs: GH_TOKEN: ${{ github.token }} strategy: fail-fast: false - max-parallel: 1 + max-parallel: 2 matrix: include: - id: clang-tidy From 08aae39ea47d65dd4ff4af655fd967f2a64167f7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 29 Oct 2025 20:27:28 -0500 Subject: [PATCH 312/526] [ci] Consolidate component splitting into determine-jobs (#11614) --- .github/workflows/ci.yml | 46 ++--------------------------- script/determine-jobs.py | 24 +++++++++++++++ script/split_components_for_ci.py | 4 +++ tests/script/test_determine_jobs.py | 11 +++++++ 4 files changed, 42 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2b1f84ca8..1756d5b765 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -180,6 +180,7 @@ jobs: memory_impact: ${{ steps.determine.outputs.memory-impact }} cpp-unit-tests-run-all: ${{ steps.determine.outputs.cpp-unit-tests-run-all }} cpp-unit-tests-components: ${{ steps.determine.outputs.cpp-unit-tests-components }} + component-test-batches: ${{ steps.determine.outputs.component-test-batches }} steps: - name: Check out code from GitHub uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -214,6 +215,7 @@ jobs: echo "memory-impact=$(echo "$output" | jq -c '.memory_impact')" >> $GITHUB_OUTPUT echo "cpp-unit-tests-run-all=$(echo "$output" | jq -r '.cpp_unit_tests_run_all')" >> $GITHUB_OUTPUT echo "cpp-unit-tests-components=$(echo "$output" | jq -c '.cpp_unit_tests_components')" >> $GITHUB_OUTPUT + echo "component-test-batches=$(echo "$output" | jq -c '.component_test_batches')" >> $GITHUB_OUTPUT integration-tests: name: Run integration tests @@ -536,59 +538,18 @@ jobs: run: script/ci-suggest-changes if: always() - test-build-components-splitter: - name: Split components for intelligent grouping (40 weighted per batch) - runs-on: ubuntu-24.04 - needs: - - common - - determine-jobs - if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0 - outputs: - matrix: ${{ steps.split.outputs.components }} - steps: - - name: Check out code from GitHub - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Restore Python - uses: ./.github/actions/restore-python - with: - python-version: ${{ env.DEFAULT_PYTHON }} - cache-key: ${{ needs.common.outputs.cache-key }} - - name: Split components intelligently based on bus configurations - id: split - run: | - . venv/bin/activate - - # Use intelligent splitter that groups components with same bus configs - components='${{ needs.determine-jobs.outputs.changed-components-with-tests }}' - - # Only isolate directly changed components when targeting dev branch - # For beta/release branches, group everything for faster CI - if [[ "${{ github.base_ref }}" == beta* ]] || [[ "${{ github.base_ref }}" == release* ]]; then - directly_changed='[]' - echo "Target branch: ${{ github.base_ref }} - grouping all components" - else - directly_changed='${{ needs.determine-jobs.outputs.directly-changed-components-with-tests }}' - echo "Target branch: ${{ github.base_ref }} - isolating directly changed components" - fi - - echo "Splitting components intelligently..." - output=$(python3 script/split_components_for_ci.py --components "$components" --directly-changed "$directly_changed" --batch-size 40 --output github) - - echo "$output" >> $GITHUB_OUTPUT - test-build-components-split: name: Test components batch (${{ matrix.components }}) runs-on: ubuntu-24.04 needs: - common - determine-jobs - - test-build-components-splitter if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0 strategy: fail-fast: false max-parallel: ${{ (startsWith(github.base_ref, 'beta') || startsWith(github.base_ref, 'release')) && 8 || 4 }} matrix: - components: ${{ fromJson(needs.test-build-components-splitter.outputs.matrix) }} + components: ${{ fromJson(needs.determine-jobs.outputs.component-test-batches) }} steps: - name: Show disk space run: | @@ -980,7 +941,6 @@ jobs: - clang-tidy-nosplit - clang-tidy-split - determine-jobs - - test-build-components-splitter - test-build-components-split - pre-commit-ci-lite - memory-impact-target-branch diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 21eb529f33..4a0edebb0d 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -43,12 +43,14 @@ from enum import StrEnum from functools import cache import json import os +from pathlib import Path import subprocess import sys from typing import Any from helpers import ( CPP_FILE_EXTENSIONS, + ESPHOME_TESTS_COMPONENTS_PATH, PYTHON_FILE_EXTENSIONS, changed_files, core_changed, @@ -65,12 +67,17 @@ from helpers import ( parse_test_filename, root_path, ) +from split_components_for_ci import create_intelligent_batches # Threshold for splitting clang-tidy jobs # For small PRs (< 65 files), use nosplit for faster CI # For large PRs (>= 65 files), use split for better parallelization CLANG_TIDY_SPLIT_THRESHOLD = 65 +# Component test batch size (weighted) +# Isolated components count as 10x, groupable components count as 1x +COMPONENT_TEST_BATCH_SIZE = 40 + class Platform(StrEnum): """Platform identifiers for memory impact analysis.""" @@ -686,6 +693,22 @@ def main() -> None: # Determine which C++ unit tests to run cpp_run_all, cpp_components = determine_cpp_unit_tests(args.branch) + # Split components into batches for CI testing + # This intelligently groups components with similar bus configurations + component_test_batches: list[str] + if changed_components_with_tests: + tests_dir = Path(root_path) / ESPHOME_TESTS_COMPONENTS_PATH + batches, _ = create_intelligent_batches( + components=changed_components_with_tests, + tests_dir=tests_dir, + batch_size=COMPONENT_TEST_BATCH_SIZE, + directly_changed=directly_changed_with_tests, + ) + # Convert batches to space-separated strings for CI matrix + component_test_batches = [" ".join(batch) for batch in batches] + else: + component_test_batches = [] + output: dict[str, Any] = { "integration_tests": run_integration, "clang_tidy": run_clang_tidy, @@ -703,6 +726,7 @@ def main() -> None: "memory_impact": memory_impact, "cpp_unit_tests_run_all": cpp_run_all, "cpp_unit_tests_components": cpp_components, + "component_test_batches": component_test_batches, } # Output as JSON diff --git a/script/split_components_for_ci.py b/script/split_components_for_ci.py index 87da540d43..65d09efb9b 100755 --- a/script/split_components_for_ci.py +++ b/script/split_components_for_ci.py @@ -62,6 +62,10 @@ def create_intelligent_batches( ) -> tuple[list[list[str]], dict[tuple[str, str], list[str]]]: """Create batches optimized for component grouping. + IMPORTANT: This function is called from both split_components_for_ci.py (standalone script) + and determine-jobs.py (integrated into job determination). Be careful when refactoring + to ensure changes work in both contexts. + Args: components: List of component names to batch tests_dir: Path to tests/components directory diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index c8ef76184f..e73c134151 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -152,6 +152,14 @@ def test_main_all_tests_should_run( assert output["memory_impact"]["should_run"] == "false" assert output["cpp_unit_tests_run_all"] is False assert output["cpp_unit_tests_components"] == ["wifi", "api", "sensor"] + # component_test_batches should be present and be a list of space-separated strings + assert "component_test_batches" in output + assert isinstance(output["component_test_batches"], list) + # Each batch should be a space-separated string of component names + for batch in output["component_test_batches"]: + assert isinstance(batch, str) + # Should contain at least one component (no empty batches) + assert len(batch) > 0 def test_main_no_tests_should_run( @@ -209,6 +217,9 @@ def test_main_no_tests_should_run( assert output["memory_impact"]["should_run"] == "false" assert output["cpp_unit_tests_run_all"] is False assert output["cpp_unit_tests_components"] == [] + # component_test_batches should be empty list + assert "component_test_batches" in output + assert output["component_test_batches"] == [] def test_main_with_branch_argument( From 29ed3c20af179fcdb2fd641566f7167fe79c863a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 29 Oct 2025 20:28:38 -0500 Subject: [PATCH 313/526] [gpio] Skip set_use_interrupt call when using default value (#11612) --- esphome/components/gpio/binary_sensor/__init__.py | 4 +++- tests/component_tests/gpio/test_gpio_binary_sensor.py | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/esphome/components/gpio/binary_sensor/__init__.py b/esphome/components/gpio/binary_sensor/__init__.py index 8372bc7e08..ca4dc43e9c 100644 --- a/esphome/components/gpio/binary_sensor/__init__.py +++ b/esphome/components/gpio/binary_sensor/__init__.py @@ -94,6 +94,8 @@ async def to_code(config): ) use_interrupt = False - cg.add(var.set_use_interrupt(use_interrupt)) if use_interrupt: cg.add(var.set_interrupt_type(config[CONF_INTERRUPT_TYPE])) + else: + # Only generate call when disabling interrupts (default is true) + cg.add(var.set_use_interrupt(use_interrupt)) diff --git a/tests/component_tests/gpio/test_gpio_binary_sensor.py b/tests/component_tests/gpio/test_gpio_binary_sensor.py index 74fa2ab1c1..73665dc45d 100644 --- a/tests/component_tests/gpio/test_gpio_binary_sensor.py +++ b/tests/component_tests/gpio/test_gpio_binary_sensor.py @@ -18,7 +18,8 @@ def test_gpio_binary_sensor_basic_setup( assert "new gpio::GPIOBinarySensor();" in main_cpp assert "App.register_binary_sensor" in main_cpp - assert "bs_gpio->set_use_interrupt(true);" in main_cpp + # set_use_interrupt(true) should NOT be generated (uses C++ default) + assert "bs_gpio->set_use_interrupt(true);" not in main_cpp assert "bs_gpio->set_interrupt_type(gpio::INTERRUPT_ANY_EDGE);" in main_cpp @@ -51,8 +52,8 @@ def test_gpio_binary_sensor_esp8266_other_pins_use_interrupt( "tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml" ) - # GPIO5 should still use interrupts - assert "bs_gpio5->set_use_interrupt(true);" in main_cpp + # GPIO5 should still use interrupts (default, so no setter call) + assert "bs_gpio5->set_use_interrupt(true);" not in main_cpp assert "bs_gpio5->set_interrupt_type(gpio::INTERRUPT_ANY_EDGE);" in main_cpp From 58235049e3c7a023a37441aed6b8440abaec0626 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 29 Oct 2025 21:06:21 -0500 Subject: [PATCH 314/526] [template] Eliminate optional wrapper to save 4 bytes RAM per instance (#11610) --- .../binary_sensor/template_binary_sensor.cpp | 14 +++-- .../binary_sensor/template_binary_sensor.h | 5 +- .../template/cover/template_cover.cpp | 33 ++++++------ .../template/cover/template_cover.h | 9 ++-- .../template/datetime/template_date.cpp | 15 +++--- .../template/datetime/template_date.h | 5 +- .../template/datetime/template_datetime.cpp | 21 ++++---- .../template/datetime/template_datetime.h | 5 +- .../template/datetime/template_time.cpp | 15 +++--- .../template/datetime/template_time.h | 5 +- .../template/lock/template_lock.cpp | 15 +++--- .../components/template/lock/template_lock.h | 6 ++- .../template/number/template_number.cpp | 9 ++-- .../template/number/template_number.h | 5 +- .../template/select/template_select.cpp | 16 +++--- .../template/select/template_select.h | 5 +- .../template/sensor/template_sensor.cpp | 5 +- .../template/sensor/template_sensor.h | 5 +- .../template/switch/template_switch.cpp | 15 +++--- .../template/switch/template_switch.h | 5 +- .../template/text/template_text.cpp | 18 +++---- .../components/template/text/template_text.h | 5 +- .../text_sensor/template_text_sensor.cpp | 5 +- .../text_sensor/template_text_sensor.h | 5 +- .../template/valve/template_valve.cpp | 17 +++---- .../template/valve/template_valve.h | 5 +- esphome/core/template_lambda.h | 51 +++++++++++++++++++ tests/components/template/common-base.yaml | 8 +++ tests/integration/fixtures/runtime_stats.yaml | 1 + 29 files changed, 196 insertions(+), 132 deletions(-) create mode 100644 esphome/core/template_lambda.h diff --git a/esphome/components/template/binary_sensor/template_binary_sensor.cpp b/esphome/components/template/binary_sensor/template_binary_sensor.cpp index 8543dff4dc..806aed49b1 100644 --- a/esphome/components/template/binary_sensor/template_binary_sensor.cpp +++ b/esphome/components/template/binary_sensor/template_binary_sensor.cpp @@ -6,17 +6,21 @@ namespace template_ { static const char *const TAG = "template.binary_sensor"; -void TemplateBinarySensor::setup() { this->loop(); } +void TemplateBinarySensor::setup() { + if (!this->f_.has_value()) { + this->disable_loop(); + } else { + this->loop(); + } +} void TemplateBinarySensor::loop() { - if (!this->f_.has_value()) - return; - - auto s = (*this->f_)(); + auto s = this->f_(); if (s.has_value()) { this->publish_state(*s); } } + void TemplateBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Template Binary Sensor", this); } } // namespace template_ diff --git a/esphome/components/template/binary_sensor/template_binary_sensor.h b/esphome/components/template/binary_sensor/template_binary_sensor.h index 2e0b216eb4..bc591391b9 100644 --- a/esphome/components/template/binary_sensor/template_binary_sensor.h +++ b/esphome/components/template/binary_sensor/template_binary_sensor.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/template_lambda.h" #include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { @@ -8,7 +9,7 @@ namespace template_ { class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor { public: - void set_template(optional (*f)()) { this->f_ = f; } + template void set_template(F &&f) { this->f_.set(std::forward(f)); } void setup() override; void loop() override; @@ -17,7 +18,7 @@ class TemplateBinarySensor : public Component, public binary_sensor::BinarySenso float get_setup_priority() const override { return setup_priority::HARDWARE; } protected: - optional (*)()> f_; + TemplateLambda f_; }; } // namespace template_ diff --git a/esphome/components/template/cover/template_cover.cpp b/esphome/components/template/cover/template_cover.cpp index bed3931e78..a87f28ccec 100644 --- a/esphome/components/template/cover/template_cover.cpp +++ b/esphome/components/template/cover/template_cover.cpp @@ -33,28 +33,27 @@ void TemplateCover::setup() { break; } } + if (!this->state_f_.has_value() && !this->tilt_f_.has_value()) + this->disable_loop(); } void TemplateCover::loop() { bool changed = false; - if (this->state_f_.has_value()) { - auto s = (*this->state_f_)(); - if (s.has_value()) { - auto pos = clamp(*s, 0.0f, 1.0f); - if (pos != this->position) { - this->position = pos; - changed = true; - } + auto s = this->state_f_(); + if (s.has_value()) { + auto pos = clamp(*s, 0.0f, 1.0f); + if (pos != this->position) { + this->position = pos; + changed = true; } } - if (this->tilt_f_.has_value()) { - auto s = (*this->tilt_f_)(); - if (s.has_value()) { - auto tilt = clamp(*s, 0.0f, 1.0f); - if (tilt != this->tilt) { - this->tilt = tilt; - changed = true; - } + + auto tilt = this->tilt_f_(); + if (tilt.has_value()) { + auto tilt_val = clamp(*tilt, 0.0f, 1.0f); + if (tilt_val != this->tilt) { + this->tilt = tilt_val; + changed = true; } } @@ -63,7 +62,6 @@ void TemplateCover::loop() { } void TemplateCover::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } void TemplateCover::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; } -void TemplateCover::set_state_lambda(optional (*f)()) { this->state_f_ = f; } float TemplateCover::get_setup_priority() const { return setup_priority::HARDWARE; } Trigger<> *TemplateCover::get_open_trigger() const { return this->open_trigger_; } Trigger<> *TemplateCover::get_close_trigger() const { return this->close_trigger_; } @@ -124,7 +122,6 @@ CoverTraits TemplateCover::get_traits() { } Trigger *TemplateCover::get_position_trigger() const { return this->position_trigger_; } Trigger *TemplateCover::get_tilt_trigger() const { return this->tilt_trigger_; } -void TemplateCover::set_tilt_lambda(optional (*tilt_f)()) { this->tilt_f_ = tilt_f; } void TemplateCover::set_has_stop(bool has_stop) { this->has_stop_ = has_stop; } void TemplateCover::set_has_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; } void TemplateCover::set_has_position(bool has_position) { this->has_position_ = has_position; } diff --git a/esphome/components/template/cover/template_cover.h b/esphome/components/template/cover/template_cover.h index ed1ebf4e43..faff69f867 100644 --- a/esphome/components/template/cover/template_cover.h +++ b/esphome/components/template/cover/template_cover.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/template_lambda.h" #include "esphome/components/cover/cover.h" namespace esphome { @@ -17,7 +18,8 @@ class TemplateCover : public cover::Cover, public Component { public: TemplateCover(); - void set_state_lambda(optional (*f)()); + template void set_state_lambda(F &&f) { this->state_f_.set(std::forward(f)); } + template void set_tilt_lambda(F &&f) { this->tilt_f_.set(std::forward(f)); } Trigger<> *get_open_trigger() const; Trigger<> *get_close_trigger() const; Trigger<> *get_stop_trigger() const; @@ -26,7 +28,6 @@ class TemplateCover : public cover::Cover, public Component { Trigger *get_tilt_trigger() const; void set_optimistic(bool optimistic); void set_assumed_state(bool assumed_state); - void set_tilt_lambda(optional (*tilt_f)()); void set_has_stop(bool has_stop); void set_has_position(bool has_position); void set_has_tilt(bool has_tilt); @@ -45,8 +46,8 @@ class TemplateCover : public cover::Cover, public Component { void stop_prev_trigger_(); TemplateCoverRestoreMode restore_mode_{COVER_RESTORE}; - optional (*)()> state_f_; - optional (*)()> tilt_f_; + TemplateLambda state_f_; + TemplateLambda tilt_f_; bool assumed_state_{false}; bool optimistic_{false}; Trigger<> *open_trigger_; diff --git a/esphome/components/template/datetime/template_date.cpp b/esphome/components/template/datetime/template_date.cpp index 2fa8016802..3f6626e847 100644 --- a/esphome/components/template/datetime/template_date.cpp +++ b/esphome/components/template/datetime/template_date.cpp @@ -40,14 +40,13 @@ void TemplateDate::update() { if (!this->f_.has_value()) return; - auto val = (*this->f_)(); - if (!val.has_value()) - return; - - this->year_ = val->year; - this->month_ = val->month; - this->day_ = val->day_of_month; - this->publish_state(); + auto val = this->f_(); + if (val.has_value()) { + this->year_ = val->year; + this->month_ = val->month; + this->day_ = val->day_of_month; + this->publish_state(); + } } void TemplateDate::control(const datetime::DateCall &call) { diff --git a/esphome/components/template/datetime/template_date.h b/esphome/components/template/datetime/template_date.h index 2a0967fc94..7fed704d0e 100644 --- a/esphome/components/template/datetime/template_date.h +++ b/esphome/components/template/datetime/template_date.h @@ -9,13 +9,14 @@ #include "esphome/core/component.h" #include "esphome/core/preferences.h" #include "esphome/core/time.h" +#include "esphome/core/template_lambda.h" namespace esphome { namespace template_ { class TemplateDate : public datetime::DateEntity, public PollingComponent { public: - void set_template(optional (*f)()) { this->f_ = f; } + template void set_template(F &&f) { this->f_.set(std::forward(f)); } void setup() override; void update() override; @@ -35,7 +36,7 @@ class TemplateDate : public datetime::DateEntity, public PollingComponent { ESPTime initial_value_{}; bool restore_value_{false}; Trigger *set_trigger_ = new Trigger(); - optional (*)()> f_; + TemplateLambda f_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/template/datetime/template_datetime.cpp b/esphome/components/template/datetime/template_datetime.cpp index a4a4e47d65..62f842a7ad 100644 --- a/esphome/components/template/datetime/template_datetime.cpp +++ b/esphome/components/template/datetime/template_datetime.cpp @@ -43,17 +43,16 @@ void TemplateDateTime::update() { if (!this->f_.has_value()) return; - auto val = (*this->f_)(); - if (!val.has_value()) - return; - - this->year_ = val->year; - this->month_ = val->month; - this->day_ = val->day_of_month; - this->hour_ = val->hour; - this->minute_ = val->minute; - this->second_ = val->second; - this->publish_state(); + auto val = this->f_(); + if (val.has_value()) { + this->year_ = val->year; + this->month_ = val->month; + this->day_ = val->day_of_month; + this->hour_ = val->hour; + this->minute_ = val->minute; + this->second_ = val->second; + this->publish_state(); + } } void TemplateDateTime::control(const datetime::DateTimeCall &call) { diff --git a/esphome/components/template/datetime/template_datetime.h b/esphome/components/template/datetime/template_datetime.h index d917015b67..ec45bf0326 100644 --- a/esphome/components/template/datetime/template_datetime.h +++ b/esphome/components/template/datetime/template_datetime.h @@ -9,13 +9,14 @@ #include "esphome/core/component.h" #include "esphome/core/preferences.h" #include "esphome/core/time.h" +#include "esphome/core/template_lambda.h" namespace esphome { namespace template_ { class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponent { public: - void set_template(optional (*f)()) { this->f_ = f; } + template void set_template(F &&f) { this->f_.set(std::forward(f)); } void setup() override; void update() override; @@ -35,7 +36,7 @@ class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponen ESPTime initial_value_{}; bool restore_value_{false}; Trigger *set_trigger_ = new Trigger(); - optional (*)()> f_; + TemplateLambda f_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/template/datetime/template_time.cpp b/esphome/components/template/datetime/template_time.cpp index 349700f187..dab28d01cc 100644 --- a/esphome/components/template/datetime/template_time.cpp +++ b/esphome/components/template/datetime/template_time.cpp @@ -40,14 +40,13 @@ void TemplateTime::update() { if (!this->f_.has_value()) return; - auto val = (*this->f_)(); - if (!val.has_value()) - return; - - this->hour_ = val->hour; - this->minute_ = val->minute; - this->second_ = val->second; - this->publish_state(); + auto val = this->f_(); + if (val.has_value()) { + this->hour_ = val->hour; + this->minute_ = val->minute; + this->second_ = val->second; + this->publish_state(); + } } void TemplateTime::control(const datetime::TimeCall &call) { diff --git a/esphome/components/template/datetime/template_time.h b/esphome/components/template/datetime/template_time.h index 2f05ba0737..ea7474c0ba 100644 --- a/esphome/components/template/datetime/template_time.h +++ b/esphome/components/template/datetime/template_time.h @@ -9,13 +9,14 @@ #include "esphome/core/component.h" #include "esphome/core/preferences.h" #include "esphome/core/time.h" +#include "esphome/core/template_lambda.h" namespace esphome { namespace template_ { class TemplateTime : public datetime::TimeEntity, public PollingComponent { public: - void set_template(optional (*f)()) { this->f_ = f; } + template void set_template(F &&f) { this->f_.set(std::forward(f)); } void setup() override; void update() override; @@ -35,7 +36,7 @@ class TemplateTime : public datetime::TimeEntity, public PollingComponent { ESPTime initial_value_{}; bool restore_value_{false}; Trigger *set_trigger_ = new Trigger(); - optional (*)()> f_; + TemplateLambda f_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/template/lock/template_lock.cpp b/esphome/components/template/lock/template_lock.cpp index c2e227c26d..8ed87b9736 100644 --- a/esphome/components/template/lock/template_lock.cpp +++ b/esphome/components/template/lock/template_lock.cpp @@ -11,14 +11,16 @@ static const char *const TAG = "template.lock"; TemplateLock::TemplateLock() : lock_trigger_(new Trigger<>()), unlock_trigger_(new Trigger<>()), open_trigger_(new Trigger<>()) {} -void TemplateLock::loop() { +void TemplateLock::setup() { if (!this->f_.has_value()) - return; - auto val = (*this->f_)(); - if (!val.has_value()) - return; + this->disable_loop(); +} - this->publish_state(*val); +void TemplateLock::loop() { + auto val = this->f_(); + if (val.has_value()) { + this->publish_state(*val); + } } void TemplateLock::control(const lock::LockCall &call) { if (this->prev_trigger_ != nullptr) { @@ -45,7 +47,6 @@ void TemplateLock::open_latch() { this->open_trigger_->trigger(); } void TemplateLock::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } -void TemplateLock::set_state_lambda(optional (*f)()) { this->f_ = f; } float TemplateLock::get_setup_priority() const { return setup_priority::HARDWARE; } Trigger<> *TemplateLock::get_lock_trigger() const { return this->lock_trigger_; } Trigger<> *TemplateLock::get_unlock_trigger() const { return this->unlock_trigger_; } diff --git a/esphome/components/template/lock/template_lock.h b/esphome/components/template/lock/template_lock.h index 428744a66f..14fca4635e 100644 --- a/esphome/components/template/lock/template_lock.h +++ b/esphome/components/template/lock/template_lock.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/template_lambda.h" #include "esphome/components/lock/lock.h" namespace esphome { @@ -11,9 +12,10 @@ class TemplateLock : public lock::Lock, public Component { public: TemplateLock(); + void setup() override; void dump_config() override; - void set_state_lambda(optional (*f)()); + template void set_state_lambda(F &&f) { this->f_.set(std::forward(f)); } Trigger<> *get_lock_trigger() const; Trigger<> *get_unlock_trigger() const; Trigger<> *get_open_trigger() const; @@ -26,7 +28,7 @@ class TemplateLock : public lock::Lock, public Component { void control(const lock::LockCall &call) override; void open_latch() override; - optional (*)()> f_; + TemplateLambda f_; bool optimistic_{false}; Trigger<> *lock_trigger_; Trigger<> *unlock_trigger_; diff --git a/esphome/components/template/number/template_number.cpp b/esphome/components/template/number/template_number.cpp index 187f426273..145a89a2f7 100644 --- a/esphome/components/template/number/template_number.cpp +++ b/esphome/components/template/number/template_number.cpp @@ -30,11 +30,10 @@ void TemplateNumber::update() { if (!this->f_.has_value()) return; - auto val = (*this->f_)(); - if (!val.has_value()) - return; - - this->publish_state(*val); + auto val = this->f_(); + if (val.has_value()) { + this->publish_state(*val); + } } void TemplateNumber::control(float value) { diff --git a/esphome/components/template/number/template_number.h b/esphome/components/template/number/template_number.h index e77b181d25..a9307e9246 100644 --- a/esphome/components/template/number/template_number.h +++ b/esphome/components/template/number/template_number.h @@ -4,13 +4,14 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/preferences.h" +#include "esphome/core/template_lambda.h" namespace esphome { namespace template_ { class TemplateNumber : public number::Number, public PollingComponent { public: - void set_template(optional (*f)()) { this->f_ = f; } + template void set_template(F &&f) { this->f_.set(std::forward(f)); } void setup() override; void update() override; @@ -28,7 +29,7 @@ class TemplateNumber : public number::Number, public PollingComponent { float initial_value_{NAN}; bool restore_value_{false}; Trigger *set_trigger_ = new Trigger(); - optional (*)()> f_; + TemplateLambda f_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/template/select/template_select.cpp b/esphome/components/template/select/template_select.cpp index c7a1d8a344..3ea34c3c7c 100644 --- a/esphome/components/template/select/template_select.cpp +++ b/esphome/components/template/select/template_select.cpp @@ -31,16 +31,14 @@ void TemplateSelect::update() { if (!this->f_.has_value()) return; - auto val = (*this->f_)(); - if (!val.has_value()) - return; - - if (!this->has_option(*val)) { - ESP_LOGE(TAG, "Lambda returned an invalid option: %s", (*val).c_str()); - return; + auto val = this->f_(); + if (val.has_value()) { + if (!this->has_option(*val)) { + ESP_LOGE(TAG, "Lambda returned an invalid option: %s", (*val).c_str()); + return; + } + this->publish_state(*val); } - - this->publish_state(*val); } void TemplateSelect::control(const std::string &value) { diff --git a/esphome/components/template/select/template_select.h b/esphome/components/template/select/template_select.h index e77e4d8f14..1c33153872 100644 --- a/esphome/components/template/select/template_select.h +++ b/esphome/components/template/select/template_select.h @@ -4,13 +4,14 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/preferences.h" +#include "esphome/core/template_lambda.h" namespace esphome { namespace template_ { class TemplateSelect : public select::Select, public PollingComponent { public: - void set_template(optional (*f)()) { this->f_ = f; } + template void set_template(F &&f) { this->f_.set(std::forward(f)); } void setup() override; void update() override; @@ -28,7 +29,7 @@ class TemplateSelect : public select::Select, public PollingComponent { size_t initial_option_index_{0}; bool restore_value_ = false; Trigger *set_trigger_ = new Trigger(); - optional (*)()> f_; + TemplateLambda f_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/template/sensor/template_sensor.cpp b/esphome/components/template/sensor/template_sensor.cpp index 65f2417670..1558ea9b15 100644 --- a/esphome/components/template/sensor/template_sensor.cpp +++ b/esphome/components/template/sensor/template_sensor.cpp @@ -11,13 +11,14 @@ void TemplateSensor::update() { if (!this->f_.has_value()) return; - auto val = (*this->f_)(); + auto val = this->f_(); if (val.has_value()) { this->publish_state(*val); } } + float TemplateSensor::get_setup_priority() const { return setup_priority::HARDWARE; } -void TemplateSensor::set_template(optional (*f)()) { this->f_ = f; } + void TemplateSensor::dump_config() { LOG_SENSOR("", "Template Sensor", this); LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/template/sensor/template_sensor.h b/esphome/components/template/sensor/template_sensor.h index 369313d607..793d754a0f 100644 --- a/esphome/components/template/sensor/template_sensor.h +++ b/esphome/components/template/sensor/template_sensor.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/template_lambda.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -8,7 +9,7 @@ namespace template_ { class TemplateSensor : public sensor::Sensor, public PollingComponent { public: - void set_template(optional (*f)()); + template void set_template(F &&f) { this->f_.set(std::forward(f)); } void update() override; @@ -17,7 +18,7 @@ class TemplateSensor : public sensor::Sensor, public PollingComponent { float get_setup_priority() const override; protected: - optional (*)()> f_; + TemplateLambda f_; }; } // namespace template_ diff --git a/esphome/components/template/switch/template_switch.cpp b/esphome/components/template/switch/template_switch.cpp index 5aaf514b2a..95e8692da5 100644 --- a/esphome/components/template/switch/template_switch.cpp +++ b/esphome/components/template/switch/template_switch.cpp @@ -9,13 +9,10 @@ static const char *const TAG = "template.switch"; TemplateSwitch::TemplateSwitch() : turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {} void TemplateSwitch::loop() { - if (!this->f_.has_value()) - return; - auto s = (*this->f_)(); - if (!s.has_value()) - return; - - this->publish_state(*s); + auto s = this->f_(); + if (s.has_value()) { + this->publish_state(*s); + } } void TemplateSwitch::write_state(bool state) { if (this->prev_trigger_ != nullptr) { @@ -35,11 +32,13 @@ void TemplateSwitch::write_state(bool state) { } void TemplateSwitch::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } bool TemplateSwitch::assumed_state() { return this->assumed_state_; } -void TemplateSwitch::set_state_lambda(optional (*f)()) { this->f_ = f; } float TemplateSwitch::get_setup_priority() const { return setup_priority::HARDWARE - 2.0f; } Trigger<> *TemplateSwitch::get_turn_on_trigger() const { return this->turn_on_trigger_; } Trigger<> *TemplateSwitch::get_turn_off_trigger() const { return this->turn_off_trigger_; } void TemplateSwitch::setup() { + if (!this->f_.has_value()) + this->disable_loop(); + optional initial_state = this->get_initial_state_with_restore_mode(); if (initial_state.has_value()) { diff --git a/esphome/components/template/switch/template_switch.h b/esphome/components/template/switch/template_switch.h index 0fba66b9bd..18a374df35 100644 --- a/esphome/components/template/switch/template_switch.h +++ b/esphome/components/template/switch/template_switch.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/template_lambda.h" #include "esphome/components/switch/switch.h" namespace esphome { @@ -14,7 +15,7 @@ class TemplateSwitch : public switch_::Switch, public Component { void setup() override; void dump_config() override; - void set_state_lambda(optional (*f)()); + template void set_state_lambda(F &&f) { this->f_.set(std::forward(f)); } Trigger<> *get_turn_on_trigger() const; Trigger<> *get_turn_off_trigger() const; void set_optimistic(bool optimistic); @@ -28,7 +29,7 @@ class TemplateSwitch : public switch_::Switch, public Component { void write_state(bool state) override; - optional (*)()> f_; + TemplateLambda f_; bool optimistic_{false}; bool assumed_state_{false}; Trigger<> *turn_on_trigger_; diff --git a/esphome/components/template/text/template_text.cpp b/esphome/components/template/text/template_text.cpp index d8e840ba7e..a917c72a14 100644 --- a/esphome/components/template/text/template_text.cpp +++ b/esphome/components/template/text/template_text.cpp @@ -7,10 +7,8 @@ namespace template_ { static const char *const TAG = "template.text"; void TemplateText::setup() { - if (!(this->f_ == nullptr)) { - if (this->f_.has_value()) - return; - } + if (this->f_.has_value()) + return; std::string value = this->initial_value_; if (!this->pref_) { ESP_LOGD(TAG, "State from initial: %s", value.c_str()); @@ -26,17 +24,13 @@ void TemplateText::setup() { } void TemplateText::update() { - if (this->f_ == nullptr) - return; - if (!this->f_.has_value()) return; - auto val = (*this->f_)(); - if (!val.has_value()) - return; - - this->publish_state(*val); + auto val = this->f_(); + if (val.has_value()) { + this->publish_state(*val); + } } void TemplateText::control(const std::string &value) { diff --git a/esphome/components/template/text/template_text.h b/esphome/components/template/text/template_text.h index 6c17d2016a..c12021f80e 100644 --- a/esphome/components/template/text/template_text.h +++ b/esphome/components/template/text/template_text.h @@ -4,6 +4,7 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/preferences.h" +#include "esphome/core/template_lambda.h" namespace esphome { namespace template_ { @@ -61,7 +62,7 @@ template class TextSaver : public TemplateTextSaverBase { class TemplateText : public text::Text, public PollingComponent { public: - void set_template(optional (*f)()) { this->f_ = f; } + template void set_template(F &&f) { this->f_.set(std::forward(f)); } void setup() override; void update() override; @@ -78,7 +79,7 @@ class TemplateText : public text::Text, public PollingComponent { bool optimistic_ = false; std::string initial_value_; Trigger *set_trigger_ = new Trigger(); - optional (*)()> f_{nullptr}; + TemplateLambda f_{}; TemplateTextSaverBase *pref_ = nullptr; }; diff --git a/esphome/components/template/text_sensor/template_text_sensor.cpp b/esphome/components/template/text_sensor/template_text_sensor.cpp index 2b0297d62f..024d0093a2 100644 --- a/esphome/components/template/text_sensor/template_text_sensor.cpp +++ b/esphome/components/template/text_sensor/template_text_sensor.cpp @@ -10,13 +10,14 @@ void TemplateTextSensor::update() { if (!this->f_.has_value()) return; - auto val = (*this->f_)(); + auto val = this->f_(); if (val.has_value()) { this->publish_state(*val); } } + float TemplateTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; } -void TemplateTextSensor::set_template(optional (*f)()) { this->f_ = f; } + void TemplateTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Template Sensor", this); } } // namespace template_ diff --git a/esphome/components/template/text_sensor/template_text_sensor.h b/esphome/components/template/text_sensor/template_text_sensor.h index 48e40c2493..0d01c72023 100644 --- a/esphome/components/template/text_sensor/template_text_sensor.h +++ b/esphome/components/template/text_sensor/template_text_sensor.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/template_lambda.h" #include "esphome/components/text_sensor/text_sensor.h" namespace esphome { @@ -9,7 +10,7 @@ namespace template_ { class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent { public: - void set_template(optional (*f)()); + template void set_template(F &&f) { this->f_.set(std::forward(f)); } void update() override; @@ -18,7 +19,7 @@ class TemplateTextSensor : public text_sensor::TextSensor, public PollingCompone void dump_config() override; protected: - optional (*)()> f_{}; + TemplateLambda f_{}; }; } // namespace template_ diff --git a/esphome/components/template/valve/template_valve.cpp b/esphome/components/template/valve/template_valve.cpp index b27cc00968..b91b32473e 100644 --- a/esphome/components/template/valve/template_valve.cpp +++ b/esphome/components/template/valve/template_valve.cpp @@ -33,19 +33,19 @@ void TemplateValve::setup() { break; } } + if (!this->state_f_.has_value()) + this->disable_loop(); } void TemplateValve::loop() { bool changed = false; - if (this->state_f_.has_value()) { - auto s = (*this->state_f_)(); - if (s.has_value()) { - auto pos = clamp(*s, 0.0f, 1.0f); - if (pos != this->position) { - this->position = pos; - changed = true; - } + auto s = this->state_f_(); + if (s.has_value()) { + auto pos = clamp(*s, 0.0f, 1.0f); + if (pos != this->position) { + this->position = pos; + changed = true; } } @@ -55,7 +55,6 @@ void TemplateValve::loop() { void TemplateValve::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } void TemplateValve::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; } -void TemplateValve::set_state_lambda(optional (*f)()) { this->state_f_ = f; } float TemplateValve::get_setup_priority() const { return setup_priority::HARDWARE; } Trigger<> *TemplateValve::get_open_trigger() const { return this->open_trigger_; } diff --git a/esphome/components/template/valve/template_valve.h b/esphome/components/template/valve/template_valve.h index 92c32f3487..d6235f8e5c 100644 --- a/esphome/components/template/valve/template_valve.h +++ b/esphome/components/template/valve/template_valve.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/template_lambda.h" #include "esphome/components/valve/valve.h" namespace esphome { @@ -17,7 +18,7 @@ class TemplateValve : public valve::Valve, public Component { public: TemplateValve(); - void set_state_lambda(optional (*f)()); + template void set_state_lambda(F &&f) { this->state_f_.set(std::forward(f)); } Trigger<> *get_open_trigger() const; Trigger<> *get_close_trigger() const; Trigger<> *get_stop_trigger() const; @@ -42,7 +43,7 @@ class TemplateValve : public valve::Valve, public Component { void stop_prev_trigger_(); TemplateValveRestoreMode restore_mode_{VALVE_NO_RESTORE}; - optional (*)()> state_f_; + TemplateLambda state_f_; bool assumed_state_{false}; bool optimistic_{false}; Trigger<> *open_trigger_; diff --git a/esphome/core/template_lambda.h b/esphome/core/template_lambda.h new file mode 100644 index 0000000000..7b8f4374aa --- /dev/null +++ b/esphome/core/template_lambda.h @@ -0,0 +1,51 @@ +#pragma once + +#include "esphome/core/optional.h" + +namespace esphome { + +/** Lightweight wrapper for template platform lambdas (stateless function pointers only). + * + * This optimizes template platforms by storing only a function pointer (4 bytes on ESP32) + * instead of std::function (16-32 bytes). + * + * IMPORTANT: This only supports stateless lambdas (no captures). The set_template() method + * is an internal API used by YAML codegen, not intended for external use. + * + * Lambdas must return optional to support the pattern: + * return {}; // Don't publish a value + * return 42.0; // Publish this value + * + * operator() returns optional, returning nullopt when no lambda is set (nullptr check). + * + * @tparam T The return type (e.g., float for sensor values) + * @tparam Args Optional arguments for the lambda + */ +template class TemplateLambda { + public: + TemplateLambda() : f_(nullptr) {} + + /** Set the lambda function pointer. + * INTERNAL API: Only for use by YAML codegen. + * Only stateless lambdas (no captures) are supported. + */ + void set(optional (*f)(Args...)) { this->f_ = f; } + + /** Check if a lambda is set */ + bool has_value() const { return this->f_ != nullptr; } + + /** Call the lambda, returning nullopt if no lambda is set */ + optional operator()(Args &&...args) { + if (this->f_ == nullptr) + return nullopt; + return this->f_(std::forward(args)...); + } + + /** Alias for operator() for compatibility */ + optional call(Args &&...args) { return (*this)(std::forward(args)...); } + + protected: + optional (*f_)(Args...); // Function pointer (4 bytes on ESP32) +}; + +} // namespace esphome diff --git a/tests/components/template/common-base.yaml b/tests/components/template/common-base.yaml index b873af5207..f101eea942 100644 --- a/tests/components/template/common-base.yaml +++ b/tests/components/template/common-base.yaml @@ -9,6 +9,13 @@ esphome: id: template_sens state: !lambda "return 42.0;" + # Test C++ API: set_template() with stateless lambda (no captures) + # NOTE: set_template() is not intended to be a public API, but we test it to ensure it doesn't break. + - lambda: |- + id(template_sens).set_template([]() -> esphome::optional { + return 123.0f; + }); + - datetime.date.set: id: test_date date: @@ -215,6 +222,7 @@ cover: number: - platform: template + id: template_number name: "Template number" optimistic: true min_value: 0 diff --git a/tests/integration/fixtures/runtime_stats.yaml b/tests/integration/fixtures/runtime_stats.yaml index aad1c275fb..fd34cdb939 100644 --- a/tests/integration/fixtures/runtime_stats.yaml +++ b/tests/integration/fixtures/runtime_stats.yaml @@ -32,6 +32,7 @@ switch: name: "Test Switch" id: test_switch optimistic: true + lambda: return false; interval: - interval: 0.5s From bd87e56bc7bbdb2a1d7178536bd9f1c5b9232d45 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 29 Oct 2025 21:14:03 -0500 Subject: [PATCH 315/526] [e131] Replace std::set with std::vector to reduce flash usage (#11598) --- esphome/components/e131/e131.cpp | 13 +++++++++---- esphome/components/e131/e131.h | 4 +--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/esphome/components/e131/e131.cpp b/esphome/components/e131/e131.cpp index d18d945cec..c10c88faf2 100644 --- a/esphome/components/e131/e131.cpp +++ b/esphome/components/e131/e131.cpp @@ -3,6 +3,8 @@ #include "e131_addressable_light_effect.h" #include "esphome/core/log.h" +#include + namespace esphome { namespace e131 { @@ -76,14 +78,14 @@ void E131Component::loop() { } void E131Component::add_effect(E131AddressableLightEffect *light_effect) { - if (light_effects_.count(light_effect)) { + if (std::find(light_effects_.begin(), light_effects_.end(), light_effect) != light_effects_.end()) { return; } ESP_LOGD(TAG, "Registering '%s' for universes %d-%d.", light_effect->get_name(), light_effect->get_first_universe(), light_effect->get_last_universe()); - light_effects_.insert(light_effect); + light_effects_.push_back(light_effect); for (auto universe = light_effect->get_first_universe(); universe <= light_effect->get_last_universe(); ++universe) { join_(universe); @@ -91,14 +93,17 @@ void E131Component::add_effect(E131AddressableLightEffect *light_effect) { } void E131Component::remove_effect(E131AddressableLightEffect *light_effect) { - if (!light_effects_.count(light_effect)) { + auto it = std::find(light_effects_.begin(), light_effects_.end(), light_effect); + if (it == light_effects_.end()) { return; } ESP_LOGD(TAG, "Unregistering '%s' for universes %d-%d.", light_effect->get_name(), light_effect->get_first_universe(), light_effect->get_last_universe()); - light_effects_.erase(light_effect); + // Swap with last element and pop for O(1) removal (order doesn't matter) + *it = light_effects_.back(); + light_effects_.pop_back(); for (auto universe = light_effect->get_first_universe(); universe <= light_effect->get_last_universe(); ++universe) { leave_(universe); diff --git a/esphome/components/e131/e131.h b/esphome/components/e131/e131.h index d0e38fa98c..831138a545 100644 --- a/esphome/components/e131/e131.h +++ b/esphome/components/e131/e131.h @@ -7,7 +7,6 @@ #include #include #include -#include #include namespace esphome { @@ -47,9 +46,8 @@ class E131Component : public esphome::Component { E131ListenMethod listen_method_{E131_MULTICAST}; std::unique_ptr socket_; - std::set light_effects_; + std::vector light_effects_; std::map universe_consumers_; - std::map universe_packets_; }; } // namespace e131 From 077cce9848303422581a0616b875d53044369a8c Mon Sep 17 00:00:00 2001 From: Markus <974709+Links2004@users.noreply.github.com> Date: Thu, 30 Oct 2025 16:08:08 +0100 Subject: [PATCH 316/526] [core] .local addresses are only resolvable if mDNS is enabled (#11508) --- esphome/__main__.py | 16 +++++++++++++--- tests/unit_tests/test_main.py | 6 +++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 26e5ae7424..b0c081a34f 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -207,14 +207,14 @@ def choose_upload_log_host( if has_mqtt_logging(): resolved.append("MQTT") - if has_api() and has_non_ip_address(): + if has_api() and has_non_ip_address() and has_resolvable_address(): resolved.extend(_resolve_with_cache(CORE.address, purpose)) elif purpose == Purpose.UPLOADING: if has_ota() and has_mqtt_ip_lookup(): resolved.append("MQTTIP") - if has_ota() and has_non_ip_address(): + if has_ota() and has_non_ip_address() and has_resolvable_address(): resolved.extend(_resolve_with_cache(CORE.address, purpose)) else: resolved.append(device) @@ -318,7 +318,17 @@ def has_resolvable_address() -> bool: """Check if CORE.address is resolvable (via mDNS, DNS, or is an IP address).""" # Any address (IP, mDNS hostname, or regular DNS hostname) is resolvable # The resolve_ip_address() function in helpers.py handles all types via AsyncResolver - return CORE.address is not None + if CORE.address is None: + return False + + if has_ip_address(): + return True + + if has_mdns(): + return True + + # .local mDNS hostnames are only resolvable if mDNS is enabled + return not CORE.address.endswith(".local") def mqtt_get_ip(config: ConfigType, username: str, password: str, client_id: str): diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index 9119c88502..9e5f399381 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -744,7 +744,7 @@ def test_choose_upload_log_host_ota_local_all_options() -> None: check_default=None, purpose=Purpose.UPLOADING, ) - assert result == ["MQTTIP", "test.local"] + assert result == ["MQTTIP"] @pytest.mark.usefixtures("mock_serial_ports") @@ -794,7 +794,7 @@ def test_choose_upload_log_host_ota_local_all_options_logging() -> None: check_default=None, purpose=Purpose.LOGGING, ) - assert result == ["MQTTIP", "MQTT", "test.local"] + assert result == ["MQTTIP", "MQTT"] @pytest.mark.usefixtures("mock_no_mqtt_logging") @@ -1564,7 +1564,7 @@ def test_has_resolvable_address() -> None: setup_core( config={CONF_MDNS: {CONF_DISABLED: True}}, address="esphome-device.local" ) - assert has_resolvable_address() is True + assert has_resolvable_address() is False # Test with mDNS disabled and regular DNS hostname (resolvable) setup_core(config={CONF_MDNS: {CONF_DISABLED: True}}, address="device.example.com") From fd64585f99fe47e749606823231020915d6fa75e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 16:50:06 -0500 Subject: [PATCH 317/526] Bump github/codeql-action from 4.31.0 to 4.31.2 (#11626) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6b940eed8a..ab938b3436 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 + uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -86,6 +86,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 + uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: category: "/language:${{matrix.language}}" From 6d0527ff2ad406603067a939d0b3f703db33fc0c Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Fri, 31 Oct 2025 20:04:55 +0100 Subject: [PATCH 318/526] [substitutions] fix jinja parsing strings that look like sets as sets (#11611) --- esphome/components/substitutions/jinja.py | 10 ++++++++-- .../fixtures/substitutions/00-simple_var.approved.yaml | 1 + .../fixtures/substitutions/00-simple_var.input.yaml | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/esphome/components/substitutions/jinja.py b/esphome/components/substitutions/jinja.py index cb3c6dfac5..fb9f843da2 100644 --- a/esphome/components/substitutions/jinja.py +++ b/esphome/components/substitutions/jinja.py @@ -138,6 +138,7 @@ def _concat_nodes_override(values: Iterator[Any]) -> Any: values = chain(head, values) raw = "".join([str(v) for v in values]) + result = None try: # Attempt to parse the concatenated string into a Python literal. # This allows expressions like "1 + 2" to be evaluated to the integer 3. @@ -145,11 +146,16 @@ def _concat_nodes_override(values: Iterator[Any]) -> Any: # fall back to returning the raw string. This is consistent with # Home Assistant's behavior when evaluating templates result = literal_eval(raw) + except (ValueError, SyntaxError, MemoryError, TypeError): + pass + else: + if isinstance(result, set): + # Sets are not supported, return raw string + return raw + if not isinstance(result, str): return result - except (ValueError, SyntaxError, MemoryError, TypeError): - pass return raw diff --git a/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml b/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml index 795a788f62..6f3bae1ac4 100644 --- a/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml +++ b/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml @@ -33,3 +33,4 @@ test_list: {{{ "x", "79"}, { "y", "82"}}} - '{{{"AA"}}}' - '"HELLO"' + - '{ 79, 82 }' diff --git a/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml b/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml index 722e116d36..306119b753 100644 --- a/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml +++ b/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml @@ -34,3 +34,4 @@ test_list: {{{ "x", "${ position.x }"}, { "y", "${ position.y }"}}} - ${ '{{{"AA"}}}' } - ${ '"HELLO"' } + - '{ ${position.x}, ${position.y} }' From 292abd1187f8de0b4941e26974e05581177abfe0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 19:46:50 +0000 Subject: [PATCH 319/526] Bump ruff from 0.14.2 to 0.14.3 (#11633) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston --- .pre-commit-config.yaml | 2 +- requirements_test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f7e4a688e0..5356bffd96 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ ci: repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.14.2 + rev: v0.14.3 hooks: # Run the linter. - id: ruff diff --git a/requirements_test.txt b/requirements_test.txt index a11992b0fd..11367172b1 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ pylint==4.0.2 flake8==7.3.0 # also change in .pre-commit-config.yaml when updating -ruff==0.14.2 # also change in .pre-commit-config.yaml when updating +ruff==0.14.3 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating pre-commit From 30f2a4395fd737bb723c1f82a56ec6f0ba4e41a0 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sat, 1 Nov 2025 10:08:28 +1000 Subject: [PATCH 320/526] [image] Catch and report svg load errors (#11619) --- esphome/components/image/__init__.py | 31 +++++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index f880b5f736..bf25a7cd92 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -671,18 +671,33 @@ async def write_image(config, all_frames=False): resize = config.get(CONF_RESIZE) if is_svg_file(path): # Local import so use of non-SVG files needn't require cairosvg installed + from pyexpat import ExpatError + from xml.etree.ElementTree import ParseError + from cairosvg import svg2png + from cairosvg.helpers import PointError if not resize: resize = (None, None) - with open(path, "rb") as file: - image = svg2png( - file_obj=file, - output_width=resize[0], - output_height=resize[1], - ) - image = Image.open(io.BytesIO(image)) - width, height = image.size + try: + with open(path, "rb") as file: + image = svg2png( + file_obj=file, + output_width=resize[0], + output_height=resize[1], + ) + image = Image.open(io.BytesIO(image)) + width, height = image.size + except ( + ValueError, + ParseError, + IndexError, + ExpatError, + AttributeError, + TypeError, + PointError, + ) as e: + raise core.EsphomeError(f"Could not load SVG image {path}: {e}") from e else: image = Image.open(path) width, height = image.size From d9d2d2f6b936369324ce271a509efa88e48aff1c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sat, 1 Nov 2025 14:17:23 +1000 Subject: [PATCH 321/526] [automations] Update error message (#11640) --- esphome/automation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/automation.py b/esphome/automation.py index cfe0af1b59..99be12451e 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -182,7 +182,7 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): value = cv.Schema([extra_validators])(value) if single: if len(value) != 1: - raise cv.Invalid("Cannot have more than 1 automation for templates") + raise cv.Invalid("This trigger allows only a single automation") return value[0] return value From 5a5894eaa3049a174f44c9af185bcd3c080cae5d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 1 Nov 2025 01:05:26 -0500 Subject: [PATCH 322/526] [ruff] Remove deprecated UP038 rule from ignore list (#11646) --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 49598d434d..d6aa584237 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -133,7 +133,6 @@ ignore = [ "PLW1641", # Object does not implement `__hash__` method "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target - "UP038", # https://github.com/astral-sh/ruff/issues/7871 https://github.com/astral-sh/ruff/pull/16681 ] [tool.ruff.lint.isort] From 8df5a3a6308a1b01c0a4137a7b68f0769d55e2a5 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sat, 1 Nov 2025 16:27:28 +1000 Subject: [PATCH 323/526] [lvgl] Trigger improvements and additions (#11628) --- esphome/components/lvgl/__init__.py | 52 +++++++++++++---------- esphome/components/lvgl/automation.py | 3 +- esphome/components/lvgl/defines.py | 2 + esphome/components/lvgl/lvgl_esphome.cpp | 33 ++++++++++---- esphome/components/lvgl/lvgl_esphome.h | 27 +++++++----- esphome/components/lvgl/types.py | 6 ++- tests/components/lvgl/test.esp32-idf.yaml | 8 ++++ 7 files changed, 86 insertions(+), 45 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 5af61300da..aa6935c5fc 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -58,7 +58,7 @@ from .types import ( FontEngine, IdleTrigger, ObjUpdateAction, - PauseTrigger, + PlainTrigger, lv_font_t, lv_group_t, lv_style_t, @@ -151,6 +151,13 @@ for w_type in WIDGET_TYPES.values(): create_modify_schema(w_type), )(update_to_code) +SIMPLE_TRIGGERS = ( + df.CONF_ON_PAUSE, + df.CONF_ON_RESUME, + df.CONF_ON_DRAW_START, + df.CONF_ON_DRAW_END, +) + def as_macro(macro, value): if value is None: @@ -244,9 +251,9 @@ def final_validation(configs): for w in refreshed_widgets: path = global_config.get_path_for_id(w) widget_conf = global_config.get_config_for_path(path[:-1]) - if not any(isinstance(v, Lambda) for v in widget_conf.values()): + if not any(isinstance(v, (Lambda, dict)) for v in widget_conf.values()): raise cv.Invalid( - f"Widget '{w}' does not have any templated properties to refresh", + f"Widget '{w}' does not have any dynamic properties to refresh", ) @@ -366,16 +373,16 @@ async def to_code(configs): conf[CONF_TRIGGER_ID], lv_component, templ ) await build_automation(idle_trigger, [], conf) - for conf in config.get(df.CONF_ON_PAUSE, ()): - pause_trigger = cg.new_Pvariable( - conf[CONF_TRIGGER_ID], lv_component, True - ) - await build_automation(pause_trigger, [], conf) - for conf in config.get(df.CONF_ON_RESUME, ()): - resume_trigger = cg.new_Pvariable( - conf[CONF_TRIGGER_ID], lv_component, False - ) - await build_automation(resume_trigger, [], conf) + for trigger_name in SIMPLE_TRIGGERS: + if conf := config.get(trigger_name): + trigger_var = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) + await build_automation(trigger_var, [], conf) + cg.add( + getattr( + lv_component, + f"set_{trigger_name.removeprefix('on_')}_trigger", + )(trigger_var) + ) await add_on_boot_triggers(config.get(CONF_ON_BOOT, ())) # This must be done after all widgets are created @@ -443,16 +450,15 @@ LVGL_SCHEMA = cv.All( ), } ), - cv.Optional(df.CONF_ON_PAUSE): validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger), - } - ), - cv.Optional(df.CONF_ON_RESUME): validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger), - } - ), + **{ + cv.Optional(x): validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PlainTrigger), + }, + single=True, + ) + for x in SIMPLE_TRIGGERS + }, cv.Exclusive(df.CONF_WIDGETS, CONF_PAGES): cv.ensure_list( WIDGET_SCHEMA ), diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index fc70b0f682..593c8c67bb 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -400,7 +400,8 @@ async def obj_refresh_to_code(config, action_id, template_arg, args): # must pass all widget-specific options here, even if not templated, but only do so if at least one is # templated. First filter out common style properties. config = {k: v for k, v in widget.config.items() if k not in ALL_STYLES} - if any(isinstance(v, Lambda) for v in config.values()): + # Check if v is a Lambda or a dict, implying it is dynamic + if any(isinstance(v, (Lambda, dict)) for v in config.values()): await widget.type.to_code(widget, config) if ( widget.type.w_type.value_property is not None diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 7fbb6de071..3241ba9c3f 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -483,6 +483,8 @@ CONF_MSGBOXES = "msgboxes" CONF_OBJ = "obj" CONF_ONE_CHECKED = "one_checked" CONF_ONE_LINE = "one_line" +CONF_ON_DRAW_START = "on_draw_start" +CONF_ON_DRAW_END = "on_draw_end" CONF_ON_PAUSE = "on_pause" CONF_ON_RESUME = "on_resume" CONF_ON_SELECT = "on_select" diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 7a32691b53..947342089c 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -82,6 +82,18 @@ static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { area->y2 = (area->y2 + draw_rounding) / draw_rounding * draw_rounding - 1; } +void LvglComponent::monitor_cb(lv_disp_drv_t *disp_drv, uint32_t time, uint32_t px) { + ESP_LOGVV(TAG, "Draw end: %" PRIu32 " pixels in %" PRIu32 " ms", px, time); + auto *comp = static_cast(disp_drv->user_data); + comp->draw_end_(); +} + +void LvglComponent::render_start_cb(lv_disp_drv_t *disp_drv) { + ESP_LOGVV(TAG, "Draw start"); + auto *comp = static_cast(disp_drv->user_data); + comp->draw_start_(); +} + lv_event_code_t lv_api_event; // NOLINT lv_event_code_t lv_update_event; // NOLINT void LvglComponent::dump_config() { @@ -101,7 +113,10 @@ void LvglComponent::set_paused(bool paused, bool show_snow) { lv_disp_trig_activity(this->disp_); // resets the inactivity time lv_obj_invalidate(lv_scr_act()); } - this->pause_callbacks_.call(paused); + if (paused && this->pause_callback_ != nullptr) + this->pause_callback_->trigger(); + if (!paused && this->resume_callback_ != nullptr) + this->resume_callback_->trigger(); } void LvglComponent::esphome_lvgl_init() { @@ -225,13 +240,6 @@ IdleTrigger::IdleTrigger(LvglComponent *parent, TemplatableValue timeo }); } -PauseTrigger::PauseTrigger(LvglComponent *parent, TemplatableValue paused) : paused_(std::move(paused)) { - parent->add_on_pause_callback([this](bool pausing) { - if (this->paused_.value() == pausing) - this->trigger(); - }); -} - #ifdef USE_LVGL_TOUCHSCREEN LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent) { this->set_parent(parent); @@ -474,6 +482,12 @@ void LvglComponent::setup() { return; } } + if (this->draw_start_callback_ != nullptr) { + this->disp_drv_.render_start_cb = render_start_cb; + } + if (this->draw_end_callback_ != nullptr) { + this->disp_drv_.monitor_cb = monitor_cb; + } #if LV_USE_LOG lv_log_register_print_cb([](const char *buf) { auto next = strchr(buf, ')'); @@ -502,8 +516,9 @@ void LvglComponent::loop() { if (this->paused_) { if (this->show_snow_) this->write_random_(); + } else { + lv_timer_handler_run_in_period(5); } - lv_timer_handler_run_in_period(5); } #ifdef USE_LVGL_ANIMIMG diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index d3dc8fac5a..ea58fdb85b 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -171,7 +171,9 @@ class LvglComponent : public PollingComponent { void add_on_idle_callback(std::function &&callback) { this->idle_callbacks_.add(std::move(callback)); } - void add_on_pause_callback(std::function &&callback) { this->pause_callbacks_.add(std::move(callback)); } + + static void monitor_cb(lv_disp_drv_t *disp_drv, uint32_t time, uint32_t px); + static void render_start_cb(lv_disp_drv_t *disp_drv); void dump_config() override; bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; } lv_disp_t *get_disp() { return this->disp_; } @@ -213,12 +215,20 @@ class LvglComponent : public PollingComponent { size_t draw_rounding{2}; display::DisplayRotation rotation{display::DISPLAY_ROTATION_0_DEGREES}; + void set_pause_trigger(Trigger<> *trigger) { this->pause_callback_ = trigger; } + void set_resume_trigger(Trigger<> *trigger) { this->resume_callback_ = trigger; } + void set_draw_start_trigger(Trigger<> *trigger) { this->draw_start_callback_ = trigger; } + void set_draw_end_trigger(Trigger<> *trigger) { this->draw_end_callback_ = trigger; } protected: + // these functions are never called unless the callbacks are non-null since the + // LVGL callbacks that call them are not set unless the start/end callbacks are non-null + void draw_start_() const { this->draw_start_callback_->trigger(); } + void draw_end_() const { this->draw_end_callback_->trigger(); } + void write_random_(); void draw_buffer_(const lv_area_t *area, lv_color_t *ptr); void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p); - std::vector displays_{}; size_t buffer_frac_{1}; bool full_refresh_{}; @@ -235,7 +245,10 @@ class LvglComponent : public PollingComponent { std::map focus_marks_{}; CallbackManager idle_callbacks_{}; - CallbackManager pause_callbacks_{}; + Trigger<> *pause_callback_{}; + Trigger<> *resume_callback_{}; + Trigger<> *draw_start_callback_{}; + Trigger<> *draw_end_callback_{}; lv_color_t *rotate_buf_{}; }; @@ -248,14 +261,6 @@ class IdleTrigger : public Trigger<> { bool is_idle_{}; }; -class PauseTrigger : public Trigger<> { - public: - explicit PauseTrigger(LvglComponent *parent, TemplatableValue paused); - - protected: - TemplatableValue paused_; -}; - template class LvglAction : public Action, public Parented { public: explicit LvglAction(std::function &&lamb) : action_(std::move(lamb)) {} diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index c19c89401a..9955b530aa 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -3,6 +3,7 @@ import sys from esphome import automation, codegen as cg from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_TEXT, CONF_VALUE from esphome.cpp_generator import MockObj, MockObjClass +from esphome.cpp_types import esphome_ns from .defines import lvgl_ns from .lvcode import lv_expr @@ -42,8 +43,11 @@ lv_event_code_t = cg.global_ns.enum("lv_event_code_t") lv_indev_type_t = cg.global_ns.enum("lv_indev_type_t") lv_key_t = cg.global_ns.enum("lv_key_t") FontEngine = lvgl_ns.class_("FontEngine") +PlainTrigger = esphome_ns.class_("Trigger<>", automation.Trigger.template()) +DrawEndTrigger = esphome_ns.class_( + "Trigger", automation.Trigger.template(cg.uint32, cg.uint32) +) IdleTrigger = lvgl_ns.class_("IdleTrigger", automation.Trigger.template()) -PauseTrigger = lvgl_ns.class_("PauseTrigger", automation.Trigger.template()) ObjUpdateAction = lvgl_ns.class_("ObjUpdateAction", automation.Action) LvglCondition = lvgl_ns.class_("LvglCondition", automation.Condition) LvglAction = lvgl_ns.class_("LvglAction", automation.Action) diff --git a/tests/components/lvgl/test.esp32-idf.yaml b/tests/components/lvgl/test.esp32-idf.yaml index 6170b0f4fb..2450d28eb8 100644 --- a/tests/components/lvgl/test.esp32-idf.yaml +++ b/tests/components/lvgl/test.esp32-idf.yaml @@ -68,5 +68,13 @@ lvgl: enter_button: pushbutton group: general initial_focus: lv_roller + on_draw_start: + - logger.log: draw started + on_draw_end: + - logger.log: draw ended + - lvgl.pause: + - component.update: tft_display + - delay: 60s + - lvgl.resume: <<: !include common.yaml From 4d1d37a9112a6b54467722531438349d672d0f5a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sat, 1 Nov 2025 16:37:07 +1000 Subject: [PATCH 324/526] [lvgl] Fix event for binary sensor (#11636) --- esphome/components/lvgl/binary_sensor/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/lvgl/binary_sensor/__init__.py b/esphome/components/lvgl/binary_sensor/__init__.py index ffbdc977b2..f9df7d23fa 100644 --- a/esphome/components/lvgl/binary_sensor/__init__.py +++ b/esphome/components/lvgl/binary_sensor/__init__.py @@ -31,7 +31,7 @@ async def to_code(config): lvgl_static.add_event_cb( widget.obj, await pressed_ctx.get_lambda(), - LV_EVENT.PRESSING, + LV_EVENT.PRESSED, LV_EVENT.RELEASED, ) ) From 0b4d445794f1d804b072d73d9ae0952139a48524 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sat, 1 Nov 2025 16:45:42 +1000 Subject: [PATCH 325/526] [sdl] Fix keymappings (#11635) --- esphome/components/sdl/binary_sensor.py | 485 ++++++++++++------------ tests/components/sdl/common.yaml | 6 +- 2 files changed, 253 insertions(+), 238 deletions(-) diff --git a/esphome/components/sdl/binary_sensor.py b/esphome/components/sdl/binary_sensor.py index 3ea6c2d218..e19a488800 100644 --- a/esphome/components/sdl/binary_sensor.py +++ b/esphome/components/sdl/binary_sensor.py @@ -12,241 +12,256 @@ CODEOWNERS = ["@bdm310"] STATE_ARG = "state" -SDL_KEYMAP = { - "SDLK_UNKNOWN": 0, - "SDLK_FIRST": 0, - "SDLK_BACKSPACE": 8, - "SDLK_TAB": 9, - "SDLK_CLEAR": 12, - "SDLK_RETURN": 13, - "SDLK_PAUSE": 19, - "SDLK_ESCAPE": 27, - "SDLK_SPACE": 32, - "SDLK_EXCLAIM": 33, - "SDLK_QUOTEDBL": 34, - "SDLK_HASH": 35, - "SDLK_DOLLAR": 36, - "SDLK_AMPERSAND": 38, - "SDLK_QUOTE": 39, - "SDLK_LEFTPAREN": 40, - "SDLK_RIGHTPAREN": 41, - "SDLK_ASTERISK": 42, - "SDLK_PLUS": 43, - "SDLK_COMMA": 44, - "SDLK_MINUS": 45, - "SDLK_PERIOD": 46, - "SDLK_SLASH": 47, - "SDLK_0": 48, - "SDLK_1": 49, - "SDLK_2": 50, - "SDLK_3": 51, - "SDLK_4": 52, - "SDLK_5": 53, - "SDLK_6": 54, - "SDLK_7": 55, - "SDLK_8": 56, - "SDLK_9": 57, - "SDLK_COLON": 58, - "SDLK_SEMICOLON": 59, - "SDLK_LESS": 60, - "SDLK_EQUALS": 61, - "SDLK_GREATER": 62, - "SDLK_QUESTION": 63, - "SDLK_AT": 64, - "SDLK_LEFTBRACKET": 91, - "SDLK_BACKSLASH": 92, - "SDLK_RIGHTBRACKET": 93, - "SDLK_CARET": 94, - "SDLK_UNDERSCORE": 95, - "SDLK_BACKQUOTE": 96, - "SDLK_a": 97, - "SDLK_b": 98, - "SDLK_c": 99, - "SDLK_d": 100, - "SDLK_e": 101, - "SDLK_f": 102, - "SDLK_g": 103, - "SDLK_h": 104, - "SDLK_i": 105, - "SDLK_j": 106, - "SDLK_k": 107, - "SDLK_l": 108, - "SDLK_m": 109, - "SDLK_n": 110, - "SDLK_o": 111, - "SDLK_p": 112, - "SDLK_q": 113, - "SDLK_r": 114, - "SDLK_s": 115, - "SDLK_t": 116, - "SDLK_u": 117, - "SDLK_v": 118, - "SDLK_w": 119, - "SDLK_x": 120, - "SDLK_y": 121, - "SDLK_z": 122, - "SDLK_DELETE": 127, - "SDLK_WORLD_0": 160, - "SDLK_WORLD_1": 161, - "SDLK_WORLD_2": 162, - "SDLK_WORLD_3": 163, - "SDLK_WORLD_4": 164, - "SDLK_WORLD_5": 165, - "SDLK_WORLD_6": 166, - "SDLK_WORLD_7": 167, - "SDLK_WORLD_8": 168, - "SDLK_WORLD_9": 169, - "SDLK_WORLD_10": 170, - "SDLK_WORLD_11": 171, - "SDLK_WORLD_12": 172, - "SDLK_WORLD_13": 173, - "SDLK_WORLD_14": 174, - "SDLK_WORLD_15": 175, - "SDLK_WORLD_16": 176, - "SDLK_WORLD_17": 177, - "SDLK_WORLD_18": 178, - "SDLK_WORLD_19": 179, - "SDLK_WORLD_20": 180, - "SDLK_WORLD_21": 181, - "SDLK_WORLD_22": 182, - "SDLK_WORLD_23": 183, - "SDLK_WORLD_24": 184, - "SDLK_WORLD_25": 185, - "SDLK_WORLD_26": 186, - "SDLK_WORLD_27": 187, - "SDLK_WORLD_28": 188, - "SDLK_WORLD_29": 189, - "SDLK_WORLD_30": 190, - "SDLK_WORLD_31": 191, - "SDLK_WORLD_32": 192, - "SDLK_WORLD_33": 193, - "SDLK_WORLD_34": 194, - "SDLK_WORLD_35": 195, - "SDLK_WORLD_36": 196, - "SDLK_WORLD_37": 197, - "SDLK_WORLD_38": 198, - "SDLK_WORLD_39": 199, - "SDLK_WORLD_40": 200, - "SDLK_WORLD_41": 201, - "SDLK_WORLD_42": 202, - "SDLK_WORLD_43": 203, - "SDLK_WORLD_44": 204, - "SDLK_WORLD_45": 205, - "SDLK_WORLD_46": 206, - "SDLK_WORLD_47": 207, - "SDLK_WORLD_48": 208, - "SDLK_WORLD_49": 209, - "SDLK_WORLD_50": 210, - "SDLK_WORLD_51": 211, - "SDLK_WORLD_52": 212, - "SDLK_WORLD_53": 213, - "SDLK_WORLD_54": 214, - "SDLK_WORLD_55": 215, - "SDLK_WORLD_56": 216, - "SDLK_WORLD_57": 217, - "SDLK_WORLD_58": 218, - "SDLK_WORLD_59": 219, - "SDLK_WORLD_60": 220, - "SDLK_WORLD_61": 221, - "SDLK_WORLD_62": 222, - "SDLK_WORLD_63": 223, - "SDLK_WORLD_64": 224, - "SDLK_WORLD_65": 225, - "SDLK_WORLD_66": 226, - "SDLK_WORLD_67": 227, - "SDLK_WORLD_68": 228, - "SDLK_WORLD_69": 229, - "SDLK_WORLD_70": 230, - "SDLK_WORLD_71": 231, - "SDLK_WORLD_72": 232, - "SDLK_WORLD_73": 233, - "SDLK_WORLD_74": 234, - "SDLK_WORLD_75": 235, - "SDLK_WORLD_76": 236, - "SDLK_WORLD_77": 237, - "SDLK_WORLD_78": 238, - "SDLK_WORLD_79": 239, - "SDLK_WORLD_80": 240, - "SDLK_WORLD_81": 241, - "SDLK_WORLD_82": 242, - "SDLK_WORLD_83": 243, - "SDLK_WORLD_84": 244, - "SDLK_WORLD_85": 245, - "SDLK_WORLD_86": 246, - "SDLK_WORLD_87": 247, - "SDLK_WORLD_88": 248, - "SDLK_WORLD_89": 249, - "SDLK_WORLD_90": 250, - "SDLK_WORLD_91": 251, - "SDLK_WORLD_92": 252, - "SDLK_WORLD_93": 253, - "SDLK_WORLD_94": 254, - "SDLK_WORLD_95": 255, - "SDLK_KP0": 256, - "SDLK_KP1": 257, - "SDLK_KP2": 258, - "SDLK_KP3": 259, - "SDLK_KP4": 260, - "SDLK_KP5": 261, - "SDLK_KP6": 262, - "SDLK_KP7": 263, - "SDLK_KP8": 264, - "SDLK_KP9": 265, - "SDLK_KP_PERIOD": 266, - "SDLK_KP_DIVIDE": 267, - "SDLK_KP_MULTIPLY": 268, - "SDLK_KP_MINUS": 269, - "SDLK_KP_PLUS": 270, - "SDLK_KP_ENTER": 271, - "SDLK_KP_EQUALS": 272, - "SDLK_UP": 273, - "SDLK_DOWN": 274, - "SDLK_RIGHT": 275, - "SDLK_LEFT": 276, - "SDLK_INSERT": 277, - "SDLK_HOME": 278, - "SDLK_END": 279, - "SDLK_PAGEUP": 280, - "SDLK_PAGEDOWN": 281, - "SDLK_F1": 282, - "SDLK_F2": 283, - "SDLK_F3": 284, - "SDLK_F4": 285, - "SDLK_F5": 286, - "SDLK_F6": 287, - "SDLK_F7": 288, - "SDLK_F8": 289, - "SDLK_F9": 290, - "SDLK_F10": 291, - "SDLK_F11": 292, - "SDLK_F12": 293, - "SDLK_F13": 294, - "SDLK_F14": 295, - "SDLK_F15": 296, - "SDLK_NUMLOCK": 300, - "SDLK_CAPSLOCK": 301, - "SDLK_SCROLLOCK": 302, - "SDLK_RSHIFT": 303, - "SDLK_LSHIFT": 304, - "SDLK_RCTRL": 305, - "SDLK_LCTRL": 306, - "SDLK_RALT": 307, - "SDLK_LALT": 308, - "SDLK_RMETA": 309, - "SDLK_LMETA": 310, - "SDLK_LSUPER": 311, - "SDLK_RSUPER": 312, - "SDLK_MODE": 313, - "SDLK_COMPOSE": 314, - "SDLK_HELP": 315, - "SDLK_PRINT": 316, - "SDLK_SYSREQ": 317, - "SDLK_BREAK": 318, - "SDLK_MENU": 319, - "SDLK_POWER": 320, - "SDLK_EURO": 321, - "SDLK_UNDO": 322, -} +SDL_KeyCode = cg.global_ns.enum("SDL_KeyCode") + +SDL_KEYS = ( + "SDLK_UNKNOWN", + "SDLK_RETURN", + "SDLK_ESCAPE", + "SDLK_BACKSPACE", + "SDLK_TAB", + "SDLK_SPACE", + "SDLK_EXCLAIM", + "SDLK_QUOTEDBL", + "SDLK_HASH", + "SDLK_PERCENT", + "SDLK_DOLLAR", + "SDLK_AMPERSAND", + "SDLK_QUOTE", + "SDLK_LEFTPAREN", + "SDLK_RIGHTPAREN", + "SDLK_ASTERISK", + "SDLK_PLUS", + "SDLK_COMMA", + "SDLK_MINUS", + "SDLK_PERIOD", + "SDLK_SLASH", + "SDLK_0", + "SDLK_1", + "SDLK_2", + "SDLK_3", + "SDLK_4", + "SDLK_5", + "SDLK_6", + "SDLK_7", + "SDLK_8", + "SDLK_9", + "SDLK_COLON", + "SDLK_SEMICOLON", + "SDLK_LESS", + "SDLK_EQUALS", + "SDLK_GREATER", + "SDLK_QUESTION", + "SDLK_AT", + "SDLK_LEFTBRACKET", + "SDLK_BACKSLASH", + "SDLK_RIGHTBRACKET", + "SDLK_CARET", + "SDLK_UNDERSCORE", + "SDLK_BACKQUOTE", + "SDLK_a", + "SDLK_b", + "SDLK_c", + "SDLK_d", + "SDLK_e", + "SDLK_f", + "SDLK_g", + "SDLK_h", + "SDLK_i", + "SDLK_j", + "SDLK_k", + "SDLK_l", + "SDLK_m", + "SDLK_n", + "SDLK_o", + "SDLK_p", + "SDLK_q", + "SDLK_r", + "SDLK_s", + "SDLK_t", + "SDLK_u", + "SDLK_v", + "SDLK_w", + "SDLK_x", + "SDLK_y", + "SDLK_z", + "SDLK_CAPSLOCK", + "SDLK_F1", + "SDLK_F2", + "SDLK_F3", + "SDLK_F4", + "SDLK_F5", + "SDLK_F6", + "SDLK_F7", + "SDLK_F8", + "SDLK_F9", + "SDLK_F10", + "SDLK_F11", + "SDLK_F12", + "SDLK_PRINTSCREEN", + "SDLK_SCROLLLOCK", + "SDLK_PAUSE", + "SDLK_INSERT", + "SDLK_HOME", + "SDLK_PAGEUP", + "SDLK_DELETE", + "SDLK_END", + "SDLK_PAGEDOWN", + "SDLK_RIGHT", + "SDLK_LEFT", + "SDLK_DOWN", + "SDLK_UP", + "SDLK_NUMLOCKCLEAR", + "SDLK_KP_DIVIDE", + "SDLK_KP_MULTIPLY", + "SDLK_KP_MINUS", + "SDLK_KP_PLUS", + "SDLK_KP_ENTER", + "SDLK_KP_1", + "SDLK_KP_2", + "SDLK_KP_3", + "SDLK_KP_4", + "SDLK_KP_5", + "SDLK_KP_6", + "SDLK_KP_7", + "SDLK_KP_8", + "SDLK_KP_9", + "SDLK_KP_0", + "SDLK_KP_PERIOD", + "SDLK_APPLICATION", + "SDLK_POWER", + "SDLK_KP_EQUALS", + "SDLK_F13", + "SDLK_F14", + "SDLK_F15", + "SDLK_F16", + "SDLK_F17", + "SDLK_F18", + "SDLK_F19", + "SDLK_F20", + "SDLK_F21", + "SDLK_F22", + "SDLK_F23", + "SDLK_F24", + "SDLK_EXECUTE", + "SDLK_HELP", + "SDLK_MENU", + "SDLK_SELECT", + "SDLK_STOP", + "SDLK_AGAIN", + "SDLK_UNDO", + "SDLK_CUT", + "SDLK_COPY", + "SDLK_PASTE", + "SDLK_FIND", + "SDLK_MUTE", + "SDLK_VOLUMEUP", + "SDLK_VOLUMEDOWN", + "SDLK_KP_COMMA", + "SDLK_KP_EQUALSAS400", + "SDLK_ALTERASE", + "SDLK_SYSREQ", + "SDLK_CANCEL", + "SDLK_CLEAR", + "SDLK_PRIOR", + "SDLK_RETURN2", + "SDLK_SEPARATOR", + "SDLK_OUT", + "SDLK_OPER", + "SDLK_CLEARAGAIN", + "SDLK_CRSEL", + "SDLK_EXSEL", + "SDLK_KP_00", + "SDLK_KP_000", + "SDLK_THOUSANDSSEPARATOR", + "SDLK_DECIMALSEPARATOR", + "SDLK_CURRENCYUNIT", + "SDLK_CURRENCYSUBUNIT", + "SDLK_KP_LEFTPAREN", + "SDLK_KP_RIGHTPAREN", + "SDLK_KP_LEFTBRACE", + "SDLK_KP_RIGHTBRACE", + "SDLK_KP_TAB", + "SDLK_KP_BACKSPACE", + "SDLK_KP_A", + "SDLK_KP_B", + "SDLK_KP_C", + "SDLK_KP_D", + "SDLK_KP_E", + "SDLK_KP_F", + "SDLK_KP_XOR", + "SDLK_KP_POWER", + "SDLK_KP_PERCENT", + "SDLK_KP_LESS", + "SDLK_KP_GREATER", + "SDLK_KP_AMPERSAND", + "SDLK_KP_DBLAMPERSAND", + "SDLK_KP_VERTICALBAR", + "SDLK_KP_DBLVERTICALBAR", + "SDLK_KP_COLON", + "SDLK_KP_HASH", + "SDLK_KP_SPACE", + "SDLK_KP_AT", + "SDLK_KP_EXCLAM", + "SDLK_KP_MEMSTORE", + "SDLK_KP_MEMRECALL", + "SDLK_KP_MEMCLEAR", + "SDLK_KP_MEMADD", + "SDLK_KP_MEMSUBTRACT", + "SDLK_KP_MEMMULTIPLY", + "SDLK_KP_MEMDIVIDE", + "SDLK_KP_PLUSMINUS", + "SDLK_KP_CLEAR", + "SDLK_KP_CLEARENTRY", + "SDLK_KP_BINARY", + "SDLK_KP_OCTAL", + "SDLK_KP_DECIMAL", + "SDLK_KP_HEXADECIMAL", + "SDLK_LCTRL", + "SDLK_LSHIFT", + "SDLK_LALT", + "SDLK_LGUI", + "SDLK_RCTRL", + "SDLK_RSHIFT", + "SDLK_RALT", + "SDLK_RGUI", + "SDLK_MODE", + "SDLK_AUDIONEXT", + "SDLK_AUDIOPREV", + "SDLK_AUDIOSTOP", + "SDLK_AUDIOPLAY", + "SDLK_AUDIOMUTE", + "SDLK_MEDIASELECT", + "SDLK_WWW", + "SDLK_MAIL", + "SDLK_CALCULATOR", + "SDLK_COMPUTER", + "SDLK_AC_SEARCH", + "SDLK_AC_HOME", + "SDLK_AC_BACK", + "SDLK_AC_FORWARD", + "SDLK_AC_STOP", + "SDLK_AC_REFRESH", + "SDLK_AC_BOOKMARKS", + "SDLK_BRIGHTNESSDOWN", + "SDLK_BRIGHTNESSUP", + "SDLK_DISPLAYSWITCH", + "SDLK_KBDILLUMTOGGLE", + "SDLK_KBDILLUMDOWN", + "SDLK_KBDILLUMUP", + "SDLK_EJECT", + "SDLK_SLEEP", + "SDLK_APP1", + "SDLK_APP2", + "SDLK_AUDIOREWIND", + "SDLK_AUDIOFASTFORWARD", + "SDLK_SOFTLEFT", + "SDLK_SOFTRIGHT", + "SDLK_CALL", + "SDLK_ENDCALL", +) + +SDL_KEYMAP = {key: getattr(SDL_KeyCode, key) for key in SDL_KEYS} CONFIG_SCHEMA = ( binary_sensor.binary_sensor_schema(BinarySensor) diff --git a/tests/components/sdl/common.yaml b/tests/components/sdl/common.yaml index 50fa4a5990..52991d595c 100644 --- a/tests/components/sdl/common.yaml +++ b/tests/components/sdl/common.yaml @@ -14,10 +14,10 @@ display: binary_sensor: - platform: sdl id: key_up - key: SDLK_a + key: SDLK_UP - platform: sdl id: key_down - key: SDLK_d + key: SDLK_DOWN - platform: sdl id: key_enter - key: SDLK_s + key: SDLK_RETURN From e28c15229811ed6731ab446cdf880f0929555ac9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 1 Nov 2025 04:48:58 -0500 Subject: [PATCH 326/526] [cpp_generator] Align isinstance() with codebase style (tuple vs PEP 604) (#11645) --- esphome/cpp_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index a2da424e5a..6f1af01a5b 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -350,7 +350,7 @@ def safe_exp(obj: SafeExpType) -> Expression: return IntLiteral(int(obj.total_seconds)) if isinstance(obj, TimePeriodMinutes): return IntLiteral(int(obj.total_minutes)) - if isinstance(obj, tuple | list): + if isinstance(obj, (tuple, list)): return ArrayInitializer(*[safe_exp(o) for o in obj]) if obj is bool: return bool_ From c662697ca77cca55e6b63a4515476f5404d5810f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 1 Nov 2025 11:18:10 -0500 Subject: [PATCH 327/526] [json] Fix component test compilation errors (#11647) --- tests/components/json/common.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/components/json/common.yaml b/tests/components/json/common.yaml index f4074e1172..c36c7f2a5a 100644 --- a/tests/components/json/common.yaml +++ b/tests/components/json/common.yaml @@ -14,12 +14,14 @@ interval: // Test parse_json bool parse_ok = esphome::json::parse_json(json_str, [](JsonObject root) { - if (root.containsKey("sensor") && root.containsKey("value")) { + if (root["sensor"].is() && root["value"].is()) { const char* sensor = root["sensor"]; float value = root["value"]; ESP_LOGD("test", "Parsed: sensor=%s, value=%.1f", sensor, value); + return true; } else { ESP_LOGD("test", "Parsed JSON missing required keys"); + return false; } }); ESP_LOGD("test", "Parse result (JSON syntax only): %s", parse_ok ? "success" : "failed"); From 55af8186294b900d52cd31eabd20407f30563286 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sat, 1 Nov 2025 17:18:38 +0100 Subject: [PATCH 328/526] [nrf52] fix compilation warning (#11656) --- esphome/components/zephyr/core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/zephyr/core.cpp b/esphome/components/zephyr/core.cpp index ad7a148cdb..365b6b8ed2 100644 --- a/esphome/components/zephyr/core.cpp +++ b/esphome/components/zephyr/core.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "esphome/core/hal.h" #include "esphome/core/helpers.h" From d25121a55c100da3aa3ddce112beac9e43f5b2e3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 1 Nov 2025 22:43:08 -0500 Subject: [PATCH 329/526] [core] Remove redundant fd bounds check in yield_with_select_() (#11666) --- esphome/core/application.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index c745aa0ae5..61cfcc7585 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -576,10 +576,11 @@ void Application::yield_with_select_(uint32_t delay_ms) { // Update fd_set if socket list has changed if (this->socket_fds_changed_) { FD_ZERO(&this->base_read_fds_); + // fd bounds are already validated in register_socket_fd() or guaranteed by platform design: + // - ESP32: LwIP guarantees fd < FD_SETSIZE by design (LWIP_SOCKET_OFFSET = FD_SETSIZE - CONFIG_LWIP_MAX_SOCKETS) + // - Other platforms: register_socket_fd() validates fd < FD_SETSIZE for (int fd : this->socket_fds_) { - if (fd >= 0 && fd < FD_SETSIZE) { - FD_SET(fd, &this->base_read_fds_); - } + FD_SET(fd, &this->base_read_fds_); } this->socket_fds_changed_ = false; } From 1fc3165b582a290f65b5be68a52ee45d75721ca5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 1 Nov 2025 22:43:39 -0500 Subject: [PATCH 330/526] [api] Remove unnecessary intermediate variable in frame helpers (#11668) --- esphome/components/api/api_frame_helper_noise.cpp | 3 +-- esphome/components/api/api_frame_helper_plaintext.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/esphome/components/api/api_frame_helper_noise.cpp b/esphome/components/api/api_frame_helper_noise.cpp index e952ea670b..633b07a7fa 100644 --- a/esphome/components/api/api_frame_helper_noise.cpp +++ b/esphome/components/api/api_frame_helper_noise.cpp @@ -434,8 +434,7 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, st return APIError::OK; } - std::vector *raw_buffer = buffer.get_buffer(); - uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer + uint8_t *buffer_data = buffer.get_buffer()->data(); this->reusable_iovs_.clear(); this->reusable_iovs_.reserve(packets.size()); diff --git a/esphome/components/api/api_frame_helper_plaintext.cpp b/esphome/components/api/api_frame_helper_plaintext.cpp index 471e6c5404..dcbd35aa32 100644 --- a/esphome/components/api/api_frame_helper_plaintext.cpp +++ b/esphome/components/api/api_frame_helper_plaintext.cpp @@ -230,8 +230,7 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer return APIError::OK; } - std::vector *raw_buffer = buffer.get_buffer(); - uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer + uint8_t *buffer_data = buffer.get_buffer()->data(); this->reusable_iovs_.clear(); this->reusable_iovs_.reserve(packets.size()); From edde2fc94c584cb07365c16c6fe7a9d0b20488a1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 08:18:17 -0600 Subject: [PATCH 331/526] Add basic tests for web_server_idf (#11659) --- tests/components/web_server_idf/common.yaml | 29 +++++++++++++++++++ .../web_server_idf/test.esp32-idf.yaml | 3 ++ 2 files changed, 32 insertions(+) create mode 100644 tests/components/web_server_idf/common.yaml create mode 100644 tests/components/web_server_idf/test.esp32-idf.yaml diff --git a/tests/components/web_server_idf/common.yaml b/tests/components/web_server_idf/common.yaml new file mode 100644 index 0000000000..b1885af266 --- /dev/null +++ b/tests/components/web_server_idf/common.yaml @@ -0,0 +1,29 @@ +esphome: + name: test-web-server-idf + +esp32: + board: esp32dev + framework: + type: esp-idf + +network: + +# Add some entities to test SSE event formatting +sensor: + - platform: template + name: "Test Sensor" + id: test_sensor + update_interval: 60s + lambda: "return 42.5;" + +binary_sensor: + - platform: template + name: "Test Binary Sensor" + id: test_binary_sensor + lambda: "return true;" + +switch: + - platform: template + name: "Test Switch" + id: test_switch + optimistic: true diff --git a/tests/components/web_server_idf/test.esp32-idf.yaml b/tests/components/web_server_idf/test.esp32-idf.yaml new file mode 100644 index 0000000000..c3b85178ef --- /dev/null +++ b/tests/components/web_server_idf/test.esp32-idf.yaml @@ -0,0 +1,3 @@ +<<: !include common.yaml + +web_server: From f6946c0b9aed3b15986079c16c98c0e51438311a Mon Sep 17 00:00:00 2001 From: Kjell Braden Date: Sun, 2 Nov 2025 22:08:45 +0100 Subject: [PATCH 332/526] add integration test for script re-entry argument issue (#11652) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../fixtures/action_concurrent_reentry.yaml | 105 ++++++++++++++++++ .../test_action_concurrent_reentry.py | 93 ++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 tests/integration/fixtures/action_concurrent_reentry.yaml create mode 100644 tests/integration/test_action_concurrent_reentry.py diff --git a/tests/integration/fixtures/action_concurrent_reentry.yaml b/tests/integration/fixtures/action_concurrent_reentry.yaml new file mode 100644 index 0000000000..68d36d1510 --- /dev/null +++ b/tests/integration/fixtures/action_concurrent_reentry.yaml @@ -0,0 +1,105 @@ +esphome: + name: action-concurrent-reentry + on_boot: + - priority: -100 + then: + - repeat: + count: 5 + then: + - lambda: id(handler_wait_until)->execute(id(global_counter)); + - lambda: id(handler_repeat)->execute(id(global_counter)); + - lambda: id(handler_while)->execute(id(global_counter)); + - lambda: id(handler_script_wait)->execute(id(global_counter)); + - delay: 50ms + - lambda: id(global_counter)++; + - delay: 50ms + +host: + +api: + +globals: + - id: global_counter + type: int + +script: + - id: handler_wait_until + + mode: parallel + + parameters: + arg: int + + then: + - wait_until: + condition: + lambda: return id(global_counter) == 5; + + - logger.log: + format: "AFTER wait_until ARG %d" + args: + - arg + + - id: handler_script_wait + + mode: parallel + + parameters: + arg: int + + then: + - script.wait: handler_wait_until + + - logger.log: + format: "AFTER script.wait ARG %d" + args: + - arg + + - id: handler_repeat + + mode: parallel + + parameters: + arg: int + + then: + - repeat: + count: 3 + then: + - logger.log: + format: "IN repeat %d ARG %d" + args: + - iteration + - arg + - delay: 100ms + + - logger.log: + format: "AFTER repeat ARG %d" + args: + - arg + + - id: handler_while + + mode: parallel + + parameters: + arg: int + + then: + - while: + condition: + lambda: return id(global_counter) != 5; + then: + - logger.log: + format: "IN while ARG %d" + args: + - arg + - delay: 100ms + + - logger.log: + format: "AFTER while ARG %d" + args: + - arg + +logger: + level: DEBUG diff --git a/tests/integration/test_action_concurrent_reentry.py b/tests/integration/test_action_concurrent_reentry.py new file mode 100644 index 0000000000..ba67e4c798 --- /dev/null +++ b/tests/integration/test_action_concurrent_reentry.py @@ -0,0 +1,93 @@ +"""Integration test for API conditional memory optimization with triggers and services.""" + +from __future__ import annotations + +import asyncio +import collections +import re + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.xfail(reason="https://github.com/esphome/issues/issues/6534") +@pytest.mark.asyncio +async def test_action_concurrent_reentry( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """ + This test runs a script in parallel with varying arguments and verifies if + each script keeps its original argument throughout its execution + """ + test_complete = asyncio.Event() + expected = {0, 1, 2, 3, 4} + + # Patterns to match in logs + after_wait_until_pattern = re.compile(r"AFTER wait_until ARG (\d+)") + after_script_wait_pattern = re.compile(r"AFTER script\.wait ARG (\d+)") + after_repeat_pattern = re.compile(r"AFTER repeat ARG (\d+)") + in_repeat_pattern = re.compile(r"IN repeat (\d+) ARG (\d+)") + after_while_pattern = re.compile(r"AFTER while ARG (\d+)") + in_while_pattern = re.compile(r"IN while ARG (\d+)") + + after_wait_until_args = [] + after_script_wait_args = [] + after_while_args = [] + in_while_args = [] + after_repeat_args = [] + in_repeat_args = collections.defaultdict(list) + + def check_output(line: str) -> None: + """Check log output for expected messages.""" + if test_complete.is_set(): + return + + if mo := after_wait_until_pattern.search(line): + after_wait_until_args.append(int(mo.group(1))) + elif mo := after_script_wait_pattern.search(line): + after_script_wait_args.append(int(mo.group(1))) + elif mo := in_while_pattern.search(line): + in_while_args.append(int(mo.group(1))) + elif mo := after_while_pattern.search(line): + after_while_args.append(int(mo.group(1))) + elif mo := in_repeat_pattern.search(line): + in_repeat_args[int(mo.group(1))].append(int(mo.group(2))) + elif mo := after_repeat_pattern.search(line): + after_repeat_args.append(int(mo.group(1))) + if len(after_repeat_args) == len(expected): + test_complete.set() + + # Run with log monitoring + async with ( + run_compiled(yaml_config, line_callback=check_output), + api_client_connected() as client, + ): + # Verify device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "action-concurrent-reentry" + + # Wait for tests to complete with timeout + try: + await asyncio.wait_for(test_complete.wait(), timeout=8.0) + except TimeoutError: + pytest.fail("test timed out") + + # order may change, but all args must be present + for args in in_repeat_args.values(): + assert set(args) == expected + assert set(in_repeat_args.keys()) == {0, 1, 2} + assert set(after_wait_until_args) == expected, after_wait_until_args + assert set(after_script_wait_args) == expected, after_script_wait_args + assert set(after_repeat_args) == expected, after_repeat_args + assert set(after_while_args) == expected, after_while_args + assert dict(collections.Counter(in_while_args)) == { + 0: 5, + 1: 4, + 2: 3, + 3: 2, + 4: 1, + }, in_while_args From 425c88ee9434f35fcb093488f08a5651c4df57cf Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 2 Nov 2025 23:06:13 +0100 Subject: [PATCH 333/526] [nextion] Send `auto_wake_on_touch` as part of startup commands on loop (#11670) --- esphome/components/nextion/nextion.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index fc152ece1e..d77af510d7 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -323,6 +323,8 @@ void Nextion::loop() { this->set_touch_sleep_timeout(this->touch_sleep_timeout_); } + this->set_auto_wake_on_touch(this->connection_state_.auto_wake_on_touch_); + this->connection_state_.ignore_is_setup_ = false; } From 338190abeca6cd7cd29ca6065065d549c490857e Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sun, 2 Nov 2025 19:11:02 -0300 Subject: [PATCH 334/526] ESP32 Pin loopTask to CORE 1 (#11669) --- esphome/components/esp32/core.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp index 3427c96e70..1c8f29fa95 100644 --- a/esphome/components/esp32/core.cpp +++ b/esphome/components/esp32/core.cpp @@ -96,7 +96,11 @@ void loop_task(void *pv_params) { extern "C" void app_main() { esp32::setup_preferences(); +#if CONFIG_FREERTOS_UNICORE xTaskCreate(loop_task, "loopTask", 8192, nullptr, 1, &loop_task_handle); +#else + xTaskCreatePinnedToCore(loop_task, "loopTask", 8192, nullptr, 1, &loop_task_handle, 1); +#endif } #endif // USE_ESP_IDF From 70ea3af578e1440fc49751dff420bc41f195af20 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sun, 2 Nov 2025 23:19:28 +0100 Subject: [PATCH 335/526] [nrf52,gpio] switch input gpio to polling mode (#11664) --- esphome/components/gpio/binary_sensor/__init__.py | 3 ++- esphome/components/zephyr/gpio.cpp | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/esphome/components/gpio/binary_sensor/__init__.py b/esphome/components/gpio/binary_sensor/__init__.py index ca4dc43e9c..3c2021d40e 100644 --- a/esphome/components/gpio/binary_sensor/__init__.py +++ b/esphome/components/gpio/binary_sensor/__init__.py @@ -39,6 +39,7 @@ CONFIG_SCHEMA = ( # due to hardware limitations or lack of reliable interrupt support. This ensures # stable operation on these platforms. Future maintainers should verify platform # capabilities before changing this default behavior. + # nrf52 has no gpio interrupts implemented yet cv.SplitDefault( CONF_USE_INTERRUPT, bk72xx=False, @@ -46,7 +47,7 @@ CONFIG_SCHEMA = ( esp8266=True, host=True, ln882x=False, - nrf52=True, + nrf52=False, rp2040=True, rtl87xx=False, ): cv.boolean, diff --git a/esphome/components/zephyr/gpio.cpp b/esphome/components/zephyr/gpio.cpp index 4b84910368..41b983535c 100644 --- a/esphome/components/zephyr/gpio.cpp +++ b/esphome/components/zephyr/gpio.cpp @@ -8,8 +8,8 @@ namespace zephyr { static const char *const TAG = "zephyr"; -static int flags_to_mode(gpio::Flags flags, bool inverted, bool value) { - int ret = 0; +static gpio_flags_t flags_to_mode(gpio::Flags flags, bool inverted, bool value) { + gpio_flags_t ret = 0; if (flags & gpio::FLAG_INPUT) { ret |= GPIO_INPUT; } @@ -79,7 +79,10 @@ void ZephyrGPIOPin::pin_mode(gpio::Flags flags) { if (nullptr == this->gpio_) { return; } - gpio_pin_configure(this->gpio_, this->pin_ % 32, flags_to_mode(flags, this->inverted_, this->value_)); + auto ret = gpio_pin_configure(this->gpio_, this->pin_ % 32, flags_to_mode(flags, this->inverted_, this->value_)); + if (ret != 0) { + ESP_LOGE(TAG, "gpio %u cannot be configured %d.", this->pin_, ret); + } } std::string ZephyrGPIOPin::dump_summary() const { From 50e7ce55e7370f4f015488e0e61e2746134d7075 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sun, 2 Nov 2025 23:20:30 +0100 Subject: [PATCH 336/526] [nrf52] enable nrf52 test (#11379) --- esphome/components/nrf52/__init__.py | 1 + tests/components/nrf52/test-bootloader.nrf52-xiao-ble.yaml | 3 +++ tests/components/nrf52/test.nrf52-mcumgr.yaml | 0 3 files changed, 4 insertions(+) create mode 100644 tests/components/nrf52/test-bootloader.nrf52-xiao-ble.yaml create mode 100644 tests/components/nrf52/test.nrf52-mcumgr.yaml diff --git a/esphome/components/nrf52/__init__.py b/esphome/components/nrf52/__init__.py index 27e1246744..ace324c1f5 100644 --- a/esphome/components/nrf52/__init__.py +++ b/esphome/components/nrf52/__init__.py @@ -290,6 +290,7 @@ def show_logs(config: ConfigType, args, devices: list[str]) -> bool: address = ble_device.address else: return True + if is_mac_address(address): asyncio.run(logger_connect(address)) return True diff --git a/tests/components/nrf52/test-bootloader.nrf52-xiao-ble.yaml b/tests/components/nrf52/test-bootloader.nrf52-xiao-ble.yaml new file mode 100644 index 0000000000..022ab9c753 --- /dev/null +++ b/tests/components/nrf52/test-bootloader.nrf52-xiao-ble.yaml @@ -0,0 +1,3 @@ +nrf52: + # it is not correct bootloader for the board + bootloader: adafruit_nrf52_sd140_v6 diff --git a/tests/components/nrf52/test.nrf52-mcumgr.yaml b/tests/components/nrf52/test.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..e69de29bb2 From c822ec152f45528949dc9b220165774286cf7456 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Sun, 2 Nov 2025 23:22:49 +0100 Subject: [PATCH 337/526] Enable IPv6 for host (#11630) --- esphome/components/network/__init__.py | 2 ++ tests/components/network/test-ipv6.host.yaml | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 tests/components/network/test-ipv6.host.yaml diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 502803da1e..1d62b661ca 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -55,6 +55,7 @@ CONFIG_SCHEMA = cv.Schema( esp32=False, rp2040=False, bk72xx=False, + host=False, ): cv.All( cv.boolean, cv.Any( @@ -64,6 +65,7 @@ CONFIG_SCHEMA = cv.Schema( esp8266_arduino=cv.Version(0, 0, 0), rp2040_arduino=cv.Version(0, 0, 0), bk72xx_arduino=cv.Version(1, 7, 0), + host=cv.Version(0, 0, 0), ), cv.boolean_false, ), diff --git a/tests/components/network/test-ipv6.host.yaml b/tests/components/network/test-ipv6.host.yaml new file mode 100644 index 0000000000..d9eeab89ea --- /dev/null +++ b/tests/components/network/test-ipv6.host.yaml @@ -0,0 +1,2 @@ +network: + enable_ipv6: true From 79378a930e8d10871a765bce6a144b7868b0e50b Mon Sep 17 00:00:00 2001 From: Juan Antonio Aldea Date: Sun, 2 Nov 2025 23:26:20 +0100 Subject: [PATCH 338/526] Use lists inits initialization instead of std::fill (#11532) --- esphome/components/remote_base/abbwelcome_protocol.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/esphome/components/remote_base/abbwelcome_protocol.h b/esphome/components/remote_base/abbwelcome_protocol.h index f2d0f5b547..b258bd920b 100644 --- a/esphome/components/remote_base/abbwelcome_protocol.h +++ b/esphome/components/remote_base/abbwelcome_protocol.h @@ -33,19 +33,13 @@ Message Format: class ABBWelcomeData { public: // Make default - ABBWelcomeData() { - std::fill(std::begin(this->data_), std::end(this->data_), 0); - this->data_[0] = 0x55; - this->data_[1] = 0xff; - } + ABBWelcomeData() : data_{0x55, 0xff} {} // Make from initializer_list - ABBWelcomeData(std::initializer_list data) { - std::fill(std::begin(this->data_), std::end(this->data_), 0); + ABBWelcomeData(std::initializer_list data) : data_{} { std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin()); } // Make from vector - ABBWelcomeData(const std::vector &data) { - std::fill(std::begin(this->data_), std::end(this->data_), 0); + ABBWelcomeData(const std::vector &data) : data_{} { std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin()); } // Default copy constructor From 8a8a80e1071a3f71938fdd278fca20a5eb5157a1 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sun, 2 Nov 2025 23:44:52 +0100 Subject: [PATCH 339/526] [nrf52, zigbee] OnlyWith support list of components (#11533) Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston --- esphome/config_validation.py | 31 ++++- tests/unit_tests/test_config_validation.py | 129 ++++++++++++++++++++- 2 files changed, 154 insertions(+), 6 deletions(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 359b257992..a3fd271a86 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -2,6 +2,7 @@ from __future__ import annotations +from collections.abc import Callable from contextlib import contextmanager, suppress from dataclasses import dataclass from datetime import datetime @@ -18,6 +19,7 @@ import logging from pathlib import Path import re from string import ascii_letters, digits +import typing import uuid as uuid_ import voluptuous as vol @@ -1763,16 +1765,37 @@ class SplitDefault(Optional): class OnlyWith(Optional): - """Set the default value only if the given component is loaded.""" + """Set the default value only if the given component(s) is/are loaded. - def __init__(self, key, component, default=None): + This validator allows configuration keys to have defaults that are only applied + when specific component(s) are loaded. Supports both single component names and + lists of components. + + Args: + key: Configuration key + component: Single component name (str) or list of component names. + For lists, ALL components must be loaded for the default to apply. + default: Default value to use when condition is met + + Example: + # Single component + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(MQTTComponent) + + # Multiple components (all must be loaded) + cv.OnlyWith(CONF_ZIGBEE_ID, ["zigbee", "nrf52"]): cv.use_id(Zigbee) + """ + + def __init__(self, key, component: str | list[str], default=None) -> None: super().__init__(key) self._component = component self._default = vol.default_factory(default) @property - def default(self): - if self._component in CORE.loaded_integrations: + def default(self) -> Callable[[], typing.Any] | vol.Undefined: + if isinstance(self._component, list): + if all(c in CORE.loaded_integrations for c in self._component): + return self._default + elif self._component in CORE.loaded_integrations: return self._default return vol.UNDEFINED diff --git a/tests/unit_tests/test_config_validation.py b/tests/unit_tests/test_config_validation.py index 2928c5c83a..104cdc2b7a 100644 --- a/tests/unit_tests/test_config_validation.py +++ b/tests/unit_tests/test_config_validation.py @@ -3,6 +3,7 @@ import string from hypothesis import example, given from hypothesis.strategies import builds, integers, ip_addresses, one_of, text import pytest +import voluptuous as vol from esphome import config_validation from esphome.components.esp32.const import ( @@ -301,8 +302,6 @@ def test_split_default(framework, platform, variant, full, idf, arduino, simple) ], ) def test_require_framework_version(framework, platform, message): - import voluptuous as vol - from esphome.const import ( KEY_CORE, KEY_FRAMEWORK_VERSION, @@ -377,3 +376,129 @@ def test_require_framework_version(framework, platform, message): config_validation.require_framework_version( extra_message="test 5", )("test") + + +def test_only_with_single_component_loaded() -> None: + """Test OnlyWith with single component when component is loaded.""" + CORE.loaded_integrations = {"mqtt"} + + schema = config_validation.Schema( + { + config_validation.OnlyWith("mqtt_id", "mqtt", default="test_mqtt"): str, + } + ) + + result = schema({}) + assert result.get("mqtt_id") == "test_mqtt" + + +def test_only_with_single_component_not_loaded() -> None: + """Test OnlyWith with single component when component is not loaded.""" + CORE.loaded_integrations = set() + + schema = config_validation.Schema( + { + config_validation.OnlyWith("mqtt_id", "mqtt", default="test_mqtt"): str, + } + ) + + result = schema({}) + assert "mqtt_id" not in result + + +def test_only_with_list_all_components_loaded() -> None: + """Test OnlyWith with list when all components are loaded.""" + CORE.loaded_integrations = {"zigbee", "nrf52"} + + schema = config_validation.Schema( + { + config_validation.OnlyWith( + "zigbee_id", ["zigbee", "nrf52"], default="test_zigbee" + ): str, + } + ) + + result = schema({}) + assert result.get("zigbee_id") == "test_zigbee" + + +def test_only_with_list_partial_components_loaded() -> None: + """Test OnlyWith with list when only some components are loaded.""" + CORE.loaded_integrations = {"zigbee"} # Only zigbee, not nrf52 + + schema = config_validation.Schema( + { + config_validation.OnlyWith( + "zigbee_id", ["zigbee", "nrf52"], default="test_zigbee" + ): str, + } + ) + + result = schema({}) + assert "zigbee_id" not in result + + +def test_only_with_list_no_components_loaded() -> None: + """Test OnlyWith with list when no components are loaded.""" + CORE.loaded_integrations = set() + + schema = config_validation.Schema( + { + config_validation.OnlyWith( + "zigbee_id", ["zigbee", "nrf52"], default="test_zigbee" + ): str, + } + ) + + result = schema({}) + assert "zigbee_id" not in result + + +def test_only_with_list_multiple_components() -> None: + """Test OnlyWith with list requiring three components.""" + CORE.loaded_integrations = {"comp1", "comp2", "comp3"} + + schema = config_validation.Schema( + { + config_validation.OnlyWith( + "test_id", ["comp1", "comp2", "comp3"], default="test_value" + ): str, + } + ) + + result = schema({}) + assert result.get("test_id") == "test_value" + + # Test with one missing + CORE.loaded_integrations = {"comp1", "comp2"} + result = schema({}) + assert "test_id" not in result + + +def test_only_with_empty_list() -> None: + """Test OnlyWith with empty list (edge case).""" + CORE.loaded_integrations = set() + + schema = config_validation.Schema( + { + config_validation.OnlyWith("test_id", [], default="test_value"): str, + } + ) + + # all([]) returns True, so default should be applied + result = schema({}) + assert result.get("test_id") == "test_value" + + +def test_only_with_user_value_overrides_default() -> None: + """Test OnlyWith respects user-provided values over defaults.""" + CORE.loaded_integrations = {"mqtt"} + + schema = config_validation.Schema( + { + config_validation.OnlyWith("mqtt_id", "mqtt", default="default_id"): str, + } + ) + + result = schema({"mqtt_id": "custom_id"}) + assert result.get("mqtt_id") == "custom_id" From 86402be9e31e7f983011797a1ce684b76c124441 Mon Sep 17 00:00:00 2001 From: Kjell Braden Date: Mon, 3 Nov 2025 00:02:13 +0100 Subject: [PATCH 340/526] actions: fix loop re-entry (#7972) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Co-authored-by: J. Nick Koston Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston --- esphome/components/script/script.h | 38 ++++- esphome/core/base_automation.h | 80 +++++++---- .../fixtures/automation_wait_actions.yaml | 130 ++++++++++++++++++ .../test_action_concurrent_reentry.py | 1 - .../test_automation_wait_actions.py | 104 ++++++++++++++ 5 files changed, 321 insertions(+), 32 deletions(-) create mode 100644 tests/integration/fixtures/automation_wait_actions.yaml create mode 100644 tests/integration/test_automation_wait_actions.py diff --git a/esphome/components/script/script.h b/esphome/components/script/script.h index 58fb67a3ea..870a623f32 100644 --- a/esphome/components/script/script.h +++ b/esphome/components/script/script.h @@ -2,6 +2,7 @@ #include #include +#include #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" @@ -264,10 +265,22 @@ template class IsRunningCondition : public Condition class ScriptWaitAction : public Action, public Component { public: ScriptWaitAction(C *script) : script_(script) {} + void setup() override { + // Start with loop disabled - only enable when there's work to do + this->disable_loop(); + } + void play_complex(Ts... x) override { this->num_running_++; // Check if we can continue immediately. @@ -275,7 +288,11 @@ template class ScriptWaitAction : public Action, this->play_next_(x...); return; } - this->var_ = std::make_tuple(x...); + + // Store parameters for later execution + this->param_queue_.emplace_front(x...); + // Enable loop now that we have work to do + this->enable_loop(); this->loop(); } @@ -286,15 +303,30 @@ template class ScriptWaitAction : public Action, if (this->script_->is_running()) return; - this->play_next_tuple_(this->var_); + while (!this->param_queue_.empty()) { + auto ¶ms = this->param_queue_.front(); + this->play_next_tuple_(params, typename gens::type()); + this->param_queue_.pop_front(); + } + // Queue is now empty - disable loop until next play_complex + this->disable_loop(); } void play(Ts... x) override { /* ignore - see play_complex */ } + void stop() override { + this->param_queue_.clear(); + this->disable_loop(); + } + protected: + template void play_next_tuple_(const std::tuple &tuple, seq /*unused*/) { + this->play_next_(std::get(tuple)...); + } + C *script_; - std::tuple var_{}; + std::forward_list> param_queue_; }; } // namespace script diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index 1c60dd1c7a..e668a1782a 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -10,6 +10,7 @@ #include "esphome/core/helpers.h" #include +#include namespace esphome { @@ -268,32 +269,28 @@ template class WhileAction : public Action { void add_then(const std::initializer_list *> &actions) { this->then_.add_actions(actions); this->then_.add_action(new LambdaAction([this](Ts... x) { - if (this->num_running_ > 0 && this->condition_->check_tuple(this->var_)) { + if (this->num_running_ > 0 && this->condition_->check(x...)) { // play again - if (this->num_running_ > 0) { - this->then_.play_tuple(this->var_); - } + this->then_.play(x...); } else { // condition false, play next - this->play_next_tuple_(this->var_); + this->play_next_(x...); } })); } void play_complex(Ts... x) override { this->num_running_++; - // Store loop parameters - this->var_ = std::make_tuple(x...); // Initial condition check - if (!this->condition_->check_tuple(this->var_)) { + if (!this->condition_->check(x...)) { // If new condition check failed, stop loop if running this->then_.stop(); - this->play_next_tuple_(this->var_); + this->play_next_(x...); return; } if (this->num_running_ > 0) { - this->then_.play_tuple(this->var_); + this->then_.play(x...); } } @@ -305,7 +302,6 @@ template class WhileAction : public Action { protected: Condition *condition_; ActionList then_; - std::tuple var_{}; }; template class RepeatAction : public Action { @@ -317,7 +313,7 @@ template class RepeatAction : public Action { this->then_.add_action(new LambdaAction([this](uint32_t iteration, Ts... x) { iteration++; if (iteration >= this->count_.value(x...)) { - this->play_next_tuple_(this->var_); + this->play_next_(x...); } else { this->then_.play(iteration, x...); } @@ -326,11 +322,10 @@ template class RepeatAction : public Action { void play_complex(Ts... x) override { this->num_running_++; - this->var_ = std::make_tuple(x...); if (this->count_.value(x...) > 0) { this->then_.play(0, x...); } else { - this->play_next_tuple_(this->var_); + this->play_next_(x...); } } @@ -341,15 +336,26 @@ template class RepeatAction : public Action { protected: ActionList then_; - std::tuple var_; }; +/** Wait until a condition is true to continue execution. + * + * Uses queue-based storage to safely handle concurrent executions. + * While concurrent execution from the same trigger is uncommon, it's possible + * (e.g., rapid button presses, high-frequency sensor updates), so we use + * queue-based storage for correctness. + */ template class WaitUntilAction : public Action, public Component { public: WaitUntilAction(Condition *condition) : condition_(condition) {} TEMPLATABLE_VALUE(uint32_t, timeout_value) + void setup() override { + // Start with loop disabled - only enable when there's work to do + this->disable_loop(); + } + void play_complex(Ts... x) override { this->num_running_++; // Check if we can continue immediately. @@ -359,13 +365,14 @@ template class WaitUntilAction : public Action, public Co } return; } - this->var_ = std::make_tuple(x...); - if (this->timeout_value_.has_value()) { - auto f = std::bind(&WaitUntilAction::play_next_, this, x...); - this->set_timeout("timeout", this->timeout_value_.value(x...), f); - } + // Store for later processing + auto now = millis(); + auto timeout = this->timeout_value_.optional_value(x...); + this->var_queue_.emplace_front(now, timeout, std::make_tuple(x...)); + // Enable loop now that we have work to do + this->enable_loop(); this->loop(); } @@ -373,13 +380,32 @@ template class WaitUntilAction : public Action, public Co if (this->num_running_ == 0) return; - if (!this->condition_->check_tuple(this->var_)) { - return; + auto now = millis(); + + this->var_queue_.remove_if([&](auto &queued) { + auto start = std::get(queued); + auto timeout = std::get>(queued); + auto &var = std::get>(queued); + + auto expired = timeout && (now - start) >= *timeout; + + if (!expired && !this->condition_->check_tuple(var)) { + return false; + } + + this->play_next_tuple_(var); + return true; + }); + + // If queue is now empty, disable loop until next play_complex + if (this->var_queue_.empty()) { + this->disable_loop(); } + } - this->cancel_timeout("timeout"); - - this->play_next_tuple_(this->var_); + void stop() override { + this->var_queue_.clear(); + this->disable_loop(); } float get_setup_priority() const override { return setup_priority::DATA; } @@ -387,11 +413,9 @@ template class WaitUntilAction : public Action, public Co void play(Ts... x) override { /* ignore - see play_complex */ } - void stop() override { this->cancel_timeout("timeout"); } - protected: Condition *condition_; - std::tuple var_{}; + std::forward_list, std::tuple>> var_queue_{}; }; template class UpdateComponentAction : public Action { diff --git a/tests/integration/fixtures/automation_wait_actions.yaml b/tests/integration/fixtures/automation_wait_actions.yaml new file mode 100644 index 0000000000..65a61be14f --- /dev/null +++ b/tests/integration/fixtures/automation_wait_actions.yaml @@ -0,0 +1,130 @@ +esphome: + name: test-automation-wait-actions + +host: + +api: + actions: + # Test 1: Trigger wait_until automation 5 times rapidly + - action: test_wait_until + then: + - logger.log: "=== TEST 1: Triggering wait_until automation 5 times ===" + # Publish 5 different values to trigger the on_value automation 5 times + - sensor.template.publish: + id: wait_until_sensor + state: 1 + - sensor.template.publish: + id: wait_until_sensor + state: 2 + - sensor.template.publish: + id: wait_until_sensor + state: 3 + - sensor.template.publish: + id: wait_until_sensor + state: 4 + - sensor.template.publish: + id: wait_until_sensor + state: 5 + # Wait then satisfy the condition so all 5 waiting actions complete + - delay: 100ms + - globals.set: + id: test_flag + value: 'true' + + # Test 2: Trigger script.wait automation 5 times rapidly + - action: test_script_wait + then: + - logger.log: "=== TEST 2: Triggering script.wait automation 5 times ===" + # Start a long-running script + - script.execute: blocking_script + # Publish 5 different values to trigger the on_value automation 5 times + - sensor.template.publish: + id: script_wait_sensor + state: 1 + - sensor.template.publish: + id: script_wait_sensor + state: 2 + - sensor.template.publish: + id: script_wait_sensor + state: 3 + - sensor.template.publish: + id: script_wait_sensor + state: 4 + - sensor.template.publish: + id: script_wait_sensor + state: 5 + + # Test 3: Trigger wait_until timeout automation 5 times rapidly + - action: test_wait_timeout + then: + - logger.log: "=== TEST 3: Triggering timeout automation 5 times ===" + # Publish 5 different values (condition will never be true, all will timeout) + - sensor.template.publish: + id: timeout_sensor + state: 1 + - sensor.template.publish: + id: timeout_sensor + state: 2 + - sensor.template.publish: + id: timeout_sensor + state: 3 + - sensor.template.publish: + id: timeout_sensor + state: 4 + - sensor.template.publish: + id: timeout_sensor + state: 5 + +logger: + level: DEBUG + +globals: + - id: test_flag + type: bool + restore_value: false + initial_value: 'false' + + - id: timeout_flag + type: bool + restore_value: false + initial_value: 'false' + +# Sensors with wait_until/script.wait in their on_value automations +sensor: + # Test 1: on_value automation with wait_until + - platform: template + id: wait_until_sensor + on_value: + # This wait_until will be hit 5 times before any complete + - wait_until: + condition: + lambda: return id(test_flag); + - logger.log: "wait_until automation completed" + + # Test 2: on_value automation with script.wait + - platform: template + id: script_wait_sensor + on_value: + # This script.wait will be hit 5 times before any complete + - script.wait: blocking_script + - logger.log: "script.wait automation completed" + + # Test 3: on_value automation with wait_until timeout + - platform: template + id: timeout_sensor + on_value: + # This wait_until will be hit 5 times, all will timeout + - wait_until: + condition: + lambda: return id(timeout_flag); + timeout: 200ms + - logger.log: "timeout automation completed" + +script: + # Blocking script for script.wait test + - id: blocking_script + mode: single + then: + - logger.log: "Blocking script: START" + - delay: 200ms + - logger.log: "Blocking script: END" diff --git a/tests/integration/test_action_concurrent_reentry.py b/tests/integration/test_action_concurrent_reentry.py index ba67e4c798..aa5801ca2b 100644 --- a/tests/integration/test_action_concurrent_reentry.py +++ b/tests/integration/test_action_concurrent_reentry.py @@ -11,7 +11,6 @@ import pytest from .types import APIClientConnectedFactory, RunCompiledFunction -@pytest.mark.xfail(reason="https://github.com/esphome/issues/issues/6534") @pytest.mark.asyncio async def test_action_concurrent_reentry( yaml_config: str, diff --git a/tests/integration/test_automation_wait_actions.py b/tests/integration/test_automation_wait_actions.py new file mode 100644 index 0000000000..adcb8ba487 --- /dev/null +++ b/tests/integration/test_automation_wait_actions.py @@ -0,0 +1,104 @@ +"""Test concurrent execution of wait_until and script.wait in direct automation actions.""" + +from __future__ import annotations + +import asyncio +import re + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_automation_wait_actions( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """ + Test that wait_until and script.wait correctly handle concurrent executions + when automation actions (not scripts) are triggered multiple times rapidly. + + This tests sensor.on_value automations being triggered 5 times before any complete. + """ + loop = asyncio.get_running_loop() + + # Track completion counts + test_results = { + "wait_until": 0, + "script_wait": 0, + "wait_until_timeout": 0, + } + + # Patterns for log messages + wait_until_complete = re.compile(r"wait_until automation completed") + script_wait_complete = re.compile(r"script\.wait automation completed") + timeout_complete = re.compile(r"timeout automation completed") + + # Test completion futures + test1_complete = loop.create_future() + test2_complete = loop.create_future() + test3_complete = loop.create_future() + + def check_output(line: str) -> None: + """Check log output for completion messages.""" + # Test 1: wait_until concurrent execution + if wait_until_complete.search(line): + test_results["wait_until"] += 1 + if test_results["wait_until"] == 5 and not test1_complete.done(): + test1_complete.set_result(True) + + # Test 2: script.wait concurrent execution + if script_wait_complete.search(line): + test_results["script_wait"] += 1 + if test_results["script_wait"] == 5 and not test2_complete.done(): + test2_complete.set_result(True) + + # Test 3: wait_until with timeout + if timeout_complete.search(line): + test_results["wait_until_timeout"] += 1 + if test_results["wait_until_timeout"] == 5 and not test3_complete.done(): + test3_complete.set_result(True) + + async with ( + run_compiled(yaml_config, line_callback=check_output), + api_client_connected() as client, + ): + # Get services + _, services = await client.list_entities_services() + + # Test 1: wait_until in automation - trigger 5 times rapidly + test_service = next((s for s in services if s.name == "test_wait_until"), None) + assert test_service is not None, "test_wait_until service not found" + client.execute_service(test_service, {}) + await asyncio.wait_for(test1_complete, timeout=3.0) + + # Verify Test 1: All 5 triggers should complete + assert test_results["wait_until"] == 5, ( + f"Test 1: Expected 5 wait_until completions, got {test_results['wait_until']}" + ) + + # Test 2: script.wait in automation - trigger 5 times rapidly + test_service = next((s for s in services if s.name == "test_script_wait"), None) + assert test_service is not None, "test_script_wait service not found" + client.execute_service(test_service, {}) + await asyncio.wait_for(test2_complete, timeout=3.0) + + # Verify Test 2: All 5 triggers should complete + assert test_results["script_wait"] == 5, ( + f"Test 2: Expected 5 script.wait completions, got {test_results['script_wait']}" + ) + + # Test 3: wait_until with timeout in automation - trigger 5 times rapidly + test_service = next( + (s for s in services if s.name == "test_wait_timeout"), None + ) + assert test_service is not None, "test_wait_timeout service not found" + client.execute_service(test_service, {}) + await asyncio.wait_for(test3_complete, timeout=3.0) + + # Verify Test 3: All 5 triggers should timeout and complete + assert test_results["wait_until_timeout"] == 5, ( + f"Test 3: Expected 5 timeout completions, got {test_results['wait_until_timeout']}" + ) From 19e275dc02894792327001d9df1bd8c863fe4c5c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 3 Nov 2025 10:33:44 +1000 Subject: [PATCH 341/526] [component] Add is_idle method and condition (#11651) --- esphome/automation.py | 26 +++++++++++++++++++++++++- esphome/components/lvgl/automation.py | 6 +++++- esphome/components/lvgl/lvgl_esphome.h | 1 - esphome/core/component.cpp | 1 + esphome/core/component.h | 8 ++++++++ tests/components/esphome/common.yaml | 7 ++++++- 6 files changed, 45 insertions(+), 4 deletions(-) diff --git a/esphome/automation.py b/esphome/automation.py index 99be12451e..2439b1ddc4 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -15,7 +15,7 @@ from esphome.const import ( CONF_TYPE_ID, CONF_UPDATE_INTERVAL, ) -from esphome.core import ID +from esphome.core import ID, Lambda from esphome.cpp_generator import ( LambdaExpression, MockObj, @@ -310,6 +310,30 @@ async def for_condition_to_code( return var +@register_condition( + "component.is_idle", + LambdaCondition, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(cg.Component), + } + ), +) +async def component_is_idle_condition_to_code( + config: ConfigType, + condition_id: ID, + template_arg: cg.TemplateArguments, + args: TemplateArgsType, +) -> MockObj: + comp = await cg.get_variable(config[CONF_ID]) + lambda_ = await cg.process_lambda( + Lambda(f"return {comp}->is_idle();"), args, return_type=bool + ) + return new_lambda_pvariable( + condition_id, lambda_, StatelessLambdaCondition, template_arg + ) + + @register_action( "delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds) ) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index 593c8c67bb..9b58727f2a 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -137,7 +137,11 @@ async def lvgl_is_idle(config, condition_id, template_arg, args): lvgl = config[CONF_LVGL_ID] timeout = await lv_milliseconds.process(config[CONF_TIMEOUT]) async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context: - lv_add(ReturnStatement(lvgl_comp.is_idle(timeout))) + lv_add( + ReturnStatement( + lv_expr.disp_get_inactive_time(lvgl_comp.get_disp()) > timeout + ) + ) var = cg.new_Pvariable( condition_id, TemplateArguments(LvglComponent, *template_arg), diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index ea58fdb85b..50d192fde3 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -175,7 +175,6 @@ class LvglComponent : public PollingComponent { static void monitor_cb(lv_disp_drv_t *disp_drv, uint32_t time, uint32_t px); static void render_start_cb(lv_disp_drv_t *disp_drv); void dump_config() override; - bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; } lv_disp_t *get_disp() { return this->disp_; } lv_obj_t *get_scr_act() { return lv_disp_get_scr_act(this->disp_); } // Pause or resume the display. diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 11d9501bb8..de3dd99d0c 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -284,6 +284,7 @@ bool Component::is_ready() const { (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE || (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_SETUP; } +bool Component::is_idle() const { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE; } bool Component::can_proceed() { return true; } bool Component::status_has_warning() const { return this->component_state_ & STATUS_LED_WARNING; } bool Component::status_has_error() const { return this->component_state_ & STATUS_LED_ERROR; } diff --git a/esphome/core/component.h b/esphome/core/component.h index e97941374d..462e0e301c 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -141,6 +141,14 @@ class Component { */ bool is_in_loop_state() const; + /** Check if this component is idle. + * Being idle means being in LOOP_DONE state. + * This means the component has completed setup, is not failed, but its loop is currently disabled. + * + * @return True if the component is idle + */ + bool is_idle() const; + /** Mark this component as failed. Any future timeouts/intervals/setup/loop will no longer be called. * * This might be useful if a component wants to indicate that a connection to its peripheral failed. diff --git a/tests/components/esphome/common.yaml b/tests/components/esphome/common.yaml index a4b309b69d..b2d7bccaa5 100644 --- a/tests/components/esphome/common.yaml +++ b/tests/components/esphome/common.yaml @@ -10,7 +10,11 @@ esphome: on_shutdown: logger.log: on_shutdown on_loop: - logger.log: on_loop + if: + condition: + component.is_idle: binary_sensor_id + then: + logger.log: on_loop - sensor idle compile_process_limit: 1 min_version: "2025.1" name_add_mac_suffix: true @@ -34,5 +38,6 @@ esphome: binary_sensor: - platform: template + id: binary_sensor_id name: Other device sensor device_id: other_device From 3e17767f6a310d10e33e74849e61786bfabeab78 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 3 Nov 2025 10:50:15 +1000 Subject: [PATCH 342/526] [font][image] Use ESPHome urls for remote images (#11675) --- tests/components/font/common.yaml | 4 ++-- tests/components/font/test.host.yaml | 4 ++-- tests/components/image/common.yaml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/components/font/common.yaml b/tests/components/font/common.yaml index 6ba52e3d97..c156b4aea1 100644 --- a/tests/components/font/common.yaml +++ b/tests/components/font/common.yaml @@ -21,12 +21,12 @@ font: id: roboto_greek size: 20 glyphs: ["\u0300", "\u00C5", "\U000000C7"] - - file: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf" + - file: "https://media.esphome.io/tests/fonts/Monocraft.ttf" id: monocraft size: 20 - file: type: web - url: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf" + url: "https://media.esphome.io/tests/fonts/Monocraft.ttf" id: monocraft2 size: 24 - file: $component_dir/Monocraft.ttf diff --git a/tests/components/font/test.host.yaml b/tests/components/font/test.host.yaml index c5399f2826..387ea47335 100644 --- a/tests/components/font/test.host.yaml +++ b/tests/components/font/test.host.yaml @@ -21,12 +21,12 @@ font: id: roboto_greek size: 20 glyphs: ["\u0300", "\u00C5", "\U000000C7"] - - file: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf" + - file: "https://media.esphome.io/tests/fonts/Monocraft.ttf" id: monocraft size: 20 - file: type: web - url: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf" + url: "https://media.esphome.io/tests/fonts/Monocraft.ttf" id: monocraft2 size: 24 - file: $component_dir/Monocraft.ttf diff --git a/tests/components/image/common.yaml b/tests/components/image/common.yaml index 864ca41c44..9819068970 100644 --- a/tests/components/image/common.yaml +++ b/tests/components/image/common.yaml @@ -50,16 +50,16 @@ image: transparency: opaque - id: web_svg_image - file: https://raw.githubusercontent.com/esphome/esphome-docs/a62d7ab193c1a464ed791670170c7d518189109b/images/logo.svg + file: https://media.esphome.io/logo/logo.svg resize: 256x48 type: BINARY transparency: chroma_key - id: web_tiff_image - file: https://upload.wikimedia.org/wikipedia/commons/b/b6/SIPI_Jelly_Beans_4.1.07.tiff + file: https://media.esphome.io/tests/images/SIPI_Jelly_Beans_4.1.07.tiff type: RGB resize: 48x48 - id: web_redirect_image - file: https://avatars.githubusercontent.com/u/3060199?s=48&v=4 + file: https://media.esphome.io/logo/logo.png type: RGB resize: 48x48 - id: mdi_alert From 1509ed8d2391f0cdd16e65b703e2979006515200 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:04:06 -0600 Subject: [PATCH 343/526] [esphome][ota] Add write_byte_() helper to reduce code duplication (#11511) --- .../components/esphome/ota/ota_esphome.cpp | 28 ++++++------------- esphome/components/esphome/ota/ota_esphome.h | 1 + 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 569268ea15..b85d660272 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -281,19 +281,15 @@ void ESPHomeOTAComponent::handle_data_() { #endif // Acknowledge auth OK - 1 byte - buf[0] = ota::OTA_RESPONSE_AUTH_OK; - this->writeall_(buf, 1); + this->write_byte_(ota::OTA_RESPONSE_AUTH_OK); // Read size, 4 bytes MSB first if (!this->readall_(buf, 4)) { this->log_read_error_(LOG_STR("size")); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } - ota_size = 0; - for (uint8_t i = 0; i < 4; i++) { - ota_size <<= 8; - ota_size |= buf[i]; - } + ota_size = (static_cast(buf[0]) << 24) | (static_cast(buf[1]) << 16) | + (static_cast(buf[2]) << 8) | buf[3]; ESP_LOGV(TAG, "Size is %u bytes", ota_size); // Now that we've passed authentication and are actually @@ -313,8 +309,7 @@ void ESPHomeOTAComponent::handle_data_() { update_started = true; // Acknowledge prepare OK - 1 byte - buf[0] = ota::OTA_RESPONSE_UPDATE_PREPARE_OK; - this->writeall_(buf, 1); + this->write_byte_(ota::OTA_RESPONSE_UPDATE_PREPARE_OK); // Read binary MD5, 32 bytes if (!this->readall_(buf, 32)) { @@ -326,8 +321,7 @@ void ESPHomeOTAComponent::handle_data_() { this->backend_->set_update_md5(sbuf); // Acknowledge MD5 OK - 1 byte - buf[0] = ota::OTA_RESPONSE_BIN_MD5_OK; - this->writeall_(buf, 1); + this->write_byte_(ota::OTA_RESPONSE_BIN_MD5_OK); while (total < ota_size) { // TODO: timeout check @@ -354,8 +348,7 @@ void ESPHomeOTAComponent::handle_data_() { total += read; #if USE_OTA_VERSION == 2 while (size_acknowledged + OTA_BLOCK_SIZE <= total || (total == ota_size && size_acknowledged < ota_size)) { - buf[0] = ota::OTA_RESPONSE_CHUNK_OK; - this->writeall_(buf, 1); + this->write_byte_(ota::OTA_RESPONSE_CHUNK_OK); size_acknowledged += OTA_BLOCK_SIZE; } #endif @@ -374,8 +367,7 @@ void ESPHomeOTAComponent::handle_data_() { } // Acknowledge receive OK - 1 byte - buf[0] = ota::OTA_RESPONSE_RECEIVE_OK; - this->writeall_(buf, 1); + this->write_byte_(ota::OTA_RESPONSE_RECEIVE_OK); error_code = this->backend_->end(); if (error_code != ota::OTA_RESPONSE_OK) { @@ -384,8 +376,7 @@ void ESPHomeOTAComponent::handle_data_() { } // Acknowledge Update end OK - 1 byte - buf[0] = ota::OTA_RESPONSE_UPDATE_END_OK; - this->writeall_(buf, 1); + this->write_byte_(ota::OTA_RESPONSE_UPDATE_END_OK); // Read ACK if (!this->readall_(buf, 1) || buf[0] != ota::OTA_RESPONSE_OK) { @@ -404,8 +395,7 @@ void ESPHomeOTAComponent::handle_data_() { App.safe_reboot(); error: - buf[0] = static_cast(error_code); - this->writeall_(buf, 1); + this->write_byte_(static_cast(error_code)); this->cleanup_connection_(); if (this->backend_ != nullptr && update_started) { diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index d4a8410d35..057461e6a4 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -53,6 +53,7 @@ class ESPHomeOTAComponent : public ota::OTAComponent { #endif // USE_OTA_PASSWORD bool readall_(uint8_t *buf, size_t len); bool writeall_(const uint8_t *buf, size_t len); + inline bool write_byte_(uint8_t byte) { return this->writeall_(&byte, 1); } bool try_read_(size_t to_read, const LogString *desc); bool try_write_(size_t to_write, const LogString *desc); From 17ab20ef6108bcf4c3c2f67d2f022cbe71d9c00d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:05:36 -0600 Subject: [PATCH 344/526] [esp32_ble] Optimize loop() to reduce flash usage by ~104 bytes (#11627) --- esphome/components/esp32_ble/ble.cpp | 120 +++++++++++++-------------- 1 file changed, 59 insertions(+), 61 deletions(-) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 5bbd5fe9ed..69e317ff6d 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -31,6 +31,26 @@ namespace esphome::esp32_ble { static const char *const TAG = "esp32_ble"; +// GAP event groups for deduplication across gap_event_handler and dispatch_gap_event_ +#define GAP_SCAN_COMPLETE_EVENTS \ + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: \ + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: \ + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT + +#define GAP_ADV_COMPLETE_EVENTS \ + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: \ + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: \ + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: \ + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: \ + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT + +#define GAP_SECURITY_EVENTS \ + case ESP_GAP_BLE_AUTH_CMPL_EVT: \ + case ESP_GAP_BLE_SEC_REQ_EVT: \ + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: \ + case ESP_GAP_BLE_PASSKEY_REQ_EVT: \ + case ESP_GAP_BLE_NC_REQ_EVT + void ESP32BLE::setup() { global_ble = this; if (!ble_pre_setup_()) { @@ -414,60 +434,48 @@ void ESP32BLE::loop() { break; // Scan complete events - case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: - case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: - case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: - // All three scan complete events have the same structure with just status - // The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe - // This is verified at compile-time by static_assert checks in ble_event.h - // The struct already contains our copy of the status (copied in BLEEvent constructor) - ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); -#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT - for (auto *gap_handler : this->gap_event_handlers_) { - gap_handler->gap_event_handler( - gap_event, reinterpret_cast(&ble_event->event_.gap.scan_complete)); - } -#endif - break; - + GAP_SCAN_COMPLETE_EVENTS: // Advertising complete events - case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: - case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: - case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: - case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: - case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: - // All advertising complete events have the same structure with just status - ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); -#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT - for (auto *gap_handler : this->gap_event_handlers_) { - gap_handler->gap_event_handler( - gap_event, reinterpret_cast(&ble_event->event_.gap.adv_complete)); - } -#endif - break; - + GAP_ADV_COMPLETE_EVENTS: // RSSI complete event case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: - ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); -#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT - for (auto *gap_handler : this->gap_event_handlers_) { - gap_handler->gap_event_handler( - gap_event, reinterpret_cast(&ble_event->event_.gap.read_rssi_complete)); - } -#endif - break; - // Security events - case ESP_GAP_BLE_AUTH_CMPL_EVT: - case ESP_GAP_BLE_SEC_REQ_EVT: - case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: - case ESP_GAP_BLE_PASSKEY_REQ_EVT: - case ESP_GAP_BLE_NC_REQ_EVT: + GAP_SECURITY_EVENTS: ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); #ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT - for (auto *gap_handler : this->gap_event_handlers_) { - gap_handler->gap_event_handler( - gap_event, reinterpret_cast(&ble_event->event_.gap.security)); + { + esp_ble_gap_cb_param_t *param; + // clang-format off + switch (gap_event) { + // All three scan complete events have the same structure with just status + // The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe + // This is verified at compile-time by static_assert checks in ble_event.h + // The struct already contains our copy of the status (copied in BLEEvent constructor) + GAP_SCAN_COMPLETE_EVENTS: + param = reinterpret_cast(&ble_event->event_.gap.scan_complete); + break; + + // All advertising complete events have the same structure with just status + GAP_ADV_COMPLETE_EVENTS: + param = reinterpret_cast(&ble_event->event_.gap.adv_complete); + break; + + case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: + param = reinterpret_cast(&ble_event->event_.gap.read_rssi_complete); + break; + + GAP_SECURITY_EVENTS: + param = reinterpret_cast(&ble_event->event_.gap.security); + break; + + default: + break; + } + // clang-format on + // Dispatch to all registered handlers + for (auto *gap_handler : this->gap_event_handlers_) { + gap_handler->gap_event_handler(gap_event, param); + } } #endif break; @@ -547,23 +555,13 @@ void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa // Queue GAP events that components need to handle // Scanning events - used by esp32_ble_tracker case ESP_GAP_BLE_SCAN_RESULT_EVT: - case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: - case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: - case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: + GAP_SCAN_COMPLETE_EVENTS: // Advertising events - used by esp32_ble_beacon and esp32_ble server - case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: - case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: - case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: - case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: - case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: + GAP_ADV_COMPLETE_EVENTS: // Connection events - used by ble_client case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: // Security events - used by ble_client and bluetooth_proxy - case ESP_GAP_BLE_AUTH_CMPL_EVT: - case ESP_GAP_BLE_SEC_REQ_EVT: - case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: - case ESP_GAP_BLE_PASSKEY_REQ_EVT: - case ESP_GAP_BLE_NC_REQ_EVT: + GAP_SECURITY_EVENTS: enqueue_ble_event(event, param); return; From 01ae86145a7e15f8c537798a6f6f3050596bcf99 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:06:40 -0600 Subject: [PATCH 345/526] [ble_client] Fix premature disconnections by reading characteristics immediately after service discovery (#11410) --- esphome/components/ble_client/sensor/ble_sensor.cpp | 3 +++ esphome/components/ble_client/text_sensor/ble_text_sensor.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index 6d293528c6..61685c0566 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -77,6 +77,9 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga } } else { this->node_state = espbt::ClientState::ESTABLISHED; + // For non-notify characteristics, trigger an immediate read after service discovery + // to avoid peripherals disconnecting due to inactivity + this->update(); } break; } diff --git a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp index e7da297fa0..b7a6d154db 100644 --- a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +++ b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp @@ -79,6 +79,9 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } } else { this->node_state = espbt::ClientState::ESTABLISHED; + // For non-notify characteristics, trigger an immediate read after service discovery + // to avoid peripherals disconnecting due to inactivity + this->update(); } break; } From 40f919eaa6f3fb60d461f745220d414e66ca80bf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:07:03 -0600 Subject: [PATCH 346/526] Add action continuation tests (#11674) --- tests/components/api/common-base.yaml | 96 +++++++ .../fixtures/continuation_actions.yaml | 174 +++++++++++++ .../integration/test_continuation_actions.py | 235 ++++++++++++++++++ 3 files changed, 505 insertions(+) create mode 100644 tests/integration/fixtures/continuation_actions.yaml create mode 100644 tests/integration/test_continuation_actions.py diff --git a/tests/components/api/common-base.yaml b/tests/components/api/common-base.yaml index 6483d5a997..c90fa4dfef 100644 --- a/tests/components/api/common-base.yaml +++ b/tests/components/api/common-base.yaml @@ -87,3 +87,99 @@ api: - float_arr.size() - string_arr[0].c_str() - string_arr.size() + # Test ContinuationAction (IfAction with then/else branches) + - action: test_if_action + variables: + condition: bool + value: int + then: + - if: + condition: + lambda: 'return condition;' + then: + - logger.log: + format: "Condition true, value: %d" + args: ['value'] + else: + - logger.log: + format: "Condition false, value: %d" + args: ['value'] + - logger.log: "After if/else" + # Test nested IfAction (multiple ContinuationAction instances) + - action: test_nested_if + variables: + outer: bool + inner: bool + then: + - if: + condition: + lambda: 'return outer;' + then: + - if: + condition: + lambda: 'return inner;' + then: + - logger.log: "Both true" + else: + - logger.log: "Outer true, inner false" + else: + - logger.log: "Outer false" + - logger.log: "After nested if" + # Test WhileLoopContinuation (WhileAction) + - action: test_while_action + variables: + max_count: int + then: + - lambda: 'id(api_continuation_test_counter) = 0;' + - while: + condition: + lambda: 'return id(api_continuation_test_counter) < max_count;' + then: + - logger.log: + format: "While loop iteration: %d" + args: ['id(api_continuation_test_counter)'] + - lambda: 'id(api_continuation_test_counter)++;' + - logger.log: "After while loop" + # Test RepeatLoopContinuation (RepeatAction) + - action: test_repeat_action + variables: + count: int + then: + - repeat: + count: !lambda 'return count;' + then: + - logger.log: + format: "Repeat iteration: %d" + args: ['iteration'] + - logger.log: "After repeat" + # Test combined continuations (if + while + repeat) + - action: test_combined_continuations + variables: + do_loop: bool + loop_count: int + then: + - if: + condition: + lambda: 'return do_loop;' + then: + - repeat: + count: !lambda 'return loop_count;' + then: + - lambda: 'id(api_continuation_test_counter) = iteration;' + - while: + condition: + lambda: 'return id(api_continuation_test_counter) > 0;' + then: + - logger.log: + format: "Combined: repeat=%d, while=%d" + args: ['iteration', 'id(api_continuation_test_counter)'] + - lambda: 'id(api_continuation_test_counter)--;' + else: + - logger.log: "Skipped loops" + - logger.log: "After combined test" + +globals: + - id: api_continuation_test_counter + type: int + restore_value: false + initial_value: '0' diff --git a/tests/integration/fixtures/continuation_actions.yaml b/tests/integration/fixtures/continuation_actions.yaml new file mode 100644 index 0000000000..bdfe149cb7 --- /dev/null +++ b/tests/integration/fixtures/continuation_actions.yaml @@ -0,0 +1,174 @@ +esphome: + name: test-continuation-actions + +host: + +api: + actions: + # Test 1: IfAction with ContinuationAction (then/else branches) + - action: test_if_action + variables: + condition: bool + value: int + then: + - logger.log: + format: "Test if: condition=%s, value=%d" + args: ['YESNO(condition)', 'value'] + - if: + condition: + lambda: 'return condition;' + then: + - logger.log: + format: "if-then executed: value=%d" + args: ['value'] + else: + - logger.log: + format: "if-else executed: value=%d" + args: ['value'] + - logger.log: "if completed" + + # Test 2: Nested IfAction (multiple ContinuationAction instances) + - action: test_nested_if + variables: + outer: bool + inner: bool + then: + - logger.log: + format: "Test nested if: outer=%s, inner=%s" + args: ['YESNO(outer)', 'YESNO(inner)'] + - if: + condition: + lambda: 'return outer;' + then: + - if: + condition: + lambda: 'return inner;' + then: + - logger.log: "nested-both-true" + else: + - logger.log: "nested-outer-true-inner-false" + else: + - logger.log: "nested-outer-false" + - logger.log: "nested if completed" + + # Test 3: WhileAction with WhileLoopContinuation + - action: test_while_action + variables: + max_count: int + then: + - logger.log: + format: "Test while: max_count=%d" + args: ['max_count'] + - globals.set: + id: continuation_test_counter + value: !lambda 'return 0;' + - while: + condition: + lambda: 'return id(continuation_test_counter) < max_count;' + then: + - logger.log: + format: "while-iteration-%d" + args: ['id(continuation_test_counter)'] + - globals.set: + id: continuation_test_counter + value: !lambda 'return id(continuation_test_counter) + 1;' + - logger.log: "while completed" + + # Test 4: RepeatAction with RepeatLoopContinuation + - action: test_repeat_action + variables: + count: int + then: + - logger.log: + format: "Test repeat: count=%d" + args: ['count'] + - repeat: + count: !lambda 'return count;' + then: + - logger.log: + format: "repeat-iteration-%d" + args: ['iteration'] + - logger.log: "repeat completed" + + # Test 5: Combined continuations (if + while + repeat) + - action: test_combined + variables: + do_loop: bool + loop_count: int + then: + - logger.log: + format: "Test combined: do_loop=%s, loop_count=%d" + args: ['YESNO(do_loop)', 'loop_count'] + - if: + condition: + lambda: 'return do_loop;' + then: + - repeat: + count: !lambda 'return loop_count;' + then: + - globals.set: + id: continuation_test_counter + value: !lambda 'return iteration;' + - while: + condition: + lambda: 'return id(continuation_test_counter) > 0;' + then: + - logger.log: + format: "combined-repeat%d-while%d" + args: ['iteration', 'id(continuation_test_counter)'] + - globals.set: + id: continuation_test_counter + value: !lambda 'return id(continuation_test_counter) - 1;' + else: + - logger.log: "combined-skipped" + - logger.log: "combined completed" + + # Test 6: Rapid triggers to verify memory efficiency + - action: test_rapid_if + then: + - logger.log: "=== Rapid if test start ===" + - sensor.template.publish: + id: rapid_sensor + state: 1 + - sensor.template.publish: + id: rapid_sensor + state: 2 + - sensor.template.publish: + id: rapid_sensor + state: 3 + - sensor.template.publish: + id: rapid_sensor + state: 4 + - sensor.template.publish: + id: rapid_sensor + state: 5 + - logger.log: "=== Rapid if test published 5 values ===" + +logger: + level: DEBUG + +globals: + - id: continuation_test_counter + type: int + restore_value: false + initial_value: '0' + +# Sensor to test rapid automation triggers with if/else (ContinuationAction) +sensor: + - platform: template + id: rapid_sensor + on_value: + - if: + condition: + lambda: 'return x > 2;' + then: + - logger.log: + format: "rapid-if-then: value=%d" + args: ['(int)x'] + else: + - logger.log: + format: "rapid-if-else: value=%d" + args: ['(int)x'] + - logger.log: + format: "rapid-if-completed: value=%d" + args: ['(int)x'] diff --git a/tests/integration/test_continuation_actions.py b/tests/integration/test_continuation_actions.py new file mode 100644 index 0000000000..1069ee7581 --- /dev/null +++ b/tests/integration/test_continuation_actions.py @@ -0,0 +1,235 @@ +"""Test continuation actions (ContinuationAction, WhileLoopContinuation, RepeatLoopContinuation).""" + +from __future__ import annotations + +import asyncio +import re + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_continuation_actions( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """ + Test that continuation actions work correctly for if/while/repeat. + + These continuation classes replace LambdaAction with simple parent pointers, + saving 32-36 bytes per instance and eliminating std::function overhead. + """ + loop = asyncio.get_running_loop() + + # Track test completions + test_results = { + "if_then": False, + "if_else": False, + "if_complete": False, + "nested_both_true": False, + "nested_outer_true_inner_false": False, + "nested_outer_false": False, + "nested_complete": False, + "while_iterations": 0, + "while_complete": False, + "repeat_iterations": 0, + "repeat_complete": False, + "combined_iterations": 0, + "combined_complete": False, + "rapid_then": 0, + "rapid_else": 0, + "rapid_complete": 0, + } + + # Patterns for log messages + if_then_pattern = re.compile(r"if-then executed: value=(\d+)") + if_else_pattern = re.compile(r"if-else executed: value=(\d+)") + if_complete_pattern = re.compile(r"if completed") + nested_both_true_pattern = re.compile(r"nested-both-true") + nested_outer_true_inner_false_pattern = re.compile(r"nested-outer-true-inner-false") + nested_outer_false_pattern = re.compile(r"nested-outer-false") + nested_complete_pattern = re.compile(r"nested if completed") + while_iteration_pattern = re.compile(r"while-iteration-(\d+)") + while_complete_pattern = re.compile(r"while completed") + repeat_iteration_pattern = re.compile(r"repeat-iteration-(\d+)") + repeat_complete_pattern = re.compile(r"repeat completed") + combined_pattern = re.compile(r"combined-repeat(\d+)-while(\d+)") + combined_complete_pattern = re.compile(r"combined completed") + rapid_then_pattern = re.compile(r"rapid-if-then: value=(\d+)") + rapid_else_pattern = re.compile(r"rapid-if-else: value=(\d+)") + rapid_complete_pattern = re.compile(r"rapid-if-completed: value=(\d+)") + + # Test completion futures + test1_complete = loop.create_future() # if action + test2_complete = loop.create_future() # nested if + test3_complete = loop.create_future() # while + test4_complete = loop.create_future() # repeat + test5_complete = loop.create_future() # combined + test6_complete = loop.create_future() # rapid + + def check_output(line: str) -> None: + """Check log output for test messages.""" + # Test 1: IfAction + if if_then_pattern.search(line): + test_results["if_then"] = True + if if_else_pattern.search(line): + test_results["if_else"] = True + if if_complete_pattern.search(line): + test_results["if_complete"] = True + if not test1_complete.done(): + test1_complete.set_result(True) + + # Test 2: Nested IfAction + if nested_both_true_pattern.search(line): + test_results["nested_both_true"] = True + if nested_outer_true_inner_false_pattern.search(line): + test_results["nested_outer_true_inner_false"] = True + if nested_outer_false_pattern.search(line): + test_results["nested_outer_false"] = True + if nested_complete_pattern.search(line): + test_results["nested_complete"] = True + if not test2_complete.done(): + test2_complete.set_result(True) + + # Test 3: WhileAction + if match := while_iteration_pattern.search(line): + test_results["while_iterations"] = max( + test_results["while_iterations"], int(match.group(1)) + 1 + ) + if while_complete_pattern.search(line): + test_results["while_complete"] = True + if not test3_complete.done(): + test3_complete.set_result(True) + + # Test 4: RepeatAction + if match := repeat_iteration_pattern.search(line): + test_results["repeat_iterations"] = max( + test_results["repeat_iterations"], int(match.group(1)) + 1 + ) + if repeat_complete_pattern.search(line): + test_results["repeat_complete"] = True + if not test4_complete.done(): + test4_complete.set_result(True) + + # Test 5: Combined + if combined_pattern.search(line): + test_results["combined_iterations"] += 1 + if combined_complete_pattern.search(line): + test_results["combined_complete"] = True + if not test5_complete.done(): + test5_complete.set_result(True) + + # Test 6: Rapid triggers + if rapid_then_pattern.search(line): + test_results["rapid_then"] += 1 + if rapid_else_pattern.search(line): + test_results["rapid_else"] += 1 + if rapid_complete_pattern.search(line): + test_results["rapid_complete"] += 1 + if test_results["rapid_complete"] == 5 and not test6_complete.done(): + test6_complete.set_result(True) + + async with ( + run_compiled(yaml_config, line_callback=check_output), + api_client_connected() as client, + ): + # Get services + _, services = await client.list_entities_services() + + # Test 1: IfAction with then branch + test_service = next((s for s in services if s.name == "test_if_action"), None) + assert test_service is not None, "test_if_action service not found" + client.execute_service(test_service, {"condition": True, "value": 42}) + await asyncio.wait_for(test1_complete, timeout=2.0) + assert test_results["if_then"], "IfAction then branch not executed" + assert test_results["if_complete"], "IfAction did not complete" + + # Test 1b: IfAction with else branch + test1_complete = loop.create_future() + test_results["if_complete"] = False + client.execute_service(test_service, {"condition": False, "value": 99}) + await asyncio.wait_for(test1_complete, timeout=2.0) + assert test_results["if_else"], "IfAction else branch not executed" + assert test_results["if_complete"], "IfAction did not complete" + + # Test 2: Nested IfAction - test all branches + test_service = next((s for s in services if s.name == "test_nested_if"), None) + assert test_service is not None, "test_nested_if service not found" + + # Both true + client.execute_service(test_service, {"outer": True, "inner": True}) + await asyncio.wait_for(test2_complete, timeout=2.0) + assert test_results["nested_both_true"], "Nested both true not executed" + + # Outer true, inner false + test2_complete = loop.create_future() + test_results["nested_complete"] = False + client.execute_service(test_service, {"outer": True, "inner": False}) + await asyncio.wait_for(test2_complete, timeout=2.0) + assert test_results["nested_outer_true_inner_false"], ( + "Nested outer true inner false not executed" + ) + + # Outer false + test2_complete = loop.create_future() + test_results["nested_complete"] = False + client.execute_service(test_service, {"outer": False, "inner": True}) + await asyncio.wait_for(test2_complete, timeout=2.0) + assert test_results["nested_outer_false"], "Nested outer false not executed" + + # Test 3: WhileAction + test_service = next( + (s for s in services if s.name == "test_while_action"), None + ) + assert test_service is not None, "test_while_action service not found" + client.execute_service(test_service, {"max_count": 3}) + await asyncio.wait_for(test3_complete, timeout=2.0) + assert test_results["while_iterations"] == 3, ( + f"WhileAction expected 3 iterations, got {test_results['while_iterations']}" + ) + assert test_results["while_complete"], "WhileAction did not complete" + + # Test 4: RepeatAction + test_service = next( + (s for s in services if s.name == "test_repeat_action"), None + ) + assert test_service is not None, "test_repeat_action service not found" + client.execute_service(test_service, {"count": 5}) + await asyncio.wait_for(test4_complete, timeout=2.0) + assert test_results["repeat_iterations"] == 5, ( + f"RepeatAction expected 5 iterations, got {test_results['repeat_iterations']}" + ) + assert test_results["repeat_complete"], "RepeatAction did not complete" + + # Test 5: Combined (if + repeat + while) + test_service = next((s for s in services if s.name == "test_combined"), None) + assert test_service is not None, "test_combined service not found" + client.execute_service(test_service, {"do_loop": True, "loop_count": 2}) + await asyncio.wait_for(test5_complete, timeout=2.0) + # Should execute: repeat 2 times, each iteration does while from iteration down to 0 + # iteration 0: while 0 times = 0 + # iteration 1: while 1 time = 1 + # Total: 1 combined log + assert test_results["combined_iterations"] >= 1, ( + f"Combined expected >=1 iterations, got {test_results['combined_iterations']}" + ) + assert test_results["combined_complete"], "Combined did not complete" + + # Test 6: Rapid triggers (tests memory efficiency of ContinuationAction) + test_service = next((s for s in services if s.name == "test_rapid_if"), None) + assert test_service is not None, "test_rapid_if service not found" + client.execute_service(test_service, {}) + await asyncio.wait_for(test6_complete, timeout=2.0) + # Values 1, 2 should hit else (<=2), values 3, 4, 5 should hit then (>2) + assert test_results["rapid_else"] == 2, ( + f"Rapid test expected 2 else, got {test_results['rapid_else']}" + ) + assert test_results["rapid_then"] == 3, ( + f"Rapid test expected 3 then, got {test_results['rapid_then']}" + ) + assert test_results["rapid_complete"] == 5, ( + f"Rapid test expected 5 completions, got {test_results['rapid_complete']}" + ) From 7a1297ec8428025075b48d52f3d32c1ccd8de8e3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:08:12 -0600 Subject: [PATCH 347/526] [web_server] Remove redundant assignment in deq_push_back_with_dedup_ (#11642) --- esphome/components/web_server/web_server.cpp | 3 +-- esphome/components/web_server_idf/web_server_idf.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 1d08ef5a35..61951e2600 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -111,8 +111,7 @@ void DeferredUpdateEventSource::deq_push_back_with_dedup_(void *source, message_ // 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; + return; // Already in queue, no need to update since items are equal } } this->deferred_queue_.push_back(item); diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index c3ba7ddc2b..ac0b5bad83 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -494,8 +494,7 @@ void AsyncEventSourceResponse::deq_push_back_with_dedup_(void *source, message_g // 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; + return; // Already in queue, no need to update since items are equal } } this->deferred_queue_.push_back(item); From 712421b82b6dc241329f25993d5500656e500f1b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:10:18 -0600 Subject: [PATCH 348/526] [web_server] Eliminate nested lambdas in DeferredUpdateEventSourceList (#11641) --- esphome/components/web_server/web_server.cpp | 61 ++++++++++---------- esphome/components/web_server/web_server.h | 2 +- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 61951e2600..cc62b019c3 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -219,50 +219,51 @@ void DeferredUpdateEventSourceList::add_new_client(WebServer *ws, AsyncWebServer DeferredUpdateEventSource *es = new DeferredUpdateEventSource(ws, "/events"); this->push_back(es); - es->onConnect([this, ws, es](AsyncEventSourceClient *client) { - ws->defer([this, ws, es]() { this->on_client_connect_(ws, es); }); - }); + es->onConnect([this, es](AsyncEventSourceClient *client) { this->on_client_connect_(es); }); - es->onDisconnect([this, ws, es](AsyncEventSourceClient *client) { - ws->defer([this, es]() { this->on_client_disconnect_((DeferredUpdateEventSource *) es); }); - }); + es->onDisconnect([this, es](AsyncEventSourceClient *client) { this->on_client_disconnect_(es); }); es->handleRequest(request); } -void DeferredUpdateEventSourceList::on_client_connect_(WebServer *ws, DeferredUpdateEventSource *source) { - // Configure reconnect timeout and send config - // this should always go through since the AsyncEventSourceClient event queue is empty on connect - std::string message = ws->get_config_json(); - source->try_send_nodefer(message.c_str(), "ping", millis(), 30000); +void DeferredUpdateEventSourceList::on_client_connect_(DeferredUpdateEventSource *source) { + WebServer *ws = source->web_server_; + ws->defer([ws, source]() { + // Configure reconnect timeout and send config + // this should always go through since the AsyncEventSourceClient event queue is empty on connect + std::string message = ws->get_config_json(); + source->try_send_nodefer(message.c_str(), "ping", millis(), 30000); #ifdef USE_WEBSERVER_SORTING - for (auto &group : ws->sorting_groups_) { - json::JsonBuilder builder; - JsonObject root = builder.root(); - root["name"] = group.second.name; - root["sorting_weight"] = group.second.weight; - message = builder.serialize(); + for (auto &group : ws->sorting_groups_) { + json::JsonBuilder builder; + JsonObject root = builder.root(); + root["name"] = group.second.name; + root["sorting_weight"] = group.second.weight; + message = builder.serialize(); - // up to 31 groups should be able to be queued initially without defer - source->try_send_nodefer(message.c_str(), "sorting_group"); - } + // up to 31 groups should be able to be queued initially without defer + source->try_send_nodefer(message.c_str(), "sorting_group"); + } #endif - source->entities_iterator_.begin(ws->include_internal_); + source->entities_iterator_.begin(ws->include_internal_); - // just dump them all up-front and take advantage of the deferred queue - // on second thought that takes too long, but leaving the commented code here for debug purposes - // while(!source->entities_iterator_.completed()) { - // source->entities_iterator_.advance(); - //} + // just dump them all up-front and take advantage of the deferred queue + // on second thought that takes too long, but leaving the commented code here for debug purposes + // while(!source->entities_iterator_.completed()) { + // source->entities_iterator_.advance(); + //} + }); } void DeferredUpdateEventSourceList::on_client_disconnect_(DeferredUpdateEventSource *source) { - // This method was called via WebServer->defer() and is no longer executing in the - // context of the network callback. The object is now dead and can be safely deleted. - this->remove(source); - delete source; // NOLINT + source->web_server_->defer([this, source]() { + // This method was called via WebServer->defer() and is no longer executing in the + // context of the network callback. The object is now dead and can be safely deleted. + this->remove(source); + delete source; // NOLINT + }); } #endif diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 2e5d58d375..c54f5558a9 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -141,7 +141,7 @@ class DeferredUpdateEventSource : public AsyncEventSource { class DeferredUpdateEventSourceList : public std::list { protected: - void on_client_connect_(WebServer *ws, DeferredUpdateEventSource *source); + void on_client_connect_(DeferredUpdateEventSource *source); void on_client_disconnect_(DeferredUpdateEventSource *source); public: From 4838eff3829831d2a6b1390de1c848e52be62fca Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:12:56 -0600 Subject: [PATCH 349/526] [web_server] Use zero-copy entity ID comparison in request handlers (#11644) --- esphome/components/web_server/web_server.cpp | 52 +++++++++++--------- esphome/components/web_server/web_server.h | 11 ++++- esphome/core/entity_base.h | 5 ++ 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index cc62b019c3..fe0da14435 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -435,9 +435,10 @@ void WebServer::on_sensor_update(sensor::Sensor *obj, float state) { } void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (sensor::Sensor *obj : App.get_sensors()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; - if (request->method() == HTTP_GET && match.method_empty()) { + // Note: request->method() is always HTTP_GET here (canHandle ensures this) + if (match.method_empty()) { auto detail = get_request_detail(request); std::string data = this->sensor_json(obj, obj->state, detail); request->send(200, "application/json", data.c_str()); @@ -477,9 +478,10 @@ void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s } void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (text_sensor::TextSensor *obj : App.get_text_sensors()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; - if (request->method() == HTTP_GET && match.method_empty()) { + // Note: request->method() is always HTTP_GET here (canHandle ensures this) + if (match.method_empty()) { auto detail = get_request_detail(request); std::string data = this->text_sensor_json(obj, obj->state, detail); request->send(200, "application/json", data.c_str()); @@ -516,7 +518,7 @@ void WebServer::on_switch_update(switch_::Switch *obj, bool state) { } void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (switch_::Switch *obj : App.get_switches()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -585,7 +587,7 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail #ifdef USE_BUTTON void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (button::Button *obj : App.get_buttons()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { auto detail = get_request_detail(request); @@ -627,9 +629,10 @@ void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) { } void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; - if (request->method() == HTTP_GET && match.method_empty()) { + // Note: request->method() is always HTTP_GET here (canHandle ensures this) + if (match.method_empty()) { auto detail = get_request_detail(request); std::string data = this->binary_sensor_json(obj, obj->state, detail); request->send(200, "application/json", data.c_str()); @@ -665,7 +668,7 @@ void WebServer::on_fan_update(fan::Fan *obj) { } void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (fan::Fan *obj : App.get_fans()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -739,7 +742,7 @@ void WebServer::on_light_update(light::LightState *obj) { } void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (light::LightState *obj : App.get_lights()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -812,7 +815,7 @@ void WebServer::on_cover_update(cover::Cover *obj) { } void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (cover::Cover *obj : App.get_covers()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -897,7 +900,7 @@ void WebServer::on_number_update(number::Number *obj, float state) { } void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (auto *obj : App.get_numbers()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -962,7 +965,7 @@ void WebServer::on_date_update(datetime::DateEntity *obj) { } void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (auto *obj : App.get_dates()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { auto detail = get_request_detail(request); @@ -1017,7 +1020,7 @@ void WebServer::on_time_update(datetime::TimeEntity *obj) { } void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (auto *obj : App.get_times()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { auto detail = get_request_detail(request); @@ -1071,7 +1074,7 @@ void WebServer::on_datetime_update(datetime::DateTimeEntity *obj) { } void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (auto *obj : App.get_datetimes()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { auto detail = get_request_detail(request); @@ -1126,7 +1129,7 @@ void WebServer::on_text_update(text::Text *obj, const std::string &state) { } void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (auto *obj : App.get_texts()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -1180,7 +1183,7 @@ void WebServer::on_select_update(select::Select *obj, const std::string &state, } void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (auto *obj : App.get_selects()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -1236,7 +1239,7 @@ void WebServer::on_climate_update(climate::Climate *obj) { } void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (auto *obj : App.get_climates()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -1377,7 +1380,7 @@ void WebServer::on_lock_update(lock::Lock *obj) { } void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (lock::Lock *obj : App.get_locks()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -1448,7 +1451,7 @@ void WebServer::on_valve_update(valve::Valve *obj) { } void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (valve::Valve *obj : App.get_valves()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -1529,7 +1532,7 @@ void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlP } void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (alarm_control_panel::AlarmControlPanel *obj : App.get_alarm_control_panels()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { @@ -1608,10 +1611,11 @@ void WebServer::on_event(event::Event *obj, const std::string &event_type) { void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (event::Event *obj : App.get_events()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; - if (request->method() == HTTP_GET && match.method_empty()) { + // Note: request->method() is always HTTP_GET here (canHandle ensures this) + if (match.method_empty()) { auto detail = get_request_detail(request); std::string data = this->event_json(obj, "", detail); request->send(200, "application/json", data.c_str()); @@ -1673,7 +1677,7 @@ void WebServer::on_update(update::UpdateEntity *obj) { } void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (update::UpdateEntity *obj : App.get_updates()) { - if (!match.id_equals(obj->get_object_id())) + if (!match.id_equals_entity(obj)) continue; if (request->method() == HTTP_GET && match.method_empty()) { diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index c54f5558a9..fb790483dc 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -48,8 +48,15 @@ struct UrlMatch { return domain && domain_len == strlen(str) && memcmp(domain, str, domain_len) == 0; } - bool id_equals(const std::string &str) const { - return id && id_len == str.length() && memcmp(id, str.c_str(), id_len) == 0; + bool id_equals_entity(EntityBase *entity) const { + // Zero-copy comparison using StringRef + StringRef static_ref = entity->get_object_id_ref_for_api_(); + if (!static_ref.empty()) { + return id && id_len == static_ref.size() && memcmp(id, static_ref.c_str(), id_len) == 0; + } + // Fallback to allocation (rare) + const auto &obj_id = entity->get_object_id(); + return id && id_len == obj_id.length() && memcmp(id, obj_id.c_str(), id_len) == 0; } bool method_equals(const char *str) const { diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index 4a6460e708..80cd6b8e77 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -17,6 +17,10 @@ namespace api { class APIConnection; } // namespace api +namespace web_server { +struct UrlMatch; +} // namespace web_server + enum EntityCategory : uint8_t { ENTITY_CATEGORY_NONE = 0, ENTITY_CATEGORY_CONFIG = 1, @@ -116,6 +120,7 @@ class EntityBase { protected: friend class api::APIConnection; + friend struct web_server::UrlMatch; // Get object_id as StringRef when it's static (for API usage) // Returns empty StringRef if object_id is dynamic (needs allocation) From 34244afea1117c6042e786f5f99a1f026aa0c39c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:16:26 -0600 Subject: [PATCH 350/526] =?UTF-8?q?[esp32=5Fble]=20Reduce=20GATT=20event?= =?UTF-8?q?=20latency=20from=208ms=20to=2012=CE=BCs=20with=20notification?= =?UTF-8?q?=20socket=20(#11663)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- esphome/components/esp32_ble/__init__.py | 9 ++ esphome/components/esp32_ble/ble.cpp | 112 +++++++++++++++++++++++ esphome/components/esp32_ble/ble.h | 41 +++++++++ 3 files changed, 162 insertions(+) diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 411c2add71..1ae8df6f5e 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -7,6 +7,7 @@ from typing import Any from esphome import automation import esphome.codegen as cg +from esphome.components import socket from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant import esphome.config_validation as cv from esphome.const import ( @@ -481,6 +482,14 @@ async def to_code(config): cg.add(var.set_name(name)) await cg.register_component(var, config) + # BLE uses 1 UDP socket for event notification to wake up main loop from select() + # This enables low-latency (~12μs) BLE event processing instead of waiting for + # select() timeout (0-16ms). The socket is created in ble_setup_() and used to + # wake lwip_select() when BLE events arrive from the BLE thread. + # Note: Called during config generation, socket is created at runtime. In practice, + # always used since esp32_ble only runs on ESP32 which always has USE_SOCKET_SELECT_SUPPORT. + socket.consume_sockets(1, "esp32_ble")(config) + # Define max connections for use in C++ code (e.g., ble_server.h) max_connections = config.get(CONF_MAX_CONNECTIONS, DEFAULT_MAX_CONNECTIONS) cg.add_define("USE_ESP32_BLE_MAX_CONNECTIONS", max_connections) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 69e317ff6d..eef0db5347 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -27,6 +27,10 @@ extern "C" { #include #endif +#ifdef USE_SOCKET_SELECT_SUPPORT +#include +#endif + namespace esphome::esp32_ble { static const char *const TAG = "esp32_ble"; @@ -293,10 +297,21 @@ bool ESP32BLE::ble_setup_() { // BLE takes some time to be fully set up, 200ms should be more than enough delay(200); // NOLINT + // Set up notification socket to wake main loop for BLE events + // This enables low-latency (~12μs) event processing instead of waiting for select() timeout +#ifdef USE_SOCKET_SELECT_SUPPORT + this->setup_event_notification_(); +#endif + return true; } bool ESP32BLE::ble_dismantle_() { + // Clean up notification socket first before dismantling BLE stack +#ifdef USE_SOCKET_SELECT_SUPPORT + this->cleanup_event_notification_(); +#endif + esp_err_t err = esp_bluedroid_disable(); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_bluedroid_disable failed: %d", err); @@ -394,6 +409,12 @@ void ESP32BLE::loop() { break; } +#ifdef USE_SOCKET_SELECT_SUPPORT + // Drain any notification socket events first + // This clears the socket so it doesn't stay "ready" in subsequent select() calls + this->drain_event_notifications_(); +#endif + BLEEvent *ble_event = this->ble_events_.pop(); while (ble_event != nullptr) { switch (ble_event->type_) { @@ -582,6 +603,10 @@ void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa 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); + // Wake up main loop to process GATT event immediately +#ifdef USE_SOCKET_SELECT_SUPPORT + global_ble->notify_main_loop_(); +#endif } #endif @@ -589,6 +614,10 @@ void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gat 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); + // Wake up main loop to process GATT event immediately +#ifdef USE_SOCKET_SELECT_SUPPORT + global_ble->notify_main_loop_(); +#endif } #endif @@ -628,6 +657,89 @@ void ESP32BLE::dump_config() { } } +#ifdef USE_SOCKET_SELECT_SUPPORT +void ESP32BLE::setup_event_notification_() { + // Create UDP socket for event notifications + this->notify_fd_ = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (this->notify_fd_ < 0) { + ESP_LOGW(TAG, "Event socket create failed: %d", errno); + return; + } + + // Bind to loopback with auto-assigned port + struct sockaddr_in addr = {}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = lwip_htonl(INADDR_LOOPBACK); + addr.sin_port = 0; // Auto-assign port + + if (lwip_bind(this->notify_fd_, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + ESP_LOGW(TAG, "Event socket bind failed: %d", errno); + lwip_close(this->notify_fd_); + this->notify_fd_ = -1; + return; + } + + // Get the assigned address and connect to it + // Connecting a UDP socket allows using send() instead of sendto() for better performance + struct sockaddr_in notify_addr; + socklen_t len = sizeof(notify_addr); + if (lwip_getsockname(this->notify_fd_, (struct sockaddr *) ¬ify_addr, &len) < 0) { + ESP_LOGW(TAG, "Event socket address failed: %d", errno); + lwip_close(this->notify_fd_); + this->notify_fd_ = -1; + return; + } + + // Connect to self (loopback) - allows using send() instead of sendto() + // After connect(), no need to store notify_addr - the socket remembers it + if (lwip_connect(this->notify_fd_, (struct sockaddr *) ¬ify_addr, sizeof(notify_addr)) < 0) { + ESP_LOGW(TAG, "Event socket connect failed: %d", errno); + lwip_close(this->notify_fd_); + this->notify_fd_ = -1; + return; + } + + // Set non-blocking mode + int flags = lwip_fcntl(this->notify_fd_, F_GETFL, 0); + lwip_fcntl(this->notify_fd_, F_SETFL, flags | O_NONBLOCK); + + // Register with application's select() loop + if (!App.register_socket_fd(this->notify_fd_)) { + ESP_LOGW(TAG, "Event socket register failed"); + lwip_close(this->notify_fd_); + this->notify_fd_ = -1; + return; + } + + ESP_LOGD(TAG, "Event socket ready"); +} + +void ESP32BLE::cleanup_event_notification_() { + if (this->notify_fd_ >= 0) { + App.unregister_socket_fd(this->notify_fd_); + lwip_close(this->notify_fd_); + this->notify_fd_ = -1; + ESP_LOGD(TAG, "Event socket closed"); + } +} + +void ESP32BLE::drain_event_notifications_() { + // Called from main loop to drain any pending notifications + // Must check is_socket_ready() to avoid blocking on empty socket + if (this->notify_fd_ >= 0 && App.is_socket_ready(this->notify_fd_)) { + char buffer[BLE_EVENT_NOTIFY_DRAIN_BUFFER_SIZE]; + // Drain all pending notifications with non-blocking reads + // Multiple BLE events may have triggered multiple writes, so drain until EWOULDBLOCK + // We control both ends of this loopback socket (always write 1 byte per event), + // so no error checking needed - any errors indicate catastrophic system failure + while (lwip_recvfrom(this->notify_fd_, buffer, sizeof(buffer), 0, nullptr, nullptr) > 0) { + // Just draining, no action needed - actual BLE events are already queued + } + } +} + +#endif // USE_SOCKET_SELECT_SUPPORT + uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) { uint64_t u = 0; u |= uint64_t(address[0] & 0xFF) << 40; diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index dc973f0e82..7c3195db6d 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -25,6 +25,10 @@ #include #include +#ifdef USE_SOCKET_SELECT_SUPPORT +#include +#endif + namespace esphome::esp32_ble { // Maximum size of the BLE event queue @@ -162,6 +166,13 @@ class ESP32BLE : public Component { void advertising_init_(); #endif +#ifdef USE_SOCKET_SELECT_SUPPORT + void setup_event_notification_(); // Create notification socket + void cleanup_event_notification_(); // Close and unregister socket + inline void notify_main_loop_(); // Wake up select() from BLE thread (hot path - inlined) + void drain_event_notifications_(); // Read pending notifications in main loop +#endif + private: template friend void enqueue_ble_event(Args... args); @@ -196,6 +207,13 @@ class ESP32BLE : public Component { esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; // 4 bytes (enum) uint32_t advertising_cycle_time_{}; // 4 bytes +#ifdef USE_SOCKET_SELECT_SUPPORT + // Event notification socket for waking up main loop from BLE thread + // Uses connected UDP loopback socket to wake lwip_select() with ~12μs latency vs 0-16ms timeout + // Socket is connected during setup, allowing use of send() instead of sendto() for efficiency + int notify_fd_{-1}; // 4 bytes (file descriptor) +#endif + // 2-byte aligned members uint16_t appearance_{0}; // 2 bytes @@ -207,6 +225,29 @@ class ESP32BLE : public Component { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern ESP32BLE *global_ble; +#ifdef USE_SOCKET_SELECT_SUPPORT +// Inline implementations for hot-path functions +// These are called from BLE thread (notify) and main loop (drain) on every event + +// Small buffer for draining notification bytes (1 byte sent per BLE event) +// Size allows draining multiple notifications per recvfrom() without wasting stack +static constexpr size_t BLE_EVENT_NOTIFY_DRAIN_BUFFER_SIZE = 16; + +inline void ESP32BLE::notify_main_loop_() { + // Called from BLE thread context when events are queued + // Wakes up lwip_select() in main loop by writing to connected loopback socket + if (this->notify_fd_ >= 0) { + const char dummy = 1; + // Non-blocking send - if it fails (unlikely), select() will wake on timeout anyway + // No error checking needed: we control both ends of this loopback socket, and the + // BLE event is already queued. Notification is best-effort to reduce latency. + // This is safe to call from BLE thread - send() is thread-safe in lwip + // Socket is already connected to loopback address, so send() is faster than sendto() + lwip_send(this->notify_fd_, &dummy, 1, 0); + } +} +#endif // USE_SOCKET_SELECT_SUPPORT + template class BLEEnabledCondition : public Condition { public: bool check(Ts... x) override { return global_ble->is_active(); } From 3f05fd82e50d43ec878939049a7ef10119571ad2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:18:59 -0600 Subject: [PATCH 351/526] [fan] Use std::vector for preset modes, preserve config order (#11483) --- esphome/components/api/api.proto | 2 +- esphome/components/api/api_connection.cpp | 2 +- esphome/components/api/api_pb2.cpp | 8 ++-- esphome/components/api/api_pb2.h | 2 +- esphome/components/fan/fan.cpp | 44 +++++++++++++------ esphome/components/fan/fan_traits.h | 38 +++++++--------- esphome/components/hbridge/fan/hbridge_fan.h | 6 +-- esphome/components/speed/fan/speed_fan.h | 6 +-- .../components/template/fan/template_fan.h | 6 +-- 9 files changed, 59 insertions(+), 55 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index f50944ffa4..20645fc47b 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -425,7 +425,7 @@ message ListEntitiesFanResponse { bool disabled_by_default = 9; string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON"]; EntityCategory entity_category = 11; - repeated string supported_preset_modes = 12 [(container_pointer) = "std::set"]; + repeated string supported_preset_modes = 12 [(container_pointer_no_template) = "std::vector"]; uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"]; } // Deprecated in API version 1.6 - only used in deprecated fields diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 382c4acc16..33d5072d9c 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -423,7 +423,7 @@ uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *con msg.supports_speed = traits.supports_speed(); msg.supports_direction = traits.supports_direction(); msg.supported_speed_count = traits.supported_speed_count(); - msg.supported_preset_modes = &traits.supported_preset_modes_for_api_(); + msg.supported_preset_modes = &traits.supported_preset_modes(); return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::fan_command(const FanCommandRequest &msg) { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 3472707d3c..0673d35518 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -355,8 +355,8 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(10, this->icon_ref_); #endif buffer.encode_uint32(11, static_cast(this->entity_category)); - for (const auto &it : *this->supported_preset_modes) { - buffer.encode_string(12, it, true); + for (const char *it : *this->supported_preset_modes) { + buffer.encode_string(12, it, strlen(it), true); } #ifdef USE_DEVICES buffer.encode_uint32(13, this->device_id); @@ -376,8 +376,8 @@ void ListEntitiesFanResponse::calculate_size(ProtoSize &size) const { #endif size.add_uint32(1, static_cast(this->entity_category)); if (!this->supported_preset_modes->empty()) { - for (const auto &it : *this->supported_preset_modes) { - size.add_length_force(1, it.size()); + for (const char *it : *this->supported_preset_modes) { + size.add_length_force(1, strlen(it)); } } #ifdef USE_DEVICES diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index aa5c031ac7..89f16044d7 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -725,7 +725,7 @@ class ListEntitiesFanResponse final : public InfoResponseProtoMessage { bool supports_speed{false}; bool supports_direction{false}; int32_t supported_speed_count{0}; - const std::set *supported_preset_modes{}; + const std::vector *supported_preset_modes{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(ProtoSize &size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 26065ed644..5b4f437f99 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -51,7 +51,14 @@ void FanCall::validate_() { if (!this->preset_mode_.empty()) { const auto &preset_modes = traits.supported_preset_modes(); - if (preset_modes.find(this->preset_mode_) == preset_modes.end()) { + bool found = false; + for (const auto &mode : preset_modes) { + if (strcmp(mode, this->preset_mode_.c_str()) == 0) { + found = true; + break; + } + } + if (!found) { ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), this->preset_mode_.c_str()); this->preset_mode_.clear(); } @@ -92,11 +99,12 @@ FanCall FanRestoreState::to_call(Fan &fan) { call.set_speed(this->speed); call.set_direction(this->direction); - if (fan.get_traits().supports_preset_modes()) { + auto traits = fan.get_traits(); + if (traits.supports_preset_modes()) { // Use stored preset index to get preset name - const auto &preset_modes = fan.get_traits().supported_preset_modes(); + const auto &preset_modes = traits.supported_preset_modes(); if (this->preset_mode < preset_modes.size()) { - call.set_preset_mode(*std::next(preset_modes.begin(), this->preset_mode)); + call.set_preset_mode(preset_modes[this->preset_mode]); } } return call; @@ -107,11 +115,12 @@ void FanRestoreState::apply(Fan &fan) { fan.speed = this->speed; fan.direction = this->direction; - if (fan.get_traits().supports_preset_modes()) { + auto traits = fan.get_traits(); + if (traits.supports_preset_modes()) { // Use stored preset index to get preset name - const auto &preset_modes = fan.get_traits().supported_preset_modes(); + const auto &preset_modes = traits.supported_preset_modes(); if (this->preset_mode < preset_modes.size()) { - fan.preset_mode = *std::next(preset_modes.begin(), this->preset_mode); + fan.preset_mode = preset_modes[this->preset_mode]; } } fan.publish_state(); @@ -182,18 +191,25 @@ void Fan::save_state_() { return; } + auto traits = this->get_traits(); + FanRestoreState state{}; state.state = this->state; state.oscillating = this->oscillating; state.speed = this->speed; state.direction = this->direction; - if (this->get_traits().supports_preset_modes() && !this->preset_mode.empty()) { - const auto &preset_modes = this->get_traits().supported_preset_modes(); + if (traits.supports_preset_modes() && !this->preset_mode.empty()) { + const auto &preset_modes = traits.supported_preset_modes(); // Store index of current preset mode - auto preset_iterator = preset_modes.find(this->preset_mode); - if (preset_iterator != preset_modes.end()) - state.preset_mode = std::distance(preset_modes.begin(), preset_iterator); + size_t i = 0; + for (const auto &mode : preset_modes) { + if (strcmp(mode, this->preset_mode.c_str()) == 0) { + state.preset_mode = i; + break; + } + i++; + } } this->rtc_.save(&state); @@ -216,8 +232,8 @@ void Fan::dump_traits_(const char *tag, const char *prefix) { } if (traits.supports_preset_modes()) { ESP_LOGCONFIG(tag, "%s Supported presets:", prefix); - for (const std::string &s : traits.supported_preset_modes()) - ESP_LOGCONFIG(tag, "%s - %s", prefix, s.c_str()); + for (const char *s : traits.supported_preset_modes()) + ESP_LOGCONFIG(tag, "%s - %s", prefix, s); } } diff --git a/esphome/components/fan/fan_traits.h b/esphome/components/fan/fan_traits.h index 48509e5705..df345f9b04 100644 --- a/esphome/components/fan/fan_traits.h +++ b/esphome/components/fan/fan_traits.h @@ -1,15 +1,9 @@ -#include -#include - #pragma once -namespace esphome { +#include +#include -#ifdef USE_API -namespace api { -class APIConnection; -} // namespace api -#endif +namespace esphome { namespace fan { @@ -36,27 +30,27 @@ class FanTraits { /// Set whether this fan supports changing direction void set_direction(bool direction) { this->direction_ = direction; } /// Return the preset modes supported by the fan. - std::set supported_preset_modes() const { return this->preset_modes_; } - /// Set the preset modes supported by the fan. - void set_supported_preset_modes(const std::set &preset_modes) { this->preset_modes_ = preset_modes; } + const std::vector &supported_preset_modes() const { return this->preset_modes_; } + /// Set the preset modes supported by the fan (from initializer list). + void set_supported_preset_modes(std::initializer_list preset_modes) { + this->preset_modes_ = preset_modes; + } + /// Set the preset modes supported by the fan (from vector). + void set_supported_preset_modes(const std::vector &preset_modes) { this->preset_modes_ = preset_modes; } + + // Deleted overloads to catch incorrect std::string usage at compile time with clear error messages + void set_supported_preset_modes(const std::vector &preset_modes) = delete; + void set_supported_preset_modes(std::initializer_list preset_modes) = delete; + /// Return if preset modes are supported bool supports_preset_modes() const { return !this->preset_modes_.empty(); } protected: -#ifdef USE_API - // The API connection is a friend class to access internal methods - friend class api::APIConnection; - // This method returns a reference to the internal preset modes set. - // It is used by the API to avoid copying data when encoding messages. - // Warning: Do not use this method outside of the API connection code. - // It returns a reference to internal data that can be invalidated. - const std::set &supported_preset_modes_for_api_() const { return this->preset_modes_; } -#endif bool oscillation_{false}; bool speed_{false}; bool direction_{false}; int speed_count_{}; - std::set preset_modes_{}; + std::vector preset_modes_{}; }; } // namespace fan diff --git a/esphome/components/hbridge/fan/hbridge_fan.h b/esphome/components/hbridge/fan/hbridge_fan.h index 4234fccae3..143c7c1853 100644 --- a/esphome/components/hbridge/fan/hbridge_fan.h +++ b/esphome/components/hbridge/fan/hbridge_fan.h @@ -1,7 +1,5 @@ #pragma once -#include - #include "esphome/core/automation.h" #include "esphome/components/output/binary_output.h" #include "esphome/components/output/float_output.h" @@ -22,7 +20,7 @@ class HBridgeFan : public Component, public fan::Fan { void set_pin_a(output::FloatOutput *pin_a) { pin_a_ = pin_a; } void set_pin_b(output::FloatOutput *pin_b) { pin_b_ = pin_b; } void set_enable_pin(output::FloatOutput *enable) { enable_ = enable; } - void set_preset_modes(const std::set &presets) { preset_modes_ = presets; } + void set_preset_modes(std::initializer_list presets) { preset_modes_ = presets; } void setup() override; void dump_config() override; @@ -38,7 +36,7 @@ class HBridgeFan : public Component, public fan::Fan { int speed_count_{}; DecayMode decay_mode_{DECAY_MODE_SLOW}; fan::FanTraits traits_; - std::set preset_modes_{}; + std::vector preset_modes_{}; void control(const fan::FanCall &call) override; void write_state_(); diff --git a/esphome/components/speed/fan/speed_fan.h b/esphome/components/speed/fan/speed_fan.h index 6537bce3f6..e9a389e0f3 100644 --- a/esphome/components/speed/fan/speed_fan.h +++ b/esphome/components/speed/fan/speed_fan.h @@ -1,7 +1,5 @@ #pragma once -#include - #include "esphome/core/component.h" #include "esphome/components/output/binary_output.h" #include "esphome/components/output/float_output.h" @@ -18,7 +16,7 @@ class SpeedFan : public Component, public fan::Fan { void set_output(output::FloatOutput *output) { this->output_ = output; } void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; } void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; } - void set_preset_modes(const std::set &presets) { this->preset_modes_ = presets; } + void set_preset_modes(std::initializer_list presets) { this->preset_modes_ = presets; } fan::FanTraits get_traits() override { return this->traits_; } protected: @@ -30,7 +28,7 @@ class SpeedFan : public Component, public fan::Fan { output::BinaryOutput *direction_{nullptr}; int speed_count_{}; fan::FanTraits traits_; - std::set preset_modes_{}; + std::vector preset_modes_{}; }; } // namespace speed diff --git a/esphome/components/template/fan/template_fan.h b/esphome/components/template/fan/template_fan.h index 7f5305ca48..b09352f4d4 100644 --- a/esphome/components/template/fan/template_fan.h +++ b/esphome/components/template/fan/template_fan.h @@ -1,7 +1,5 @@ #pragma once -#include - #include "esphome/core/component.h" #include "esphome/components/fan/fan.h" @@ -16,7 +14,7 @@ class TemplateFan : public Component, public fan::Fan { void set_has_direction(bool has_direction) { this->has_direction_ = has_direction; } void set_has_oscillating(bool has_oscillating) { this->has_oscillating_ = has_oscillating; } void set_speed_count(int count) { this->speed_count_ = count; } - void set_preset_modes(const std::set &presets) { this->preset_modes_ = presets; } + void set_preset_modes(std::initializer_list presets) { this->preset_modes_ = presets; } fan::FanTraits get_traits() override { return this->traits_; } protected: @@ -26,7 +24,7 @@ class TemplateFan : public Component, public fan::Fan { bool has_direction_{false}; int speed_count_{0}; fan::FanTraits traits_; - std::set preset_modes_{}; + std::vector preset_modes_{}; }; } // namespace template_ From cf76c3a74769f18254e24c9e6173974581966802 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:23:03 -0600 Subject: [PATCH 352/526] [web_server_idf] Reduce flash by eliminating temporary string allocations in event formatting (#11658) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- .../web_server_idf/web_server_idf.cpp | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index ac0b5bad83..0dab5e7e8c 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "esphome/core/helpers.h" #include "esphome/core/log.h" @@ -245,8 +246,8 @@ void AsyncWebServerRequest::redirect(const std::string &url) { } void AsyncWebServerRequest::init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type) { - // Set status code - use constants for common codes to avoid string allocation - const char *status = nullptr; + // Set status code - use constants for common codes, default to 500 for unknown codes + const char *status; switch (code) { case 200: status = HTTPD_200; @@ -258,9 +259,10 @@ void AsyncWebServerRequest::init_response_(AsyncWebServerResponse *rsp, int code status = HTTPD_409; break; default: + status = HTTPD_500; break; } - httpd_resp_set_status(*this, status == nullptr ? to_string(code).c_str() : status); + httpd_resp_set_status(*this, status); if (content_type && *content_type) { httpd_resp_set_type(*this, content_type); @@ -348,7 +350,13 @@ void AsyncWebServerResponse::addHeader(const char *name, const char *value) { httpd_resp_set_hdr(*this->req_, name, value); } -void AsyncResponseStream::print(float value) { this->print(to_string(value)); } +void AsyncResponseStream::print(float value) { + // Use stack buffer to avoid temporary string allocation + // Size: sign (1) + digits (10) + decimal (1) + precision (6) + exponent (5) + null (1) = 24, use 32 for safety + char buf[32]; + int len = snprintf(buf, sizeof(buf), "%f", value); + this->content_.append(buf, len); +} void AsyncResponseStream::printf(const char *fmt, ...) { va_list args; @@ -593,16 +601,19 @@ bool AsyncEventSourceResponse::try_send_nodefer(const char *message, const char event_buffer_.append(chunk_len_header); + // Use stack buffer for formatting numeric fields to avoid temporary string allocations + // Size: "retry: " (7) + max uint32 (10 digits) + CRLF (2) + null (1) = 20 bytes, use 32 for safety + constexpr size_t num_buf_size = 32; + char num_buf[num_buf_size]; + if (reconnect) { - event_buffer_.append("retry: ", sizeof("retry: ") - 1); - event_buffer_.append(to_string(reconnect)); - event_buffer_.append(CRLF_STR, CRLF_LEN); + int len = snprintf(num_buf, num_buf_size, "retry: %" PRIu32 CRLF_STR, reconnect); + event_buffer_.append(num_buf, len); } if (id) { - event_buffer_.append("id: ", sizeof("id: ") - 1); - event_buffer_.append(to_string(id)); - event_buffer_.append(CRLF_STR, CRLF_LEN); + int len = snprintf(num_buf, num_buf_size, "id: %" PRIu32 CRLF_STR, id); + event_buffer_.append(num_buf, len); } if (event && *event) { From 4a5e6576c8eac98dc9120e250325af2394e8ea8b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:29:29 -0600 Subject: [PATCH 353/526] [scheduler] Refactor call() for improved code organization (#11643) --- esphome/core/scheduler.cpp | 105 +++++++++++-------------------------- esphome/core/scheduler.h | 58 ++++++++++++++++++++ 2 files changed, 88 insertions(+), 75 deletions(-) diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 0d4715f621..11d59c2499 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -316,59 +316,37 @@ optional HOT Scheduler::next_schedule_in(uint32_t now) { return 0; return next_exec - now_64; } + +void Scheduler::full_cleanup_removed_items_() { + // We hold the lock for the entire cleanup operation because: + // 1. We're rebuilding the entire items_ list, so we need exclusive access throughout + // 2. Other threads must see either the old state or the new state, not intermediate states + // 3. The operation is already expensive (O(n)), so lock overhead is negligible + // 4. No operations inside can block or take other locks, so no deadlock risk + LockGuard guard{this->lock_}; + + std::vector> valid_items; + + // Move all non-removed items to valid_items, recycle removed ones + for (auto &item : this->items_) { + if (!is_item_removed_(item.get())) { + valid_items.push_back(std::move(item)); + } else { + // Recycle removed items + this->recycle_item_(std::move(item)); + } + } + + // Replace items_ with the filtered list + this->items_ = std::move(valid_items); + // Rebuild the heap structure since items are no longer in heap order + std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp); + this->to_remove_ = 0; +} + void HOT Scheduler::call(uint32_t now) { #ifndef ESPHOME_THREAD_SINGLE - // Process defer queue first to guarantee FIFO execution order for deferred items. - // Previously, defer() used the heap which gave undefined order for equal timestamps, - // causing race conditions on multi-core systems (ESP32, BK7200). - // With the defer queue: - // - Deferred items (delay=0) go directly to defer_queue_ in set_timer_common_ - // - Items execute in exact order they were deferred (FIFO guarantee) - // - No deferred items exist in to_add_, so processing order doesn't affect correctness - // Single-core platforms don't use this queue and fall back to the heap-based approach. - // - // Note: Items cancelled via cancel_item_locked_() are marked with remove=true but still - // processed here. They are skipped during execution by should_skip_item_(). - // This is intentional - no memory leak occurs. - // - // We use an index (defer_queue_front_) to track the read position instead of calling - // erase() on every pop, which would be O(n). The queue is processed once per loop - - // any items added during processing are left for the next loop iteration. - - // Snapshot the queue end point - only process items that existed at loop start - // Items added during processing (by callbacks or other threads) run next loop - // No lock needed: single consumer (main loop), stale read just means we process less this iteration - size_t defer_queue_end = this->defer_queue_.size(); - - while (this->defer_queue_front_ < defer_queue_end) { - std::unique_ptr item; - { - LockGuard lock(this->lock_); - // SAFETY: Moving out the unique_ptr leaves a nullptr in the vector at defer_queue_front_. - // This is intentional and safe because: - // 1. The vector is only cleaned up by cleanup_defer_queue_locked_() at the end of this function - // 2. Any code iterating defer_queue_ MUST check for nullptr items (see mark_matching_items_removed_ - // and has_cancelled_timeout_in_container_ in scheduler.h) - // 3. The lock protects concurrent access, but the nullptr remains until cleanup - item = std::move(this->defer_queue_[this->defer_queue_front_]); - this->defer_queue_front_++; - } - - // Execute callback without holding lock to prevent deadlocks - // if the callback tries to call defer() again - if (!this->should_skip_item_(item.get())) { - now = this->execute_item_(item.get(), now); - } - // Recycle the defer item after execution - this->recycle_item_(std::move(item)); - } - - // If we've consumed all items up to the snapshot point, clean up the dead space - // Single consumer (main loop), so no lock needed for this check - if (this->defer_queue_front_ >= defer_queue_end) { - LockGuard lock(this->lock_); - this->cleanup_defer_queue_locked_(); - } + this->process_defer_queue_(now); #endif /* not ESPHOME_THREAD_SINGLE */ // Convert the fresh timestamp from main loop to 64-bit for scheduler operations @@ -429,30 +407,7 @@ void HOT Scheduler::call(uint32_t now) { // If we still have too many cancelled items, do a full cleanup // This only happens if cancelled items are stuck in the middle/bottom of the heap if (this->to_remove_ >= MAX_LOGICALLY_DELETED_ITEMS) { - // We hold the lock for the entire cleanup operation because: - // 1. We're rebuilding the entire items_ list, so we need exclusive access throughout - // 2. Other threads must see either the old state or the new state, not intermediate states - // 3. The operation is already expensive (O(n)), so lock overhead is negligible - // 4. No operations inside can block or take other locks, so no deadlock risk - LockGuard guard{this->lock_}; - - std::vector> valid_items; - - // Move all non-removed items to valid_items, recycle removed ones - for (auto &item : this->items_) { - if (!is_item_removed_(item.get())) { - valid_items.push_back(std::move(item)); - } else { - // Recycle removed items - this->recycle_item_(std::move(item)); - } - } - - // Replace items_ with the filtered list - this->items_ = std::move(valid_items); - // Rebuild the heap structure since items are no longer in heap order - std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp); - this->to_remove_ = 0; + this->full_cleanup_removed_items_(); } while (!this->items_.empty()) { // Don't copy-by value yet diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index df0be0e4ce..f6ec07294d 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -263,7 +263,65 @@ class Scheduler { // Helper to recycle a SchedulerItem void recycle_item_(std::unique_ptr item); + // Helper to perform full cleanup when too many items are cancelled + void full_cleanup_removed_items_(); + #ifndef ESPHOME_THREAD_SINGLE + // Helper to process defer queue - inline for performance in hot path + inline void process_defer_queue_(uint32_t &now) { + // Process defer queue first to guarantee FIFO execution order for deferred items. + // Previously, defer() used the heap which gave undefined order for equal timestamps, + // causing race conditions on multi-core systems (ESP32, BK7200). + // With the defer queue: + // - Deferred items (delay=0) go directly to defer_queue_ in set_timer_common_ + // - Items execute in exact order they were deferred (FIFO guarantee) + // - No deferred items exist in to_add_, so processing order doesn't affect correctness + // Single-core platforms don't use this queue and fall back to the heap-based approach. + // + // Note: Items cancelled via cancel_item_locked_() are marked with remove=true but still + // processed here. They are skipped during execution by should_skip_item_(). + // This is intentional - no memory leak occurs. + // + // We use an index (defer_queue_front_) to track the read position instead of calling + // erase() on every pop, which would be O(n). The queue is processed once per loop - + // any items added during processing are left for the next loop iteration. + + // Snapshot the queue end point - only process items that existed at loop start + // Items added during processing (by callbacks or other threads) run next loop + // No lock needed: single consumer (main loop), stale read just means we process less this iteration + size_t defer_queue_end = this->defer_queue_.size(); + + while (this->defer_queue_front_ < defer_queue_end) { + std::unique_ptr item; + { + LockGuard lock(this->lock_); + // SAFETY: Moving out the unique_ptr leaves a nullptr in the vector at defer_queue_front_. + // This is intentional and safe because: + // 1. The vector is only cleaned up by cleanup_defer_queue_locked_() at the end of this function + // 2. Any code iterating defer_queue_ MUST check for nullptr items (see mark_matching_items_removed_ + // and has_cancelled_timeout_in_container_ in scheduler.h) + // 3. The lock protects concurrent access, but the nullptr remains until cleanup + item = std::move(this->defer_queue_[this->defer_queue_front_]); + this->defer_queue_front_++; + } + + // Execute callback without holding lock to prevent deadlocks + // if the callback tries to call defer() again + if (!this->should_skip_item_(item.get())) { + now = this->execute_item_(item.get(), now); + } + // Recycle the defer item after execution + this->recycle_item_(std::move(item)); + } + + // If we've consumed all items up to the snapshot point, clean up the dead space + // Single consumer (main loop), so no lock needed for this check + if (this->defer_queue_front_ >= defer_queue_end) { + LockGuard lock(this->lock_); + this->cleanup_defer_queue_locked_(); + } + } + // Helper to cleanup defer_queue_ after processing // IMPORTANT: Caller must hold the scheduler lock before calling this function. inline void cleanup_defer_queue_locked_() { From 0f0cd1f706e31c35961578647f4d63c8e04dc768 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 19:40:13 -0600 Subject: [PATCH 354/526] [core] Avoid redundant millis() calls in base_automation loop methods (#11676) --- esphome/core/base_automation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index e668a1782a..28af02a846 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -103,7 +103,7 @@ template class ForCondition : public Condition, public Co bool check_internal() { bool cond = this->condition_->check(); if (!cond) - this->last_inactive_ = millis(); + this->last_inactive_ = App.get_loop_component_start_time(); return cond; } @@ -380,7 +380,7 @@ template class WaitUntilAction : public Action, public Co if (this->num_running_ == 0) return; - auto now = millis(); + auto now = App.get_loop_component_start_time(); this->var_queue_.remove_if([&](auto &queued) { auto start = std::get(queued); From 4dd3c906635ae3964f6b12eab46bcbc7d44990f2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 20:55:17 -0600 Subject: [PATCH 355/526] [esp32_ble] Wake main loop for GAP security events (#11677) --- esphome/components/esp32_ble/ble.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index eef0db5347..d6f7e1ce43 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -581,9 +581,17 @@ void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa GAP_ADV_COMPLETE_EVENTS: // Connection events - used by ble_client case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: + enqueue_ble_event(event, param); + return; + // Security events - used by ble_client and bluetooth_proxy + // These are rare but interactive (pairing/bonding), so notify immediately GAP_SECURITY_EVENTS: enqueue_ble_event(event, param); + // Wake up main loop to process security event immediately +#ifdef USE_SOCKET_SELECT_SUPPORT + global_ble->notify_main_loop_(); +#endif return; // Ignore these GAP events as they are not relevant for our use case From a41c7b2b3c65aef775ae4d5ff70ce772e28d2c01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:16:38 -0600 Subject: [PATCH 356/526] Bump aioesphomeapi from 42.5.0 to 42.6.0 (#11682) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 660b18c933..33fa2b64eb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==42.5.0 +aioesphomeapi==42.6.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.16 # dashboard_import From 42833c85f5fd50313f7dcf965434508aeb030c67 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Nov 2025 23:16:39 -0600 Subject: [PATCH 357/526] [climate] Replace std::vector with const char* for custom fan modes and presets (#11621) --- esphome/components/api/api.proto | 4 +- esphome/components/api/api_connection.cpp | 8 +- esphome/components/api/api_pb2.cpp | 16 +- esphome/components/api/api_pb2.h | 4 +- esphome/components/bedjet/bedjet_const.h | 1 - .../bedjet/climate/bedjet_climate.cpp | 68 +++--- .../bedjet/climate/bedjet_climate.h | 12 +- esphome/components/climate/climate.cpp | 223 ++++++++++++------ esphome/components/climate/climate.h | 64 ++++- esphome/components/climate/climate_traits.h | 85 +++++-- esphome/components/demo/demo_climate.h | 22 +- esphome/components/midea/ac_adapter.cpp | 18 +- esphome/components/midea/ac_adapter.h | 14 +- esphome/components/midea/air_conditioner.cpp | 14 +- esphome/components/midea/air_conditioner.h | 8 +- .../thermostat/thermostat_climate.cpp | 47 ++-- .../thermostat/thermostat_climate.h | 2 +- esphome/components/web_server/web_server.cpp | 10 +- script/api_protobuf/api_protobuf.py | 3 +- .../climate_custom_fan_modes_and_presets.yaml | 40 ++++ .../integration/test_climate_custom_modes.py | 42 ++++ 21 files changed, 475 insertions(+), 230 deletions(-) create mode 100644 tests/integration/fixtures/climate_custom_fan_modes_and_presets.yaml create mode 100644 tests/integration/test_climate_custom_modes.py diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 20645fc47b..7a50fa6b17 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1000,9 +1000,9 @@ message ListEntitiesClimateResponse { bool supports_action = 12; // Deprecated: use feature_flags repeated ClimateFanMode supported_fan_modes = 13 [(container_pointer_no_template) = "climate::ClimateFanModeMask"]; repeated ClimateSwingMode supported_swing_modes = 14 [(container_pointer_no_template) = "climate::ClimateSwingModeMask"]; - repeated string supported_custom_fan_modes = 15 [(container_pointer) = "std::vector"]; + repeated string supported_custom_fan_modes = 15 [(container_pointer_no_template) = "std::vector"]; repeated ClimatePreset supported_presets = 16 [(container_pointer_no_template) = "climate::ClimatePresetMask"]; - repeated string supported_custom_presets = 17 [(container_pointer) = "std::vector"]; + repeated string supported_custom_presets = 17 [(container_pointer_no_template) = "std::vector"]; bool disabled_by_default = 18; string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON"]; EntityCategory entity_category = 20; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 33d5072d9c..f8490b6ef7 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -637,14 +637,14 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection } if (traits.get_supports_fan_modes() && climate->fan_mode.has_value()) resp.fan_mode = static_cast(climate->fan_mode.value()); - if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value()) { - resp.set_custom_fan_mode(StringRef(climate->custom_fan_mode.value())); + if (!traits.get_supported_custom_fan_modes().empty() && climate->has_custom_fan_mode()) { + resp.set_custom_fan_mode(StringRef(climate->get_custom_fan_mode())); } if (traits.get_supports_presets() && climate->preset.has_value()) { resp.preset = static_cast(climate->preset.value()); } - if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value()) { - resp.set_custom_preset(StringRef(climate->custom_preset.value())); + if (!traits.get_supported_custom_presets().empty() && climate->has_custom_preset()) { + resp.set_custom_preset(StringRef(climate->get_custom_preset())); } if (traits.get_supports_swing_modes()) resp.swing_mode = static_cast(climate->swing_mode); diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 0673d35518..dfa1a1320f 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -1179,14 +1179,14 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { for (const auto &it : *this->supported_swing_modes) { buffer.encode_uint32(14, static_cast(it), true); } - for (const auto &it : *this->supported_custom_fan_modes) { - buffer.encode_string(15, it, true); + for (const char *it : *this->supported_custom_fan_modes) { + buffer.encode_string(15, it, strlen(it), true); } for (const auto &it : *this->supported_presets) { buffer.encode_uint32(16, static_cast(it), true); } - for (const auto &it : *this->supported_custom_presets) { - buffer.encode_string(17, it, true); + for (const char *it : *this->supported_custom_presets) { + buffer.encode_string(17, it, strlen(it), true); } buffer.encode_bool(18, this->disabled_by_default); #ifdef USE_ENTITY_ICON @@ -1229,8 +1229,8 @@ void ListEntitiesClimateResponse::calculate_size(ProtoSize &size) const { } } if (!this->supported_custom_fan_modes->empty()) { - for (const auto &it : *this->supported_custom_fan_modes) { - size.add_length_force(1, it.size()); + for (const char *it : *this->supported_custom_fan_modes) { + size.add_length_force(1, strlen(it)); } } if (!this->supported_presets->empty()) { @@ -1239,8 +1239,8 @@ void ListEntitiesClimateResponse::calculate_size(ProtoSize &size) const { } } if (!this->supported_custom_presets->empty()) { - for (const auto &it : *this->supported_custom_presets) { - size.add_length_force(2, it.size()); + for (const char *it : *this->supported_custom_presets) { + size.add_length_force(2, strlen(it)); } } size.add_bool(2, this->disabled_by_default); diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 89f16044d7..716f1a6e9b 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1384,9 +1384,9 @@ class ListEntitiesClimateResponse final : public InfoResponseProtoMessage { bool supports_action{false}; const climate::ClimateFanModeMask *supported_fan_modes{}; const climate::ClimateSwingModeMask *supported_swing_modes{}; - const std::vector *supported_custom_fan_modes{}; + const std::vector *supported_custom_fan_modes{}; const climate::ClimatePresetMask *supported_presets{}; - const std::vector *supported_custom_presets{}; + const std::vector *supported_custom_presets{}; float visual_current_temperature_step{0.0f}; bool supports_current_humidity{false}; bool supports_target_humidity{false}; diff --git a/esphome/components/bedjet/bedjet_const.h b/esphome/components/bedjet/bedjet_const.h index 0693be1092..10f403dd1a 100644 --- a/esphome/components/bedjet/bedjet_const.h +++ b/esphome/components/bedjet/bedjet_const.h @@ -100,7 +100,6 @@ enum BedjetCommand : uint8_t { static const uint8_t BEDJET_FAN_SPEED_COUNT = 20; static constexpr const char *const BEDJET_FAN_STEP_NAMES[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_; -static const std::string BEDJET_FAN_STEP_NAME_STRINGS[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_; } // namespace bedjet } // namespace esphome diff --git a/esphome/components/bedjet/climate/bedjet_climate.cpp b/esphome/components/bedjet/climate/bedjet_climate.cpp index f22d312b5a..716d4d4241 100644 --- a/esphome/components/bedjet/climate/bedjet_climate.cpp +++ b/esphome/components/bedjet/climate/bedjet_climate.cpp @@ -8,15 +8,15 @@ namespace bedjet { using namespace esphome::climate; -static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) { +static const char *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) { if (fan_step < BEDJET_FAN_SPEED_COUNT) - return &BEDJET_FAN_STEP_NAME_STRINGS[fan_step]; + return BEDJET_FAN_STEP_NAMES[fan_step]; return nullptr; } -static uint8_t bedjet_fan_speed_to_step(const std::string &fan_step_percent) { +static uint8_t bedjet_fan_speed_to_step(const char *fan_step_percent) { for (int i = 0; i < BEDJET_FAN_SPEED_COUNT; i++) { - if (fan_step_percent == BEDJET_FAN_STEP_NAME_STRINGS[i]) { + if (strcmp(BEDJET_FAN_STEP_NAMES[i], fan_step_percent) == 0) { return i; } } @@ -48,7 +48,7 @@ void BedJetClimate::dump_config() { ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(climate_fan_mode_to_string(mode))); } for (const auto &mode : traits.get_supported_custom_fan_modes()) { - ESP_LOGCONFIG(TAG, " - %s (c)", mode.c_str()); + ESP_LOGCONFIG(TAG, " - %s (c)", mode); } ESP_LOGCONFIG(TAG, " Supported presets:"); @@ -56,7 +56,7 @@ void BedJetClimate::dump_config() { ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(climate_preset_to_string(preset))); } for (const auto &preset : traits.get_supported_custom_presets()) { - ESP_LOGCONFIG(TAG, " - %s (c)", preset.c_str()); + ESP_LOGCONFIG(TAG, " - %s (c)", preset); } } @@ -79,7 +79,7 @@ void BedJetClimate::reset_state_() { this->target_temperature = NAN; this->current_temperature = NAN; this->preset.reset(); - this->custom_preset.reset(); + this->clear_custom_preset_(); this->publish_state(); } @@ -120,7 +120,7 @@ void BedJetClimate::control(const ClimateCall &call) { if (button_result) { this->mode = mode; // We're using (custom) preset for Turbo, EXT HT, & M1-3 presets, so changing climate mode will clear those - this->custom_preset.reset(); + this->clear_custom_preset_(); this->preset.reset(); } } @@ -144,8 +144,7 @@ void BedJetClimate::control(const ClimateCall &call) { if (result) { this->mode = CLIMATE_MODE_HEAT; - this->preset = CLIMATE_PRESET_BOOST; - this->custom_preset.reset(); + this->set_preset_(CLIMATE_PRESET_BOOST); } } else if (preset == CLIMATE_PRESET_NONE && this->preset.has_value()) { if (this->mode == CLIMATE_MODE_HEAT && this->preset == CLIMATE_PRESET_BOOST) { @@ -153,7 +152,7 @@ void BedJetClimate::control(const ClimateCall &call) { result = this->parent_->send_button(heat_button(this->heating_mode_)); if (result) { this->preset.reset(); - this->custom_preset.reset(); + this->clear_custom_preset_(); } } else { ESP_LOGD(TAG, "Ignoring preset '%s' call; with current mode '%s' and preset '%s'", @@ -164,28 +163,27 @@ void BedJetClimate::control(const ClimateCall &call) { ESP_LOGW(TAG, "Unsupported preset: %d", preset); return; } - } else if (call.get_custom_preset().has_value()) { - std::string preset = *call.get_custom_preset(); + } else if (call.has_custom_preset()) { + const char *preset = call.get_custom_preset(); bool result; - if (preset == "M1") { + if (strcmp(preset, "M1") == 0) { result = this->parent_->button_memory1(); - } else if (preset == "M2") { + } else if (strcmp(preset, "M2") == 0) { result = this->parent_->button_memory2(); - } else if (preset == "M3") { + } else if (strcmp(preset, "M3") == 0) { result = this->parent_->button_memory3(); - } else if (preset == "LTD HT") { + } else if (strcmp(preset, "LTD HT") == 0) { result = this->parent_->button_heat(); - } else if (preset == "EXT HT") { + } else if (strcmp(preset, "EXT HT") == 0) { result = this->parent_->button_ext_heat(); } else { - ESP_LOGW(TAG, "Unsupported preset: %s", preset.c_str()); + ESP_LOGW(TAG, "Unsupported preset: %s", preset); return; } if (result) { - this->custom_preset = preset; - this->preset.reset(); + this->set_custom_preset_(preset); } } @@ -207,19 +205,16 @@ void BedJetClimate::control(const ClimateCall &call) { } if (result) { - this->fan_mode = fan_mode; - this->custom_fan_mode.reset(); + this->set_fan_mode_(fan_mode); } - } else if (call.get_custom_fan_mode().has_value()) { - auto fan_mode = *call.get_custom_fan_mode(); + } else if (call.has_custom_fan_mode()) { + const char *fan_mode = call.get_custom_fan_mode(); auto fan_index = bedjet_fan_speed_to_step(fan_mode); if (fan_index <= 19) { - ESP_LOGV(TAG, "[%s] Converted fan mode %s to bedjet fan step %d", this->get_name().c_str(), fan_mode.c_str(), - fan_index); + ESP_LOGV(TAG, "[%s] Converted fan mode %s to bedjet fan step %d", this->get_name().c_str(), fan_mode, fan_index); bool result = this->parent_->set_fan_index(fan_index); if (result) { - this->custom_fan_mode = fan_mode; - this->fan_mode.reset(); + this->set_custom_fan_mode_(fan_mode); } } } @@ -245,7 +240,7 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) { const auto *fan_mode_name = bedjet_fan_step_to_fan_mode(data->fan_step); if (fan_mode_name != nullptr) { - this->custom_fan_mode = *fan_mode_name; + this->set_custom_fan_mode_(fan_mode_name); } // TODO: Get biorhythm data to determine which preset (M1-3) is running, if any. @@ -255,7 +250,7 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) { this->mode = CLIMATE_MODE_OFF; this->action = CLIMATE_ACTION_IDLE; this->fan_mode = CLIMATE_FAN_OFF; - this->custom_preset.reset(); + this->clear_custom_preset_(); this->preset.reset(); break; @@ -266,7 +261,7 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) { if (this->heating_mode_ == HEAT_MODE_EXTENDED) { this->set_custom_preset_("LTD HT"); } else { - this->custom_preset.reset(); + this->clear_custom_preset_(); } break; @@ -275,7 +270,7 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) { this->action = CLIMATE_ACTION_HEATING; this->preset.reset(); if (this->heating_mode_ == HEAT_MODE_EXTENDED) { - this->custom_preset.reset(); + this->clear_custom_preset_(); } else { this->set_custom_preset_("EXT HT"); } @@ -284,20 +279,19 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) { case MODE_COOL: this->mode = CLIMATE_MODE_FAN_ONLY; this->action = CLIMATE_ACTION_COOLING; - this->custom_preset.reset(); + this->clear_custom_preset_(); this->preset.reset(); break; case MODE_DRY: this->mode = CLIMATE_MODE_DRY; this->action = CLIMATE_ACTION_DRYING; - this->custom_preset.reset(); + this->clear_custom_preset_(); this->preset.reset(); break; case MODE_TURBO: - this->preset = CLIMATE_PRESET_BOOST; - this->custom_preset.reset(); + this->set_preset_(CLIMATE_PRESET_BOOST); this->mode = CLIMATE_MODE_HEAT; this->action = CLIMATE_ACTION_HEATING; break; diff --git a/esphome/components/bedjet/climate/bedjet_climate.h b/esphome/components/bedjet/climate/bedjet_climate.h index dbbb73aeae..05f4a849e0 100644 --- a/esphome/components/bedjet/climate/bedjet_climate.h +++ b/esphome/components/bedjet/climate/bedjet_climate.h @@ -50,21 +50,13 @@ class BedJetClimate : public climate::Climate, public BedJetClient, public Polli // Climate doesn't have a "TURBO" mode, but we can use the BOOST preset instead. climate::CLIMATE_PRESET_BOOST, }); + // String literals are stored in rodata and valid for program lifetime traits.set_supported_custom_presets({ - // We could fetch biodata from bedjet and set these names that way. - // But then we have to invert the lookup in order to send the right preset. - // For now, we can leave them as M1-3 to match the remote buttons. - // EXT HT added to match remote button. - "EXT HT", + this->heating_mode_ == HEAT_MODE_EXTENDED ? "LTD HT" : "EXT HT", "M1", "M2", "M3", }); - if (this->heating_mode_ == HEAT_MODE_EXTENDED) { - traits.add_supported_custom_preset("LTD HT"); - } else { - traits.add_supported_custom_preset("EXT HT"); - } traits.set_visual_min_temperature(19.0); traits.set_visual_max_temperature(43.0); traits.set_visual_temperature_step(1.0); diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 944934edbf..7df38758dc 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -50,21 +50,21 @@ void ClimateCall::perform() { const LogString *mode_s = climate_mode_to_string(*this->mode_); ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(mode_s)); } - if (this->custom_fan_mode_.has_value()) { + if (this->custom_fan_mode_ != nullptr) { this->fan_mode_.reset(); - ESP_LOGD(TAG, " Custom Fan: %s", this->custom_fan_mode_.value().c_str()); + ESP_LOGD(TAG, " Custom Fan: %s", this->custom_fan_mode_); } if (this->fan_mode_.has_value()) { - this->custom_fan_mode_.reset(); + this->custom_fan_mode_ = nullptr; const LogString *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_); ESP_LOGD(TAG, " Fan: %s", LOG_STR_ARG(fan_mode_s)); } - if (this->custom_preset_.has_value()) { + if (this->custom_preset_ != nullptr) { this->preset_.reset(); - ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset_.value().c_str()); + ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset_); } if (this->preset_.has_value()) { - this->custom_preset_.reset(); + this->custom_preset_ = nullptr; const LogString *preset_s = climate_preset_to_string(*this->preset_); ESP_LOGD(TAG, " Preset: %s", LOG_STR_ARG(preset_s)); } @@ -96,11 +96,10 @@ void ClimateCall::validate_() { this->mode_.reset(); } } - if (this->custom_fan_mode_.has_value()) { - auto custom_fan_mode = *this->custom_fan_mode_; - if (!traits.supports_custom_fan_mode(custom_fan_mode)) { - ESP_LOGW(TAG, " Fan Mode %s not supported", custom_fan_mode.c_str()); - this->custom_fan_mode_.reset(); + if (this->custom_fan_mode_ != nullptr) { + if (!traits.supports_custom_fan_mode(this->custom_fan_mode_)) { + ESP_LOGW(TAG, " Fan Mode %s not supported", this->custom_fan_mode_); + this->custom_fan_mode_ = nullptr; } } else if (this->fan_mode_.has_value()) { auto fan_mode = *this->fan_mode_; @@ -109,11 +108,10 @@ void ClimateCall::validate_() { this->fan_mode_.reset(); } } - if (this->custom_preset_.has_value()) { - auto custom_preset = *this->custom_preset_; - if (!traits.supports_custom_preset(custom_preset)) { - ESP_LOGW(TAG, " Preset %s not supported", custom_preset.c_str()); - this->custom_preset_.reset(); + if (this->custom_preset_ != nullptr) { + if (!traits.supports_custom_preset(this->custom_preset_)) { + ESP_LOGW(TAG, " Preset %s not supported", this->custom_preset_); + this->custom_preset_ = nullptr; } } else if (this->preset_.has_value()) { auto preset = *this->preset_; @@ -186,26 +184,29 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) { ClimateCall &ClimateCall::set_fan_mode(ClimateFanMode fan_mode) { this->fan_mode_ = fan_mode; - this->custom_fan_mode_.reset(); + this->custom_fan_mode_ = nullptr; return *this; } -ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) { +ClimateCall &ClimateCall::set_fan_mode(const char *custom_fan_mode) { + // Check if it's a standard enum mode first for (const auto &mode_entry : CLIMATE_FAN_MODES_BY_STR) { - if (str_equals_case_insensitive(fan_mode, mode_entry.str)) { - this->set_fan_mode(static_cast(mode_entry.value)); - return *this; + if (str_equals_case_insensitive(custom_fan_mode, mode_entry.str)) { + return this->set_fan_mode(static_cast(mode_entry.value)); } } - if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) { - this->custom_fan_mode_ = fan_mode; + // Find the matching pointer from parent climate device + if (const char *mode_ptr = this->parent_->find_custom_fan_mode_(custom_fan_mode)) { + this->custom_fan_mode_ = mode_ptr; this->fan_mode_.reset(); - } else { - ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str()); + return *this; } + ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), custom_fan_mode); return *this; } +ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) { return this->set_fan_mode(fan_mode.c_str()); } + ClimateCall &ClimateCall::set_fan_mode(optional fan_mode) { if (fan_mode.has_value()) { this->set_fan_mode(fan_mode.value()); @@ -215,26 +216,29 @@ ClimateCall &ClimateCall::set_fan_mode(optional fan_mode) { ClimateCall &ClimateCall::set_preset(ClimatePreset preset) { this->preset_ = preset; - this->custom_preset_.reset(); + this->custom_preset_ = nullptr; return *this; } -ClimateCall &ClimateCall::set_preset(const std::string &preset) { +ClimateCall &ClimateCall::set_preset(const char *custom_preset) { + // Check if it's a standard enum preset first for (const auto &preset_entry : CLIMATE_PRESETS_BY_STR) { - if (str_equals_case_insensitive(preset, preset_entry.str)) { - this->set_preset(static_cast(preset_entry.value)); - return *this; + if (str_equals_case_insensitive(custom_preset, preset_entry.str)) { + return this->set_preset(static_cast(preset_entry.value)); } } - if (this->parent_->get_traits().supports_custom_preset(preset)) { - this->custom_preset_ = preset; + // Find the matching pointer from parent climate device + if (const char *preset_ptr = this->parent_->find_custom_preset_(custom_preset)) { + this->custom_preset_ = preset_ptr; this->preset_.reset(); - } else { - ESP_LOGW(TAG, "'%s' - Unrecognized preset %s", this->parent_->get_name().c_str(), preset.c_str()); + return *this; } + ESP_LOGW(TAG, "'%s' - Unrecognized preset %s", this->parent_->get_name().c_str(), custom_preset); return *this; } +ClimateCall &ClimateCall::set_preset(const std::string &preset) { return this->set_preset(preset.c_str()); } + ClimateCall &ClimateCall::set_preset(optional preset) { if (preset.has_value()) { this->set_preset(preset.value()); @@ -287,8 +291,6 @@ const optional &ClimateCall::get_mode() const { return this->mode_; const optional &ClimateCall::get_fan_mode() const { return this->fan_mode_; } const optional &ClimateCall::get_swing_mode() const { return this->swing_mode_; } const optional &ClimateCall::get_preset() const { return this->preset_; } -const optional &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; } -const optional &ClimateCall::get_custom_preset() const { return this->custom_preset_; } ClimateCall &ClimateCall::set_target_temperature_high(optional target_temperature_high) { this->target_temperature_high_ = target_temperature_high; @@ -317,13 +319,13 @@ ClimateCall &ClimateCall::set_mode(optional mode) { ClimateCall &ClimateCall::set_fan_mode(optional fan_mode) { this->fan_mode_ = fan_mode; - this->custom_fan_mode_.reset(); + this->custom_fan_mode_ = nullptr; return *this; } ClimateCall &ClimateCall::set_preset(optional preset) { this->preset_ = preset; - this->custom_preset_.reset(); + this->custom_preset_ = nullptr; return *this; } @@ -382,13 +384,13 @@ void Climate::save_state_() { state.uses_custom_fan_mode = false; state.fan_mode = this->fan_mode.value(); } - if (!traits.get_supported_custom_fan_modes().empty() && custom_fan_mode.has_value()) { + if (!traits.get_supported_custom_fan_modes().empty() && this->has_custom_fan_mode()) { state.uses_custom_fan_mode = true; const auto &supported = traits.get_supported_custom_fan_modes(); // std::vector maintains insertion order size_t i = 0; - for (const auto &mode : supported) { - if (mode == custom_fan_mode) { + for (const char *mode : supported) { + if (strcmp(mode, this->custom_fan_mode_) == 0) { state.custom_fan_mode = i; break; } @@ -399,13 +401,13 @@ void Climate::save_state_() { state.uses_custom_preset = false; state.preset = this->preset.value(); } - if (!traits.get_supported_custom_presets().empty() && custom_preset.has_value()) { + if (!traits.get_supported_custom_presets().empty() && this->has_custom_preset()) { state.uses_custom_preset = true; const auto &supported = traits.get_supported_custom_presets(); // std::vector maintains insertion order size_t i = 0; - for (const auto &preset : supported) { - if (preset == custom_preset) { + for (const char *preset : supported) { + if (strcmp(preset, this->custom_preset_) == 0) { state.custom_preset = i; break; } @@ -430,14 +432,14 @@ void Climate::publish_state() { if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) { ESP_LOGD(TAG, " Fan Mode: %s", LOG_STR_ARG(climate_fan_mode_to_string(this->fan_mode.value()))); } - if (!traits.get_supported_custom_fan_modes().empty() && this->custom_fan_mode.has_value()) { - ESP_LOGD(TAG, " Custom Fan Mode: %s", this->custom_fan_mode.value().c_str()); + if (!traits.get_supported_custom_fan_modes().empty() && this->has_custom_fan_mode()) { + ESP_LOGD(TAG, " Custom Fan Mode: %s", this->custom_fan_mode_); } if (traits.get_supports_presets() && this->preset.has_value()) { ESP_LOGD(TAG, " Preset: %s", LOG_STR_ARG(climate_preset_to_string(this->preset.value()))); } - if (!traits.get_supported_custom_presets().empty() && this->custom_preset.has_value()) { - ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset.value().c_str()); + if (!traits.get_supported_custom_presets().empty() && this->has_custom_preset()) { + ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset_); } if (traits.get_supports_swing_modes()) { ESP_LOGD(TAG, " Swing Mode: %s", LOG_STR_ARG(climate_swing_mode_to_string(this->swing_mode))); @@ -527,7 +529,7 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) { if (this->uses_custom_fan_mode) { if (this->custom_fan_mode < traits.get_supported_custom_fan_modes().size()) { call.fan_mode_.reset(); - call.custom_fan_mode_ = *std::next(traits.get_supported_custom_fan_modes().cbegin(), this->custom_fan_mode); + call.custom_fan_mode_ = traits.get_supported_custom_fan_modes()[this->custom_fan_mode]; } } else if (traits.supports_fan_mode(this->fan_mode)) { call.set_fan_mode(this->fan_mode); @@ -535,7 +537,7 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) { if (this->uses_custom_preset) { if (this->custom_preset < traits.get_supported_custom_presets().size()) { call.preset_.reset(); - call.custom_preset_ = *std::next(traits.get_supported_custom_presets().cbegin(), this->custom_preset); + call.custom_preset_ = traits.get_supported_custom_presets()[this->custom_preset]; } } else if (traits.supports_preset(this->preset)) { call.set_preset(this->preset); @@ -562,20 +564,20 @@ void ClimateDeviceRestoreState::apply(Climate *climate) { if (this->uses_custom_fan_mode) { if (this->custom_fan_mode < traits.get_supported_custom_fan_modes().size()) { climate->fan_mode.reset(); - climate->custom_fan_mode = *std::next(traits.get_supported_custom_fan_modes().cbegin(), this->custom_fan_mode); + climate->custom_fan_mode_ = traits.get_supported_custom_fan_modes()[this->custom_fan_mode]; } } else if (traits.supports_fan_mode(this->fan_mode)) { climate->fan_mode = this->fan_mode; - climate->custom_fan_mode.reset(); + climate->clear_custom_fan_mode_(); } if (this->uses_custom_preset) { if (this->custom_preset < traits.get_supported_custom_presets().size()) { climate->preset.reset(); - climate->custom_preset = *std::next(traits.get_supported_custom_presets().cbegin(), this->custom_preset); + climate->custom_preset_ = traits.get_supported_custom_presets()[this->custom_preset]; } } else if (traits.supports_preset(this->preset)) { climate->preset = this->preset; - climate->custom_preset.reset(); + climate->clear_custom_preset_(); } if (traits.supports_swing_mode(this->swing_mode)) { climate->swing_mode = this->swing_mode; @@ -583,28 +585,107 @@ void ClimateDeviceRestoreState::apply(Climate *climate) { climate->publish_state(); } -template bool set_alternative(optional &dst, optional &alt, const T1 &src) { - bool is_changed = alt.has_value(); - alt.reset(); - if (is_changed || dst != src) { - dst = src; - is_changed = true; +/** Template helper for setting primary modes (fan_mode, preset) with mutual exclusion. + * + * Climate devices have mutually exclusive mode pairs: + * - fan_mode (enum) vs custom_fan_mode_ (const char*) + * - preset (enum) vs custom_preset_ (const char*) + * + * Only one mode in each pair can be active at a time. This helper ensures setting a primary + * mode automatically clears its corresponding custom mode. + * + * Example state transitions: + * Before: custom_fan_mode_="Turbo", fan_mode=nullopt + * Call: set_fan_mode_(CLIMATE_FAN_HIGH) + * After: custom_fan_mode_=nullptr, fan_mode=CLIMATE_FAN_HIGH + * + * @param primary The primary mode optional (fan_mode or preset) + * @param custom_ptr Reference to the custom mode pointer (custom_fan_mode_ or custom_preset_) + * @param value The new primary mode value to set + * @return true if state changed, false if already set to this value + */ +template bool set_primary_mode(optional &primary, const char *&custom_ptr, T value) { + // Clear the custom mode (mutual exclusion) + bool changed = custom_ptr != nullptr; + custom_ptr = nullptr; + // Set the primary mode + if (changed || !primary.has_value() || primary.value() != value) { + primary = value; + return true; } - return is_changed; + return false; +} + +/** Template helper for setting custom modes (custom_fan_mode_, custom_preset_) with mutual exclusion. + * + * This helper ensures setting a custom mode automatically clears its corresponding primary mode. + * It also validates that the custom mode exists in the device's supported modes (lifetime safety). + * + * Example state transitions: + * Before: fan_mode=CLIMATE_FAN_HIGH, custom_fan_mode_=nullptr + * Call: set_custom_fan_mode_("Turbo") + * After: fan_mode=nullopt, custom_fan_mode_="Turbo" (pointer from traits) + * + * Lifetime Safety: + * - found_ptr must come from traits.find_custom_*_mode_() + * - Only pointers found in traits are stored, ensuring they remain valid + * - Prevents dangling pointers from temporary strings + * + * @param custom_ptr Reference to the custom mode pointer to set + * @param primary The primary mode optional to clear + * @param found_ptr The validated pointer from traits (nullptr if not found) + * @param has_custom Whether a custom mode is currently active + * @return true if state changed, false otherwise + */ +template +bool set_custom_mode(const char *&custom_ptr, optional &primary, const char *found_ptr, bool has_custom) { + if (found_ptr != nullptr) { + // Clear the primary mode (mutual exclusion) + bool changed = primary.has_value(); + primary.reset(); + // Set the custom mode (pointer is validated by caller from traits) + if (changed || custom_ptr != found_ptr) { + custom_ptr = found_ptr; + return true; + } + return false; + } + // Mode not found in supported modes, clear it if currently set + if (has_custom) { + custom_ptr = nullptr; + return true; + } + return false; } bool Climate::set_fan_mode_(ClimateFanMode mode) { - return set_alternative(this->fan_mode, this->custom_fan_mode, mode); + return set_primary_mode(this->fan_mode, this->custom_fan_mode_, mode); } -bool Climate::set_custom_fan_mode_(const std::string &mode) { - return set_alternative(this->custom_fan_mode, this->fan_mode, mode); +bool Climate::set_custom_fan_mode_(const char *mode) { + auto traits = this->get_traits(); + return set_custom_mode(this->custom_fan_mode_, this->fan_mode, traits.find_custom_fan_mode_(mode), + this->has_custom_fan_mode()); } -bool Climate::set_preset_(ClimatePreset preset) { return set_alternative(this->preset, this->custom_preset, preset); } +void Climate::clear_custom_fan_mode_() { this->custom_fan_mode_ = nullptr; } -bool Climate::set_custom_preset_(const std::string &preset) { - return set_alternative(this->custom_preset, this->preset, preset); +bool Climate::set_preset_(ClimatePreset preset) { return set_primary_mode(this->preset, this->custom_preset_, preset); } + +bool Climate::set_custom_preset_(const char *preset) { + auto traits = this->get_traits(); + return set_custom_mode(this->custom_preset_, this->preset, traits.find_custom_preset_(preset), + this->has_custom_preset()); +} + +void Climate::clear_custom_preset_() { this->custom_preset_ = nullptr; } + +const char *Climate::find_custom_fan_mode_(const char *custom_fan_mode) { + return this->get_traits().find_custom_fan_mode_(custom_fan_mode); +} + +const char *Climate::find_custom_preset_(const char *custom_preset) { + return this->get_traits().find_custom_preset_(custom_preset); } void Climate::dump_traits_(const char *tag) { @@ -656,8 +737,8 @@ void Climate::dump_traits_(const char *tag) { } if (!traits.get_supported_custom_fan_modes().empty()) { ESP_LOGCONFIG(tag, " Supported custom fan modes:"); - for (const std::string &s : traits.get_supported_custom_fan_modes()) - ESP_LOGCONFIG(tag, " - %s", s.c_str()); + for (const char *s : traits.get_supported_custom_fan_modes()) + ESP_LOGCONFIG(tag, " - %s", s); } if (!traits.get_supported_presets().empty()) { ESP_LOGCONFIG(tag, " Supported presets:"); @@ -666,8 +747,8 @@ void Climate::dump_traits_(const char *tag) { } if (!traits.get_supported_custom_presets().empty()) { ESP_LOGCONFIG(tag, " Supported custom presets:"); - for (const std::string &s : traits.get_supported_custom_presets()) - ESP_LOGCONFIG(tag, " - %s", s.c_str()); + for (const char *s : traits.get_supported_custom_presets()) + ESP_LOGCONFIG(tag, " - %s", s); } if (!traits.get_supported_swing_modes().empty()) { ESP_LOGCONFIG(tag, " Supported swing modes:"); diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index 0c3e3ebe16..b277877c3e 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -77,6 +77,8 @@ class ClimateCall { ClimateCall &set_fan_mode(const std::string &fan_mode); /// Set the fan mode of the climate device based on a string. ClimateCall &set_fan_mode(optional fan_mode); + /// Set the custom fan mode of the climate device. + ClimateCall &set_fan_mode(const char *custom_fan_mode); /// Set the swing mode of the climate device. ClimateCall &set_swing_mode(ClimateSwingMode swing_mode); /// Set the swing mode of the climate device. @@ -91,6 +93,8 @@ class ClimateCall { ClimateCall &set_preset(const std::string &preset); /// Set the preset of the climate device based on a string. ClimateCall &set_preset(optional preset); + /// Set the custom preset of the climate device. + ClimateCall &set_preset(const char *custom_preset); void perform(); @@ -103,8 +107,10 @@ class ClimateCall { const optional &get_fan_mode() const; const optional &get_swing_mode() const; const optional &get_preset() const; - const optional &get_custom_fan_mode() const; - const optional &get_custom_preset() const; + const char *get_custom_fan_mode() const { return this->custom_fan_mode_; } + const char *get_custom_preset() const { return this->custom_preset_; } + bool has_custom_fan_mode() const { return this->custom_fan_mode_ != nullptr; } + bool has_custom_preset() const { return this->custom_preset_ != nullptr; } protected: void validate_(); @@ -118,8 +124,10 @@ class ClimateCall { optional fan_mode_; optional swing_mode_; optional preset_; - optional custom_fan_mode_; - optional custom_preset_; + + private: + const char *custom_fan_mode_{nullptr}; + const char *custom_preset_{nullptr}; }; /// Struct used to save the state of the climate device in restore memory. @@ -212,6 +220,12 @@ class Climate : public EntityBase { void set_visual_min_humidity_override(float visual_min_humidity_override); void set_visual_max_humidity_override(float visual_max_humidity_override); + /// Check if a custom fan mode is currently active. + bool has_custom_fan_mode() const { return this->custom_fan_mode_ != nullptr; } + + /// Check if a custom preset is currently active. + bool has_custom_preset() const { return this->custom_preset_ != nullptr; } + /// The current temperature of the climate device, as reported from the integration. float current_temperature{NAN}; @@ -238,12 +252,6 @@ class Climate : public EntityBase { /// The active preset of the climate device. optional preset; - /// The active custom fan mode of the climate device. - optional custom_fan_mode; - - /// The active custom preset mode of the climate device. - optional custom_preset; - /// The active mode of the climate device. ClimateMode mode{CLIMATE_MODE_OFF}; @@ -253,20 +261,37 @@ class Climate : public EntityBase { /// The active swing mode of the climate device. ClimateSwingMode swing_mode{CLIMATE_SWING_OFF}; + /// Get the active custom fan mode (read-only access). + const char *get_custom_fan_mode() const { return this->custom_fan_mode_; } + + /// Get the active custom preset (read-only access). + const char *get_custom_preset() const { return this->custom_preset_; } + protected: friend ClimateCall; + friend struct ClimateDeviceRestoreState; /// Set fan mode. Reset custom fan mode. Return true if fan mode has been changed. bool set_fan_mode_(ClimateFanMode mode); /// Set custom fan mode. Reset primary fan mode. Return true if fan mode has been changed. - bool set_custom_fan_mode_(const std::string &mode); + bool set_custom_fan_mode_(const char *mode); + /// Clear custom fan mode. + void clear_custom_fan_mode_(); /// Set preset. Reset custom preset. Return true if preset has been changed. bool set_preset_(ClimatePreset preset); /// Set custom preset. Reset primary preset. Return true if preset has been changed. - bool set_custom_preset_(const std::string &preset); + bool set_custom_preset_(const char *preset); + /// Clear custom preset. + void clear_custom_preset_(); + + /// Find and return the matching custom fan mode pointer from traits, or nullptr if not found. + const char *find_custom_fan_mode_(const char *custom_fan_mode); + + /// Find and return the matching custom preset pointer from traits, or nullptr if not found. + const char *find_custom_preset_(const char *custom_preset); /** Get the default traits of this climate device. * @@ -303,6 +328,21 @@ class Climate : public EntityBase { optional visual_current_temperature_step_override_{}; optional visual_min_humidity_override_{}; optional visual_max_humidity_override_{}; + + private: + /** The active custom fan mode (private - enforces use of safe setters). + * + * Points to an entry in traits.supported_custom_fan_modes_ or nullptr. + * Use get_custom_fan_mode() to read, set_custom_fan_mode_() to modify. + */ + const char *custom_fan_mode_{nullptr}; + + /** The active custom preset (private - enforces use of safe setters). + * + * Points to an entry in traits.supported_custom_presets_ or nullptr. + * Use get_custom_preset() to read, set_custom_preset_() to modify. + */ + const char *custom_preset_{nullptr}; }; } // namespace climate diff --git a/esphome/components/climate/climate_traits.h b/esphome/components/climate/climate_traits.h index 1161a54f4e..0eecf9789f 100644 --- a/esphome/components/climate/climate_traits.h +++ b/esphome/components/climate/climate_traits.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "climate_mode.h" #include "esphome/core/finite_set_mask.h" @@ -18,16 +19,25 @@ using ClimateSwingModeMask = FiniteSetMask>; using ClimatePresetMask = FiniteSetMask>; -// Lightweight linear search for small vectors (1-20 items) +// Lightweight linear search for small vectors (1-20 items) of const char* pointers // Avoids std::find template overhead -template inline bool vector_contains(const std::vector &vec, const T &value) { - for (const auto &item : vec) { - if (item == value) +inline bool vector_contains(const std::vector &vec, const char *value) { + for (const char *item : vec) { + if (strcmp(item, value) == 0) return true; } return false; } +// Find and return matching pointer from vector, or nullptr if not found +inline const char *vector_find(const std::vector &vec, const char *value) { + for (const char *item : vec) { + if (strcmp(item, value) == 0) + return item; + } + return nullptr; +} + /** This class contains all static data for climate devices. * * All climate devices must support these features: @@ -55,7 +65,11 @@ template inline bool vector_contains(const std::vector &vec, cons * - temperature step - the step with which to increase/decrease target temperature. * This also affects with how many decimal places the temperature is shown */ +class Climate; // Forward declaration + class ClimateTraits { + friend class Climate; // Allow Climate to access protected find methods + public: /// Get/set feature flags (see ClimateFeatures enum in climate_mode.h) uint32_t get_feature_flags() const { return this->feature_flags_; } @@ -128,47 +142,61 @@ class ClimateTraits { void set_supported_fan_modes(ClimateFanModeMask modes) { this->supported_fan_modes_ = modes; } void add_supported_fan_mode(ClimateFanMode mode) { this->supported_fan_modes_.insert(mode); } - void add_supported_custom_fan_mode(const std::string &mode) { this->supported_custom_fan_modes_.push_back(mode); } bool supports_fan_mode(ClimateFanMode fan_mode) const { return this->supported_fan_modes_.count(fan_mode); } bool get_supports_fan_modes() const { return !this->supported_fan_modes_.empty() || !this->supported_custom_fan_modes_.empty(); } const ClimateFanModeMask &get_supported_fan_modes() const { return this->supported_fan_modes_; } - void set_supported_custom_fan_modes(std::vector supported_custom_fan_modes) { - this->supported_custom_fan_modes_ = std::move(supported_custom_fan_modes); + void set_supported_custom_fan_modes(std::initializer_list modes) { + this->supported_custom_fan_modes_ = modes; } - void set_supported_custom_fan_modes(std::initializer_list modes) { + void set_supported_custom_fan_modes(const std::vector &modes) { this->supported_custom_fan_modes_ = modes; } template void set_supported_custom_fan_modes(const char *const (&modes)[N]) { this->supported_custom_fan_modes_.assign(modes, modes + N); } - const std::vector &get_supported_custom_fan_modes() const { return this->supported_custom_fan_modes_; } - bool supports_custom_fan_mode(const std::string &custom_fan_mode) const { + + // Deleted overloads to catch incorrect std::string usage at compile time with clear error messages + void set_supported_custom_fan_modes(const std::vector &modes) = delete; + void set_supported_custom_fan_modes(std::initializer_list modes) = delete; + + const std::vector &get_supported_custom_fan_modes() const { return this->supported_custom_fan_modes_; } + bool supports_custom_fan_mode(const char *custom_fan_mode) const { return vector_contains(this->supported_custom_fan_modes_, custom_fan_mode); } + bool supports_custom_fan_mode(const std::string &custom_fan_mode) const { + return this->supports_custom_fan_mode(custom_fan_mode.c_str()); + } void set_supported_presets(ClimatePresetMask presets) { this->supported_presets_ = presets; } void add_supported_preset(ClimatePreset preset) { this->supported_presets_.insert(preset); } - void add_supported_custom_preset(const std::string &preset) { this->supported_custom_presets_.push_back(preset); } bool supports_preset(ClimatePreset preset) const { return this->supported_presets_.count(preset); } bool get_supports_presets() const { return !this->supported_presets_.empty(); } const ClimatePresetMask &get_supported_presets() const { return this->supported_presets_; } - void set_supported_custom_presets(std::vector supported_custom_presets) { - this->supported_custom_presets_ = std::move(supported_custom_presets); + void set_supported_custom_presets(std::initializer_list presets) { + this->supported_custom_presets_ = presets; } - void set_supported_custom_presets(std::initializer_list presets) { + void set_supported_custom_presets(const std::vector &presets) { this->supported_custom_presets_ = presets; } template void set_supported_custom_presets(const char *const (&presets)[N]) { this->supported_custom_presets_.assign(presets, presets + N); } - const std::vector &get_supported_custom_presets() const { return this->supported_custom_presets_; } - bool supports_custom_preset(const std::string &custom_preset) const { + + // Deleted overloads to catch incorrect std::string usage at compile time with clear error messages + void set_supported_custom_presets(const std::vector &presets) = delete; + void set_supported_custom_presets(std::initializer_list presets) = delete; + + const std::vector &get_supported_custom_presets() const { return this->supported_custom_presets_; } + bool supports_custom_preset(const char *custom_preset) const { return vector_contains(this->supported_custom_presets_, custom_preset); } + bool supports_custom_preset(const std::string &custom_preset) const { + return this->supports_custom_preset(custom_preset.c_str()); + } void set_supported_swing_modes(ClimateSwingModeMask modes) { this->supported_swing_modes_ = modes; } void add_supported_swing_mode(ClimateSwingMode mode) { this->supported_swing_modes_.insert(mode); } @@ -227,6 +255,18 @@ class ClimateTraits { } } + /// Find and return the matching custom fan mode pointer from supported modes, or nullptr if not found + /// This is protected as it's an implementation detail - use Climate::find_custom_fan_mode_() instead + const char *find_custom_fan_mode_(const char *custom_fan_mode) const { + return vector_find(this->supported_custom_fan_modes_, custom_fan_mode); + } + + /// Find and return the matching custom preset pointer from supported presets, or nullptr if not found + /// This is protected as it's an implementation detail - use Climate::find_custom_preset_() instead + const char *find_custom_preset_(const char *custom_preset) const { + return vector_find(this->supported_custom_presets_, custom_preset); + } + uint32_t feature_flags_{0}; float visual_min_temperature_{10}; float visual_max_temperature_{30}; @@ -239,8 +279,17 @@ class ClimateTraits { climate::ClimateFanModeMask supported_fan_modes_; climate::ClimateSwingModeMask supported_swing_modes_; climate::ClimatePresetMask supported_presets_; - std::vector supported_custom_fan_modes_; - std::vector supported_custom_presets_; + + /** Custom mode storage using const char* pointers to eliminate std::string overhead. + * + * Pointers must remain valid for the ClimateTraits lifetime. Safe patterns: + * - String literals: set_supported_custom_fan_modes({"Turbo", "Silent"}) + * - Static const data: static const char* MODE = "Eco"; + * + * Climate class setters validate pointers are from these vectors before storing. + */ + std::vector supported_custom_fan_modes_; + std::vector supported_custom_presets_; }; } // namespace climate diff --git a/esphome/components/demo/demo_climate.h b/esphome/components/demo/demo_climate.h index 84b16e7ec5..e2dfb0142b 100644 --- a/esphome/components/demo/demo_climate.h +++ b/esphome/components/demo/demo_climate.h @@ -28,16 +28,16 @@ class DemoClimate : public climate::Climate, public Component { this->mode = climate::CLIMATE_MODE_AUTO; this->action = climate::CLIMATE_ACTION_COOLING; this->fan_mode = climate::CLIMATE_FAN_HIGH; - this->custom_preset = {"My Preset"}; + this->set_custom_preset_("My Preset"); break; case DemoClimateType::TYPE_3: this->current_temperature = 21.5; this->target_temperature_low = 21.0; this->target_temperature_high = 22.5; this->mode = climate::CLIMATE_MODE_HEAT_COOL; - this->custom_fan_mode = {"Auto Low"}; + this->set_custom_fan_mode_("Auto Low"); this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; - this->preset = climate::CLIMATE_PRESET_AWAY; + this->set_preset_(climate::CLIMATE_PRESET_AWAY); break; } this->publish_state(); @@ -58,23 +58,19 @@ class DemoClimate : public climate::Climate, public Component { this->target_temperature_high = *call.get_target_temperature_high(); } if (call.get_fan_mode().has_value()) { - this->fan_mode = *call.get_fan_mode(); - this->custom_fan_mode.reset(); + this->set_fan_mode_(*call.get_fan_mode()); } if (call.get_swing_mode().has_value()) { this->swing_mode = *call.get_swing_mode(); } - if (call.get_custom_fan_mode().has_value()) { - this->custom_fan_mode = *call.get_custom_fan_mode(); - this->fan_mode.reset(); + if (call.has_custom_fan_mode()) { + this->set_custom_fan_mode_(call.get_custom_fan_mode()); } if (call.get_preset().has_value()) { - this->preset = *call.get_preset(); - this->custom_preset.reset(); + this->set_preset_(*call.get_preset()); } - if (call.get_custom_preset().has_value()) { - this->custom_preset = *call.get_custom_preset(); - this->preset.reset(); + if (call.has_custom_preset()) { + this->set_custom_preset_(call.get_custom_preset()); } this->publish_state(); } diff --git a/esphome/components/midea/ac_adapter.cpp b/esphome/components/midea/ac_adapter.cpp index 2837713c35..d903db4a1b 100644 --- a/esphome/components/midea/ac_adapter.cpp +++ b/esphome/components/midea/ac_adapter.cpp @@ -8,9 +8,9 @@ namespace midea { namespace ac { const char *const Constants::TAG = "midea"; -const std::string Constants::FREEZE_PROTECTION = "freeze protection"; -const std::string Constants::SILENT = "silent"; -const std::string Constants::TURBO = "turbo"; +const char *const Constants::FREEZE_PROTECTION = "freeze protection"; +const char *const Constants::SILENT = "silent"; +const char *const Constants::TURBO = "turbo"; ClimateMode Converters::to_climate_mode(MideaMode mode) { switch (mode) { @@ -108,7 +108,7 @@ bool Converters::is_custom_midea_fan_mode(MideaFanMode mode) { } } -const std::string &Converters::to_custom_climate_fan_mode(MideaFanMode mode) { +const char *Converters::to_custom_climate_fan_mode(MideaFanMode mode) { switch (mode) { case MideaFanMode::FAN_SILENT: return Constants::SILENT; @@ -117,8 +117,8 @@ const std::string &Converters::to_custom_climate_fan_mode(MideaFanMode mode) { } } -MideaFanMode Converters::to_midea_fan_mode(const std::string &mode) { - if (mode == Constants::SILENT) +MideaFanMode Converters::to_midea_fan_mode(const char *mode) { + if (strcmp(mode, Constants::SILENT) == 0) return MideaFanMode::FAN_SILENT; return MideaFanMode::FAN_TURBO; } @@ -151,9 +151,9 @@ ClimatePreset Converters::to_climate_preset(MideaPreset preset) { bool Converters::is_custom_midea_preset(MideaPreset preset) { return preset == MideaPreset::PRESET_FREEZE_PROTECTION; } -const std::string &Converters::to_custom_climate_preset(MideaPreset preset) { return Constants::FREEZE_PROTECTION; } +const char *Converters::to_custom_climate_preset(MideaPreset preset) { return Constants::FREEZE_PROTECTION; } -MideaPreset Converters::to_midea_preset(const std::string &preset) { return MideaPreset::PRESET_FREEZE_PROTECTION; } +MideaPreset Converters::to_midea_preset(const char *preset) { return MideaPreset::PRESET_FREEZE_PROTECTION; } void Converters::to_climate_traits(ClimateTraits &traits, const dudanov::midea::ac::Capabilities &capabilities) { if (capabilities.supportAutoMode()) @@ -169,7 +169,7 @@ void Converters::to_climate_traits(ClimateTraits &traits, const dudanov::midea:: if (capabilities.supportEcoPreset()) traits.add_supported_preset(ClimatePreset::CLIMATE_PRESET_ECO); if (capabilities.supportFrostProtectionPreset()) - traits.add_supported_custom_preset(Constants::FREEZE_PROTECTION); + traits.set_supported_custom_presets({Constants::FREEZE_PROTECTION}); } } // namespace ac diff --git a/esphome/components/midea/ac_adapter.h b/esphome/components/midea/ac_adapter.h index c17894ae31..b0589a37f9 100644 --- a/esphome/components/midea/ac_adapter.h +++ b/esphome/components/midea/ac_adapter.h @@ -20,9 +20,9 @@ using MideaPreset = dudanov::midea::ac::Preset; class Constants { public: static const char *const TAG; - static const std::string FREEZE_PROTECTION; - static const std::string SILENT; - static const std::string TURBO; + static const char *const FREEZE_PROTECTION; + static const char *const SILENT; + static const char *const TURBO; }; class Converters { @@ -32,15 +32,15 @@ class Converters { static MideaSwingMode to_midea_swing_mode(ClimateSwingMode mode); static ClimateSwingMode to_climate_swing_mode(MideaSwingMode mode); static MideaPreset to_midea_preset(ClimatePreset preset); - static MideaPreset to_midea_preset(const std::string &preset); + static MideaPreset to_midea_preset(const char *preset); static bool is_custom_midea_preset(MideaPreset preset); static ClimatePreset to_climate_preset(MideaPreset preset); - static const std::string &to_custom_climate_preset(MideaPreset preset); + static const char *to_custom_climate_preset(MideaPreset preset); static MideaFanMode to_midea_fan_mode(ClimateFanMode fan_mode); - static MideaFanMode to_midea_fan_mode(const std::string &fan_mode); + static MideaFanMode to_midea_fan_mode(const char *fan_mode); static bool is_custom_midea_fan_mode(MideaFanMode fan_mode); static ClimateFanMode to_climate_fan_mode(MideaFanMode fan_mode); - static const std::string &to_custom_climate_fan_mode(MideaFanMode fan_mode); + static const char *to_custom_climate_fan_mode(MideaFanMode fan_mode); static void to_climate_traits(ClimateTraits &traits, const dudanov::midea::ac::Capabilities &capabilities); }; diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index 0ad26ebd51..a6a8d52549 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -64,13 +64,13 @@ void AirConditioner::control(const ClimateCall &call) { ctrl.mode = Converters::to_midea_mode(call.get_mode().value()); if (call.get_preset().has_value()) { ctrl.preset = Converters::to_midea_preset(call.get_preset().value()); - } else if (call.get_custom_preset().has_value()) { - ctrl.preset = Converters::to_midea_preset(call.get_custom_preset().value()); + } else if (call.has_custom_preset()) { + ctrl.preset = Converters::to_midea_preset(call.get_custom_preset()); } if (call.get_fan_mode().has_value()) { ctrl.fanMode = Converters::to_midea_fan_mode(call.get_fan_mode().value()); - } else if (call.get_custom_fan_mode().has_value()) { - ctrl.fanMode = Converters::to_midea_fan_mode(call.get_custom_fan_mode().value()); + } else if (call.has_custom_fan_mode()) { + ctrl.fanMode = Converters::to_midea_fan_mode(call.get_custom_fan_mode()); } this->base_.control(ctrl); } @@ -84,8 +84,10 @@ ClimateTraits AirConditioner::traits() { traits.set_supported_modes(this->supported_modes_); traits.set_supported_swing_modes(this->supported_swing_modes_); traits.set_supported_presets(this->supported_presets_); - traits.set_supported_custom_presets(this->supported_custom_presets_); - traits.set_supported_custom_fan_modes(this->supported_custom_fan_modes_); + if (!this->supported_custom_presets_.empty()) + traits.set_supported_custom_presets(this->supported_custom_presets_); + if (!this->supported_custom_fan_modes_.empty()) + traits.set_supported_custom_fan_modes(this->supported_custom_fan_modes_); /* + MINIMAL SET OF CAPABILITIES */ traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_AUTO); traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_LOW); diff --git a/esphome/components/midea/air_conditioner.h b/esphome/components/midea/air_conditioner.h index 6c2401efe7..70833b8bcc 100644 --- a/esphome/components/midea/air_conditioner.h +++ b/esphome/components/midea/air_conditioner.h @@ -46,8 +46,8 @@ class AirConditioner : public ApplianceBase, void set_supported_modes(ClimateModeMask modes) { this->supported_modes_ = modes; } void set_supported_swing_modes(ClimateSwingModeMask modes) { this->supported_swing_modes_ = modes; } void set_supported_presets(ClimatePresetMask presets) { this->supported_presets_ = presets; } - void set_custom_presets(const std::vector &presets) { this->supported_custom_presets_ = presets; } - void set_custom_fan_modes(const std::vector &modes) { this->supported_custom_fan_modes_ = modes; } + void set_custom_presets(std::initializer_list presets) { this->supported_custom_presets_ = presets; } + void set_custom_fan_modes(std::initializer_list modes) { this->supported_custom_fan_modes_ = modes; } protected: void control(const ClimateCall &call) override; @@ -55,8 +55,8 @@ class AirConditioner : public ApplianceBase, ClimateModeMask supported_modes_{}; ClimateSwingModeMask supported_swing_modes_{}; ClimatePresetMask supported_presets_{}; - std::vector supported_custom_presets_{}; - std::vector supported_custom_fan_modes_{}; + std::vector supported_custom_presets_{}; + std::vector supported_custom_fan_modes_{}; Sensor *outdoor_sensor_{nullptr}; Sensor *humidity_sensor_{nullptr}; Sensor *power_sensor_{nullptr}; diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index 18efe3984e..d533ef93ec 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -54,7 +54,7 @@ void ThermostatClimate::setup() { if (this->default_preset_ != climate::ClimatePreset::CLIMATE_PRESET_NONE) { this->change_preset_(this->default_preset_); } else if (!this->default_custom_preset_.empty()) { - this->change_custom_preset_(this->default_custom_preset_); + this->change_custom_preset_(this->default_custom_preset_.c_str()); } } @@ -218,12 +218,13 @@ void ThermostatClimate::control(const climate::ClimateCall &call) { this->preset = call.get_preset().value(); } } - if (call.get_custom_preset().has_value()) { + if (call.has_custom_preset()) { // setup_complete_ blocks modifying/resetting the temps immediately after boot if (this->setup_complete_) { - this->change_custom_preset_(call.get_custom_preset().value()); + this->change_custom_preset_(call.get_custom_preset()); } else { - this->custom_preset = call.get_custom_preset().value(); + // Use the base class method which handles pointer lookup internally + this->set_custom_preset_(call.get_custom_preset()); } } @@ -321,9 +322,17 @@ climate::ClimateTraits ThermostatClimate::traits() { for (auto &it : this->preset_config_) { traits.add_supported_preset(it.first); } - for (auto &it : this->custom_preset_config_) { - traits.add_supported_custom_preset(it.first); + + // Extract custom preset names from the custom_preset_config_ map + if (!this->custom_preset_config_.empty()) { + std::vector custom_preset_names; + custom_preset_names.reserve(this->custom_preset_config_.size()); + for (const auto &it : this->custom_preset_config_) { + custom_preset_names.push_back(it.first.c_str()); + } + traits.set_supported_custom_presets(custom_preset_names); } + return traits; } @@ -1153,7 +1162,7 @@ void ThermostatClimate::change_preset_(climate::ClimatePreset preset) { this->preset.value() != preset) { // Fire any preset changed trigger if defined Trigger<> *trig = this->preset_change_trigger_; - this->preset = preset; + this->set_preset_(preset); if (trig != nullptr) { trig->trigger(); } @@ -1163,36 +1172,36 @@ void ThermostatClimate::change_preset_(climate::ClimatePreset preset) { } else { ESP_LOGI(TAG, "No changes required to apply preset %s", LOG_STR_ARG(climate::climate_preset_to_string(preset))); } - this->custom_preset.reset(); - this->preset = preset; } else { ESP_LOGW(TAG, "Preset %s not configured; ignoring", LOG_STR_ARG(climate::climate_preset_to_string(preset))); } } -void ThermostatClimate::change_custom_preset_(const std::string &custom_preset) { +void ThermostatClimate::change_custom_preset_(const char *custom_preset) { auto config = this->custom_preset_config_.find(custom_preset); if (config != this->custom_preset_config_.end()) { - ESP_LOGV(TAG, "Custom preset %s requested", custom_preset.c_str()); - if (this->change_preset_internal_(config->second) || (!this->custom_preset.has_value()) || - this->custom_preset.value() != custom_preset) { + ESP_LOGV(TAG, "Custom preset %s requested", custom_preset); + if (this->change_preset_internal_(config->second) || !this->has_custom_preset() || + strcmp(this->get_custom_preset(), custom_preset) != 0) { // Fire any preset changed trigger if defined Trigger<> *trig = this->preset_change_trigger_; - this->custom_preset = custom_preset; + // Use the base class method which handles pointer lookup and preset reset internally + this->set_custom_preset_(custom_preset); if (trig != nullptr) { trig->trigger(); } this->refresh(); - ESP_LOGI(TAG, "Custom preset %s applied", custom_preset.c_str()); + ESP_LOGI(TAG, "Custom preset %s applied", custom_preset); } else { - ESP_LOGI(TAG, "No changes required to apply custom preset %s", custom_preset.c_str()); + ESP_LOGI(TAG, "No changes required to apply custom preset %s", custom_preset); + // Note: set_custom_preset_() above handles preset.reset() and custom_preset_ assignment internally. + // The old code had these lines here unconditionally, which was a bug (double assignment, state modification + // even when no changes were needed). Now properly handled by the protected setter with mutual exclusion. } - this->preset.reset(); - this->custom_preset = custom_preset; } else { - ESP_LOGW(TAG, "Custom preset %s not configured; ignoring", custom_preset.c_str()); + ESP_LOGW(TAG, "Custom preset %s not configured; ignoring", custom_preset); } } diff --git a/esphome/components/thermostat/thermostat_climate.h b/esphome/components/thermostat/thermostat_climate.h index 42adab7751..c9795d9666 100644 --- a/esphome/components/thermostat/thermostat_climate.h +++ b/esphome/components/thermostat/thermostat_climate.h @@ -199,7 +199,7 @@ class ThermostatClimate : public climate::Climate, public Component { /// Change to a provided preset setting; will reset temperature, mode, fan, and swing modes accordingly void change_preset_(climate::ClimatePreset preset); /// Change to a provided custom preset setting; will reset temperature, mode, fan, and swing modes accordingly - void change_custom_preset_(const std::string &custom_preset); + void change_custom_preset_(const char *custom_preset); /// Applies the temperature, mode, fan, and swing modes of the provided config. /// This is agnostic of custom vs built in preset diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index fe0da14435..465356db80 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1315,7 +1315,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf for (climate::ClimatePreset m : traits.get_supported_presets()) opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m))); } - if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) { + if (!traits.get_supported_custom_presets().empty() && obj->has_custom_preset()) { JsonArray opt = root["custom_presets"].to(); for (auto const &custom_preset : traits.get_supported_custom_presets()) opt.add(custom_preset); @@ -1336,14 +1336,14 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) { root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value())); } - if (!traits.get_supported_custom_fan_modes().empty() && obj->custom_fan_mode.has_value()) { - root["custom_fan_mode"] = obj->custom_fan_mode.value().c_str(); + if (!traits.get_supported_custom_fan_modes().empty() && obj->has_custom_fan_mode()) { + root["custom_fan_mode"] = obj->get_custom_fan_mode(); } if (traits.get_supports_presets() && obj->preset.has_value()) { root["preset"] = PSTR_LOCAL(climate_preset_to_string(obj->preset.value())); } - if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) { - root["custom_preset"] = obj->custom_preset.value().c_str(); + if (!traits.get_supported_custom_presets().empty() && obj->has_custom_preset()) { + root["custom_preset"] = obj->get_custom_preset(); } if (traits.get_supports_swing_modes()) { root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode)); diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 394e92b9a7..3b756095a1 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -1610,8 +1610,9 @@ class RepeatedTypeInfo(TypeInfo): # Other types need the actual value # Special handling for const char* elements if self._use_pointer and "const char" in self._container_no_template: + field_id_size = self.calculate_field_id_size() o += f" for (const char *it : {container_ref}) {{\n" - o += " size.add_length_force(1, strlen(it));\n" + o += f" size.add_length_force({field_id_size}, strlen(it));\n" else: auto_ref = "" if self._ti_is_bool else "&" o += f" for (const auto {auto_ref}it : {container_ref}) {{\n" diff --git a/tests/integration/fixtures/climate_custom_fan_modes_and_presets.yaml b/tests/integration/fixtures/climate_custom_fan_modes_and_presets.yaml new file mode 100644 index 0000000000..bf4ef9eafd --- /dev/null +++ b/tests/integration/fixtures/climate_custom_fan_modes_and_presets.yaml @@ -0,0 +1,40 @@ +esphome: + name: climate-custom-modes-test +host: +api: +logger: + +sensor: + - platform: template + id: thermostat_sensor + lambda: "return 22.0;" + +climate: + - platform: thermostat + id: test_thermostat + name: Test Thermostat Custom Modes + sensor: thermostat_sensor + preset: + - name: Away + default_target_temperature_low: 16°C + default_target_temperature_high: 20°C + - name: Eco Plus + default_target_temperature_low: 18°C + default_target_temperature_high: 22°C + - name: Super Saver + default_target_temperature_low: 20°C + default_target_temperature_high: 24°C + - name: Vacation Mode + default_target_temperature_low: 15°C + default_target_temperature_high: 18°C + idle_action: + - logger.log: idle_action + cool_action: + - logger.log: cool_action + heat_action: + - logger.log: heat_action + min_cooling_off_time: 10s + min_cooling_run_time: 10s + min_heating_off_time: 10s + min_heating_run_time: 10s + min_idle_time: 10s diff --git a/tests/integration/test_climate_custom_modes.py b/tests/integration/test_climate_custom_modes.py new file mode 100644 index 0000000000..ce34959d88 --- /dev/null +++ b/tests/integration/test_climate_custom_modes.py @@ -0,0 +1,42 @@ +"""Integration test for climate custom presets.""" + +from __future__ import annotations + +from aioesphomeapi import ClimateInfo, ClimatePreset +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_climate_custom_fan_modes_and_presets( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that custom presets are properly exposed via API.""" + async with run_compiled(yaml_config), api_client_connected() as client: + # Get entities and services + entities, services = await client.list_entities_services() + climate_infos = [e for e in entities if isinstance(e, ClimateInfo)] + assert len(climate_infos) == 1, "Expected exactly 1 climate entity" + + test_climate = climate_infos[0] + + # Verify enum presets are exposed (from preset: config map) + assert ClimatePreset.AWAY in test_climate.supported_presets, ( + "Expected AWAY in enum presets" + ) + + # Verify custom string presets are exposed (non-standard preset names from preset map) + custom_presets = test_climate.supported_custom_presets + assert len(custom_presets) == 3, ( + f"Expected 3 custom presets, got {len(custom_presets)}: {custom_presets}" + ) + assert "Eco Plus" in custom_presets, "Expected 'Eco Plus' in custom presets" + assert "Super Saver" in custom_presets, ( + "Expected 'Super Saver' in custom presets" + ) + assert "Vacation Mode" in custom_presets, ( + "Expected 'Vacation Mode' in custom presets" + ) From 0e792d07912346c85dc09e41d6081b8861c2f8c2 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sun, 2 Nov 2025 21:20:08 -0800 Subject: [PATCH 358/526] [nrf52,debug] fix status of nRESET pin, add extra registry from UICR (#11667) Co-authored-by: J. Nick Koston --- esphome/components/debug/debug_zephyr.cpp | 33 ++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/esphome/components/debug/debug_zephyr.cpp b/esphome/components/debug/debug_zephyr.cpp index 231b39a711..62fa391e5f 100644 --- a/esphome/components/debug/debug_zephyr.cpp +++ b/esphome/components/debug/debug_zephyr.cpp @@ -8,8 +8,7 @@ #define BOOTLOADER_VERSION_REGISTER NRF_TIMER2->CC[0] -namespace esphome { -namespace debug { +namespace esphome::debug { static const char *const TAG = "debug"; constexpr std::uintptr_t MBR_PARAM_PAGE_ADDR = 0xFFC; @@ -281,14 +280,18 @@ void DebugComponent::get_device_info_(std::string &device_info) { NRF_FICR->INFO.VARIANT & 0xFF, package(NRF_FICR->INFO.PACKAGE)); ESP_LOGD(TAG, "RAM: %ukB, Flash: %ukB, production test: %sdone", NRF_FICR->INFO.RAM, NRF_FICR->INFO.FLASH, (NRF_FICR->PRODTEST[0] == 0xBB42319F ? "" : "not ")); + bool n_reset_enabled = NRF_UICR->PSELRESET[0] == NRF_UICR->PSELRESET[1] && + (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) == UICR_PSELRESET_CONNECT_Connected + << UICR_PSELRESET_CONNECT_Pos; ESP_LOGD( TAG, "GPIO as NFC pins: %s, GPIO as nRESET pin: %s", YESNO((NRF_UICR->NFCPINS & UICR_NFCPINS_PROTECT_Msk) == (UICR_NFCPINS_PROTECT_NFC << UICR_NFCPINS_PROTECT_Pos)), - YESNO(((NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) != - (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos)) || - ((NRF_UICR->PSELRESET[1] & UICR_PSELRESET_CONNECT_Msk) != - (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos)))); - + YESNO(n_reset_enabled)); + if (n_reset_enabled) { + uint8_t port = (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_PORT_Msk) >> UICR_PSELRESET_PORT_Pos; + uint8_t pin = (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_PIN_Msk) >> UICR_PSELRESET_PIN_Pos; + ESP_LOGD(TAG, "nRESET port P%u.%02u", port, pin); + } #ifdef USE_BOOTLOADER_MCUBOOT ESP_LOGD(TAG, "bootloader: mcuboot"); #else @@ -322,10 +325,22 @@ void DebugComponent::get_device_info_(std::string &device_info) { #endif } #endif + auto uicr = [](volatile uint32_t *data, uint8_t size) { + std::string res; + char buf[sizeof(uint32_t) * 2 + 1]; + for (size_t i = 0; i < size; i++) { + if (i > 0) { + res += ' '; + } + res += format_hex_pretty(data[i], '\0', false); + } + return res; + }; + ESP_LOGD(TAG, "NRFFW %s", uicr(NRF_UICR->NRFFW, 13).c_str()); + ESP_LOGD(TAG, "NRFHW %s", uicr(NRF_UICR->NRFHW, 12).c_str()); } void DebugComponent::update_platform_() {} -} // namespace debug -} // namespace esphome +} // namespace esphome::debug #endif From 7e1cea8e6941f0e5d7df17a23c1213a352331b14 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Mon, 3 Nov 2025 22:05:33 +0800 Subject: [PATCH 359/526] [template] alarm_control_panel more ESP_LOGCONFIG reductions (#11691) --- .../template_alarm_control_panel.cpp | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp index f7e9872ce1..af662a05a0 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp @@ -42,23 +42,21 @@ static const LogString *sensor_type_to_string(AlarmSensorType type) { #endif void TemplateAlarmControlPanel::dump_config() { - ESP_LOGCONFIG(TAG, "TemplateAlarmControlPanel:"); ESP_LOGCONFIG(TAG, + "TemplateAlarmControlPanel:\n" " Current State: %s\n" - " Number of Codes: %u", - LOG_STR_ARG(alarm_control_panel_state_to_string(this->current_state_)), this->codes_.size()); - if (!this->codes_.empty()) - ESP_LOGCONFIG(TAG, " Requires Code To Arm: %s", YESNO(this->requires_code_to_arm_)); - ESP_LOGCONFIG(TAG, " Arming Away Time: %" PRIu32 "s", (this->arming_away_time_ / 1000)); - if (this->arming_home_time_ != 0) - ESP_LOGCONFIG(TAG, " Arming Home Time: %" PRIu32 "s", (this->arming_home_time_ / 1000)); - if (this->arming_night_time_ != 0) - ESP_LOGCONFIG(TAG, " Arming Night Time: %" PRIu32 "s", (this->arming_night_time_ / 1000)); - ESP_LOGCONFIG(TAG, + " Number of Codes: %u\n" + " Requires Code To Arm: %s\n" + " Arming Away Time: %" PRIu32 "s\n" + " Arming Home Time: %" PRIu32 "s\n" + " Arming Night Time: %" PRIu32 "s\n" " Pending Time: %" PRIu32 "s\n" " Trigger Time: %" PRIu32 "s\n" " Supported Features: %" PRIu32, - (this->pending_time_ / 1000), (this->trigger_time_ / 1000), this->get_supported_features()); + LOG_STR_ARG(alarm_control_panel_state_to_string(this->current_state_)), this->codes_.size(), + YESNO(!this->codes_.empty() && this->requires_code_to_arm_), (this->arming_away_time_ / 1000), + (this->arming_home_time_ / 1000), (this->arming_night_time_ / 1000), (this->pending_time_ / 1000), + (this->trigger_time_ / 1000), this->get_supported_features()); #ifdef USE_BINARY_SENSOR for (auto const &[sensor, info] : this->sensor_map_) { ESP_LOGCONFIG(TAG, From 1ec1692c774173dd73d91bbe6ededc402207274a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Nov 2025 08:23:04 -0600 Subject: [PATCH 360/526] [mqtt] Fix climate custom fan mode and preset compilation errors (#11692) --- esphome/components/mqtt/mqtt_climate.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index a6f4e0a201..aee2b38942 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -357,8 +357,8 @@ bool MQTTClimateComponent::publish_state_() { payload = "unknown"; } } - if (this->device_->custom_preset.has_value()) - payload = this->device_->custom_preset.value(); + if (this->device_->has_custom_preset()) + payload = this->device_->get_custom_preset(); if (!this->publish(this->get_preset_state_topic(), payload)) success = false; } @@ -429,8 +429,8 @@ bool MQTTClimateComponent::publish_state_() { payload = "unknown"; } } - if (this->device_->custom_fan_mode.has_value()) - payload = this->device_->custom_fan_mode.value(); + if (this->device_->has_custom_fan_mode()) + payload = this->device_->get_custom_fan_mode(); if (!this->publish(this->get_fan_mode_state_topic(), payload)) success = false; } From f05f45af74f2fc02e30445ca67f449c672fee6b4 Mon Sep 17 00:00:00 2001 From: Nathan Bernard <3122054+pixelatedmirror@users.noreply.github.com> Date: Mon, 3 Nov 2025 07:17:28 -0800 Subject: [PATCH 361/526] Add support for Mopeka standard check alternate ID (#10907) Co-authored-by: J. Nick Koston --- esphome/components/mopeka_std_check/mopeka_std_check.cpp | 2 +- esphome/components/mopeka_std_check/mopeka_std_check.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/mopeka_std_check/mopeka_std_check.cpp b/esphome/components/mopeka_std_check/mopeka_std_check.cpp index 6685a23c41..0d8340f95f 100644 --- a/esphome/components/mopeka_std_check/mopeka_std_check.cpp +++ b/esphome/components/mopeka_std_check/mopeka_std_check.cpp @@ -72,7 +72,7 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) const u_int8_t hardware_id = mopeka_data->data_1 & 0xCF; if (static_cast(hardware_id) != STANDARD && static_cast(hardware_id) != XL && - static_cast(hardware_id) != ETRAILER) { + static_cast(hardware_id) != ETRAILER && static_cast(hardware_id) != STANDARD_ALT) { ESP_LOGE(TAG, "[%s] Unsupported Sensor Type (0x%X)", device.address_str().c_str(), hardware_id); return false; } diff --git a/esphome/components/mopeka_std_check/mopeka_std_check.h b/esphome/components/mopeka_std_check/mopeka_std_check.h index b92445df34..897b5414ed 100644 --- a/esphome/components/mopeka_std_check/mopeka_std_check.h +++ b/esphome/components/mopeka_std_check/mopeka_std_check.h @@ -15,6 +15,7 @@ namespace mopeka_std_check { enum SensorType { STANDARD = 0x02, XL = 0x03, + STANDARD_ALT = 0x44, ETRAILER = 0x46, }; From cb039b42aa235042fb9c51373eadcf2d98af2108 Mon Sep 17 00:00:00 2001 From: Paul Strawder Date: Mon, 3 Nov 2025 17:34:53 +0100 Subject: [PATCH 362/526] [esp32] Make the loop task's stack size configurable (#10564) Co-authored-by: Paul Strawder Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/esp32/__init__.py | 12 ++++++++++++ esphome/components/esp32/core.cpp | 5 +++-- esphome/core/defines.h | 1 + .../components/esp32/test-stack_size.esp32-idf.yaml | 6 ++++++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/components/esp32/test-stack_size.esp32-idf.yaml diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index ddb8dbb1f0..6981662d77 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -558,6 +558,7 @@ CONF_DISABLE_LIBC_LOCKS_IN_IRAM = "disable_libc_locks_in_iram" CONF_DISABLE_VFS_SUPPORT_TERMIOS = "disable_vfs_support_termios" CONF_DISABLE_VFS_SUPPORT_SELECT = "disable_vfs_support_select" CONF_DISABLE_VFS_SUPPORT_DIR = "disable_vfs_support_dir" +CONF_LOOP_TASK_STACK_SIZE = "loop_task_stack_size" # VFS requirement tracking # Components that need VFS features can call require_vfs_select() or require_vfs_dir() @@ -654,6 +655,9 @@ FRAMEWORK_SCHEMA = cv.All( ): cv.boolean, cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean, cv.Optional(CONF_EXECUTE_FROM_PSRAM): cv.boolean, + cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range( + min=8192, max=32768 + ), } ), cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list( @@ -926,6 +930,10 @@ async def to_code(config): f"VERSION_CODE({framework_ver.major}, {framework_ver.minor}, {framework_ver.patch})" ), ) + add_idf_sdkconfig_option( + "CONFIG_ARDUINO_LOOP_STACK_SIZE", + conf[CONF_ADVANCED][CONF_LOOP_TASK_STACK_SIZE], + ) add_idf_sdkconfig_option("CONFIG_AUTOSTART_ARDUINO", True) add_idf_sdkconfig_option("CONFIG_MBEDTLS_PSK_MODES", True) add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True) @@ -1071,6 +1079,10 @@ async def to_code(config): ) add_idf_sdkconfig_option("CONFIG_IDF_EXPERIMENTAL_FEATURES", True) + cg.add_define( + "ESPHOME_LOOP_TASK_STACK_SIZE", advanced.get(CONF_LOOP_TASK_STACK_SIZE) + ) + cg.add_define( "USE_ESP_IDF_VERSION_CODE", cg.RawExpression( diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp index 1c8f29fa95..6215ff862f 100644 --- a/esphome/components/esp32/core.cpp +++ b/esphome/components/esp32/core.cpp @@ -1,5 +1,6 @@ #ifdef USE_ESP32 +#include "esphome/core/defines.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "preferences.h" @@ -97,9 +98,9 @@ void loop_task(void *pv_params) { extern "C" void app_main() { esp32::setup_preferences(); #if CONFIG_FREERTOS_UNICORE - xTaskCreate(loop_task, "loopTask", 8192, nullptr, 1, &loop_task_handle); + xTaskCreate(loop_task, "loopTask", ESPHOME_LOOP_TASK_STACK_SIZE, nullptr, 1, &loop_task_handle); #else - xTaskCreatePinnedToCore(loop_task, "loopTask", 8192, nullptr, 1, &loop_task_handle, 1); + xTaskCreatePinnedToCore(loop_task, "loopTask", ESPHOME_LOOP_TASK_STACK_SIZE, nullptr, 1, &loop_task_handle, 1); #endif } #endif // USE_ESP_IDF diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 868df6e254..dc37dcbc0e 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -155,6 +155,7 @@ // IDF-specific feature flags #ifdef USE_ESP_IDF #define USE_MQTT_IDF_ENQUEUE +#define ESPHOME_LOOP_TASK_STACK_SIZE 8192 #endif // ESP32-specific feature flags diff --git a/tests/components/esp32/test-stack_size.esp32-idf.yaml b/tests/components/esp32/test-stack_size.esp32-idf.yaml new file mode 100644 index 0000000000..4953588035 --- /dev/null +++ b/tests/components/esp32/test-stack_size.esp32-idf.yaml @@ -0,0 +1,6 @@ +esp32: + board: esp32dev + framework: + type: esp-idf + advanced: + loop_task_stack_size: 16384 From 06d0787ee06e0780606c6e7010d599f08adaaef2 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 3 Nov 2025 08:42:49 -0800 Subject: [PATCH 363/526] [nrf52, i2c] i2c support for nrf52 (#8150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Co-authored-by: Ludovic BOUÉ Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- esphome/components/i2c/__init__.py | 70 ++++++++- esphome/components/i2c/i2c.cpp | 3 + esphome/components/i2c/i2c_bus_zephyr.cpp | 133 ++++++++++++++++++ esphome/components/i2c/i2c_bus_zephyr.h | 38 +++++ esphome/components/zephyr/__init__.py | 3 +- .../ads1115/test.nrf52-adafruit.yaml | 4 + .../components/ads1115/test.nrf52-mcumgr.yaml | 4 + .../components/aht10/test.nrf52-adafruit.yaml | 4 + tests/components/i2c/test.nrf52-adafruit.yaml | 4 + tests/components/i2c/test.nrf52-mcumgr.yaml | 4 + tests/components/i2c/test.nrf52-xiao-ble.yaml | 4 + .../common/i2c/nrf52.yaml | 11 ++ 12 files changed, 276 insertions(+), 6 deletions(-) create mode 100644 esphome/components/i2c/i2c_bus_zephyr.cpp create mode 100644 esphome/components/i2c/i2c_bus_zephyr.h create mode 100644 tests/components/ads1115/test.nrf52-adafruit.yaml create mode 100644 tests/components/ads1115/test.nrf52-mcumgr.yaml create mode 100644 tests/components/aht10/test.nrf52-adafruit.yaml create mode 100644 tests/components/i2c/test.nrf52-adafruit.yaml create mode 100644 tests/components/i2c/test.nrf52-mcumgr.yaml create mode 100644 tests/components/i2c/test.nrf52-xiao-ble.yaml create mode 100644 tests/test_build_components/common/i2c/nrf52.yaml diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index 3cfec1e94d..6308923759 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -2,11 +2,18 @@ import logging from esphome import pins import esphome.codegen as cg +from esphome.components.zephyr import ( + zephyr_add_overlay, + zephyr_add_prj_conf, + zephyr_data, +) +from esphome.components.zephyr.const import KEY_BOARD from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( CONF_ADDRESS, CONF_FREQUENCY, + CONF_I2C, CONF_I2C_ID, CONF_ID, CONF_SCAN, @@ -15,10 +22,12 @@ from esphome.const import ( CONF_TIMEOUT, PLATFORM_ESP32, PLATFORM_ESP8266, + PLATFORM_NRF52, PLATFORM_RP2040, PlatformFramework, ) from esphome.core import CORE, CoroPriority, coroutine_with_priority +from esphome.cpp_generator import MockObj import esphome.final_validate as fv LOGGER = logging.getLogger(__name__) @@ -28,6 +37,7 @@ I2CBus = i2c_ns.class_("I2CBus") InternalI2CBus = i2c_ns.class_("InternalI2CBus", I2CBus) ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", InternalI2CBus, cg.Component) IDFI2CBus = i2c_ns.class_("IDFI2CBus", InternalI2CBus, cg.Component) +ZephyrI2CBus = i2c_ns.class_("ZephyrI2CBus", I2CBus, cg.Component) I2CDevice = i2c_ns.class_("I2CDevice") @@ -41,6 +51,8 @@ def _bus_declare_type(value): return cv.declare_id(ArduinoI2CBus)(value) if CORE.using_esp_idf: return cv.declare_id(IDFI2CBus)(value) + if CORE.using_zephyr: + return cv.declare_id(ZephyrI2CBus)(value) raise NotImplementedError @@ -62,23 +74,70 @@ CONFIG_SCHEMA = cv.All( cv.SplitDefault(CONF_SCL_PULLUP_ENABLED, esp32_idf=True): cv.All( cv.only_with_esp_idf, cv.boolean ), - cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All( - cv.frequency, cv.Range(min=0, min_included=False) + cv.SplitDefault( + CONF_FREQUENCY, + esp32="50kHz", + esp8266="50kHz", + rp2040="50kHz", + nrf52="100kHz", + ): cv.All( + cv.frequency, + cv.Range(min=0, min_included=False), + ), + cv.Optional(CONF_TIMEOUT): cv.All( + cv.only_with_framework(["arduino", "esp-idf"]), + cv.positive_time_period, ), - cv.Optional(CONF_TIMEOUT): cv.positive_time_period, cv.Optional(CONF_SCAN, default=True): cv.boolean, } ).extend(cv.COMPONENT_SCHEMA), - cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]), + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040, PLATFORM_NRF52]), validate_config, ) +def _final_validate(config): + full_config = fv.full_config.get()[CONF_I2C] + if CORE.using_zephyr and len(full_config) > 1: + raise cv.Invalid("Second i2c is not implemented on Zephyr yet") + + +FINAL_VALIDATE_SCHEMA = _final_validate + + @coroutine_with_priority(CoroPriority.BUS) async def to_code(config): cg.add_global(i2c_ns.using) cg.add_define("USE_I2C") - var = cg.new_Pvariable(config[CONF_ID]) + if CORE.using_zephyr: + zephyr_add_prj_conf("I2C", True) + i2c = "i2c0" + if zephyr_data()[KEY_BOARD] in ["xiao_ble"]: + i2c = "i2c1" + zephyr_add_overlay( + f""" + &pinctrl {{ + {i2c}_default: {i2c}_default {{ + group1 {{ + psels = , + ; + }}; + }}; + {i2c}_sleep: {i2c}_sleep {{ + group1 {{ + psels = , + ; + low-power-enable; + }}; + }}; + }}; + """ + ) + var = cg.new_Pvariable( + config[CONF_ID], MockObj(f"DEVICE_DT_GET(DT_NODELABEL({i2c}))") + ) + else: + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) cg.add(var.set_sda_pin(config[CONF_SDA])) @@ -197,5 +256,6 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform( PlatformFramework.LN882X_ARDUINO, }, "i2c_bus_esp_idf.cpp": {PlatformFramework.ESP32_IDF}, + "i2c_bus_zephyr.cpp": {PlatformFramework.NRF52_ZEPHYR}, } ) diff --git a/esphome/components/i2c/i2c.cpp b/esphome/components/i2c/i2c.cpp index 31c21f398c..f8c7a1b40b 100644 --- a/esphome/components/i2c/i2c.cpp +++ b/esphome/components/i2c/i2c.cpp @@ -1,6 +1,7 @@ #include "i2c.h" #include "esphome/core/defines.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" #include @@ -23,6 +24,8 @@ void I2CBus::i2c_scan_() { } else if (err == ERROR_UNKNOWN) { scan_results_.emplace_back(address, false); } + // it takes 16sec to scan on nrf52. It prevents board reset. + arch_feed_wdt(); } #if defined(USE_ESP32) && defined(USE_LOGGER) esp_log_level_set("*", previous); diff --git a/esphome/components/i2c/i2c_bus_zephyr.cpp b/esphome/components/i2c/i2c_bus_zephyr.cpp new file mode 100644 index 0000000000..658dcee35c --- /dev/null +++ b/esphome/components/i2c/i2c_bus_zephyr.cpp @@ -0,0 +1,133 @@ +#ifdef USE_ZEPHYR + +#include "i2c_bus_zephyr.h" +#include +#include "esphome/core/log.h" + +namespace esphome::i2c { + +static const char *const TAG = "i2c.zephyr"; + +void ZephyrI2CBus::setup() { + if (!device_is_ready(this->i2c_dev_)) { + ESP_LOGE(TAG, "I2C dev is not ready."); + mark_failed(); + return; + } + + int ret = i2c_configure(this->i2c_dev_, this->dev_config_); + if (ret < 0) { + ESP_LOGE(TAG, "I2C: Failed to configure device"); + } + + this->recovery_result_ = i2c_recover_bus(this->i2c_dev_); + if (this->recovery_result_ != 0) { + ESP_LOGE(TAG, "I2C recover bus failed, err %d", this->recovery_result_); + } + if (this->scan_) { + ESP_LOGV(TAG, "Scanning I2C bus for active devices..."); + this->i2c_scan_(); + } +} + +void ZephyrI2CBus::dump_config() { + auto get_speed = [](uint32_t dev_config) { + switch (I2C_SPEED_GET(dev_config)) { + case I2C_SPEED_STANDARD: + return "100 kHz"; + case I2C_SPEED_FAST: + return "400 kHz"; + case I2C_SPEED_FAST_PLUS: + return "1 MHz"; + case I2C_SPEED_HIGH: + return "3.4 MHz"; + case I2C_SPEED_ULTRA: + return "5 MHz"; + } + return "unknown"; + }; + ESP_LOGCONFIG(TAG, + "I2C Bus:\n" + " SDA Pin: GPIO%u\n" + " SCL Pin: GPIO%u\n" + " Frequency: %s\n" + " Name: %s", + this->sda_pin_, this->scl_pin_, get_speed(this->dev_config_), this->i2c_dev_->name); + + if (this->recovery_result_ != 0) { + ESP_LOGCONFIG(TAG, " Recovery: failed, err %d", this->recovery_result_); + } else { + ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered"); + } + if (this->scan_) { + ESP_LOGI(TAG, "Results from I2C bus scan:"); + if (scan_results_.empty()) { + ESP_LOGI(TAG, "Found no I2C devices!"); + } else { + for (const auto &s : scan_results_) { + if (s.second) { + ESP_LOGI(TAG, "Found I2C device at address 0x%02X", s.first); + } else { + ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first); + } + } + } + } +} + +ErrorCode ZephyrI2CBus::write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, + uint8_t *read_buffer, size_t read_count) { + if (!device_is_ready(this->i2c_dev_)) { + return ERROR_NOT_INITIALIZED; + } + + i2c_msg msgs[2]{}; + size_t cnt = 0; + uint8_t dst = 0x00; // dummy data to not use random value + + if (read_count == 0 && write_count == 0) { + msgs[cnt].buf = &dst; + msgs[cnt].len = 0U; + msgs[cnt++].flags = I2C_MSG_WRITE; + } else { + if (write_count) { + // the same struct is used for read/write — const cast is fine; data isn't modified + msgs[cnt].buf = const_cast(write_buffer); + msgs[cnt].len = write_count; + msgs[cnt++].flags = I2C_MSG_WRITE; + } + if (read_count) { + msgs[cnt].buf = const_cast(read_buffer); + msgs[cnt].len = read_count; + msgs[cnt++].flags = I2C_MSG_READ | I2C_MSG_RESTART; + } + } + + msgs[cnt - 1].flags |= I2C_MSG_STOP; + + auto err = i2c_transfer(this->i2c_dev_, msgs, cnt, address); + + if (err == -EIO) { + return ERROR_NOT_ACKNOWLEDGED; + } + + if (err != 0) { + ESP_LOGE(TAG, "i2c transfer error %d", err); + return ERROR_UNKNOWN; + } + + return ERROR_OK; +} + +void ZephyrI2CBus::set_frequency(uint32_t frequency) { + this->dev_config_ &= ~I2C_SPEED_MASK; + if (frequency >= 400000) { + this->dev_config_ |= I2C_SPEED_SET(I2C_SPEED_FAST); + } else { + this->dev_config_ |= I2C_SPEED_SET(I2C_SPEED_STANDARD); + } +} + +} // namespace esphome::i2c + +#endif diff --git a/esphome/components/i2c/i2c_bus_zephyr.h b/esphome/components/i2c/i2c_bus_zephyr.h new file mode 100644 index 0000000000..49cac5b992 --- /dev/null +++ b/esphome/components/i2c/i2c_bus_zephyr.h @@ -0,0 +1,38 @@ +#pragma once + +#ifdef USE_ZEPHYR + +#include "i2c_bus.h" +#include "esphome/core/component.h" + +struct device; + +namespace esphome::i2c { + +class ZephyrI2CBus : public InternalI2CBus, public Component { + public: + explicit ZephyrI2CBus(const device *i2c_dev) : i2c_dev_(i2c_dev) {} + void setup() override; + void dump_config() override; + ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, + size_t read_count) override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void set_scan(bool scan) { scan_ = scan; } + void set_sda_pin(uint8_t sda_pin) { this->sda_pin_ = sda_pin; } + void set_scl_pin(uint8_t scl_pin) { this->scl_pin_ = scl_pin; } + void set_frequency(uint32_t frequency); + + int get_port() const override { return 0; } + + protected: + const device *i2c_dev_; + int recovery_result_ = 0; + uint8_t sda_pin_{}; + uint8_t scl_pin_{}; + uint32_t dev_config_{}; +}; + +} // namespace esphome::i2c + +#endif diff --git a/esphome/components/zephyr/__init__.py b/esphome/components/zephyr/__init__.py index a2fb12a5e2..0381fbcba9 100644 --- a/esphome/components/zephyr/__init__.py +++ b/esphome/components/zephyr/__init__.py @@ -1,4 +1,5 @@ from pathlib import Path +import textwrap from typing import TypedDict import esphome.codegen as cg @@ -90,7 +91,7 @@ def zephyr_add_prj_conf( def zephyr_add_overlay(content): - zephyr_data()[KEY_OVERLAY] += content + zephyr_data()[KEY_OVERLAY] += textwrap.dedent(content) def add_extra_build_file(filename: str, path: Path) -> bool: diff --git a/tests/components/ads1115/test.nrf52-adafruit.yaml b/tests/components/ads1115/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..2a0de6241c --- /dev/null +++ b/tests/components/ads1115/test.nrf52-adafruit.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml diff --git a/tests/components/ads1115/test.nrf52-mcumgr.yaml b/tests/components/ads1115/test.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..2a0de6241c --- /dev/null +++ b/tests/components/ads1115/test.nrf52-mcumgr.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml diff --git a/tests/components/aht10/test.nrf52-adafruit.yaml b/tests/components/aht10/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..2a0de6241c --- /dev/null +++ b/tests/components/aht10/test.nrf52-adafruit.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml diff --git a/tests/components/i2c/test.nrf52-adafruit.yaml b/tests/components/i2c/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..2a0de6241c --- /dev/null +++ b/tests/components/i2c/test.nrf52-adafruit.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml diff --git a/tests/components/i2c/test.nrf52-mcumgr.yaml b/tests/components/i2c/test.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..2a0de6241c --- /dev/null +++ b/tests/components/i2c/test.nrf52-mcumgr.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml diff --git a/tests/components/i2c/test.nrf52-xiao-ble.yaml b/tests/components/i2c/test.nrf52-xiao-ble.yaml new file mode 100644 index 0000000000..2a0de6241c --- /dev/null +++ b/tests/components/i2c/test.nrf52-xiao-ble.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml diff --git a/tests/test_build_components/common/i2c/nrf52.yaml b/tests/test_build_components/common/i2c/nrf52.yaml new file mode 100644 index 0000000000..b86cdf0d69 --- /dev/null +++ b/tests/test_build_components/common/i2c/nrf52.yaml @@ -0,0 +1,11 @@ +# Common I2C configuration for NRF52 tests + +substitutions: + scl_pin: P0.04 + sda_pin: P0.05 + +i2c: + - id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + scan: true From 3f12630a6b0227ee6787f92709205692313a629e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Nov 2025 13:13:37 -0600 Subject: [PATCH 364/526] [core][esp32_ble][socket] Add wake_loop_threadsafe() helper for background thread wakeups (#11681) --- esphome/components/esp32_ble/__init__.py | 10 +- esphome/components/esp32_ble/ble.cpp | 112 +----------------- esphome/components/esp32_ble/ble.h | 40 +------ esphome/components/socket/__init__.py | 27 +++++ esphome/core/application.cpp | 79 ++++++++++++ esphome/core/application.h | 42 +++++++ esphome/core/defines.h | 1 + tests/components/socket/conftest.py | 12 ++ .../socket/test_wake_loop_threadsafe.py | 42 +++++++ 9 files changed, 217 insertions(+), 148 deletions(-) create mode 100644 tests/components/socket/conftest.py create mode 100644 tests/components/socket/test_wake_loop_threadsafe.py diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 1ae8df6f5e..ced7e3fec9 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -22,6 +22,7 @@ from esphome.core import CORE, CoroPriority, TimePeriod, coroutine_with_priority import esphome.final_validate as fv DEPENDENCIES = ["esp32"] +AUTO_LOAD = ["socket"] CODEOWNERS = ["@jesserockz", "@Rapsssito", "@bdraco"] DOMAIN = "esp32_ble" @@ -482,13 +483,10 @@ async def to_code(config): cg.add(var.set_name(name)) await cg.register_component(var, config) - # BLE uses 1 UDP socket for event notification to wake up main loop from select() + # BLE uses the socket wake_loop_threadsafe() mechanism to wake the main loop from BLE tasks # This enables low-latency (~12μs) BLE event processing instead of waiting for - # select() timeout (0-16ms). The socket is created in ble_setup_() and used to - # wake lwip_select() when BLE events arrive from the BLE thread. - # Note: Called during config generation, socket is created at runtime. In practice, - # always used since esp32_ble only runs on ESP32 which always has USE_SOCKET_SELECT_SUPPORT. - socket.consume_sockets(1, "esp32_ble")(config) + # select() timeout (0-16ms). The wake socket is shared across all components. + socket.require_wake_loop_threadsafe() # Define max connections for use in C++ code (e.g., ble_server.h) max_connections = config.get(CONF_MAX_CONNECTIONS, DEFAULT_MAX_CONNECTIONS) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index d6f7e1ce43..fc26a7fc21 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -297,21 +297,10 @@ bool ESP32BLE::ble_setup_() { // BLE takes some time to be fully set up, 200ms should be more than enough delay(200); // NOLINT - // Set up notification socket to wake main loop for BLE events - // This enables low-latency (~12μs) event processing instead of waiting for select() timeout -#ifdef USE_SOCKET_SELECT_SUPPORT - this->setup_event_notification_(); -#endif - return true; } bool ESP32BLE::ble_dismantle_() { - // Clean up notification socket first before dismantling BLE stack -#ifdef USE_SOCKET_SELECT_SUPPORT - this->cleanup_event_notification_(); -#endif - esp_err_t err = esp_bluedroid_disable(); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_bluedroid_disable failed: %d", err); @@ -409,12 +398,6 @@ void ESP32BLE::loop() { break; } -#ifdef USE_SOCKET_SELECT_SUPPORT - // Drain any notification socket events first - // This clears the socket so it doesn't stay "ready" in subsequent select() calls - this->drain_event_notifications_(); -#endif - BLEEvent *ble_event = this->ble_events_.pop(); while (ble_event != nullptr) { switch (ble_event->type_) { @@ -589,8 +572,8 @@ void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa GAP_SECURITY_EVENTS: enqueue_ble_event(event, param); // Wake up main loop to process security event immediately -#ifdef USE_SOCKET_SELECT_SUPPORT - global_ble->notify_main_loop_(); +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + App.wake_loop_threadsafe(); #endif return; @@ -612,8 +595,8 @@ void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gat esp_ble_gatts_cb_param_t *param) { enqueue_ble_event(event, gatts_if, param); // Wake up main loop to process GATT event immediately -#ifdef USE_SOCKET_SELECT_SUPPORT - global_ble->notify_main_loop_(); +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + App.wake_loop_threadsafe(); #endif } #endif @@ -623,8 +606,8 @@ void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat esp_ble_gattc_cb_param_t *param) { enqueue_ble_event(event, gattc_if, param); // Wake up main loop to process GATT event immediately -#ifdef USE_SOCKET_SELECT_SUPPORT - global_ble->notify_main_loop_(); +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + App.wake_loop_threadsafe(); #endif } #endif @@ -665,89 +648,6 @@ void ESP32BLE::dump_config() { } } -#ifdef USE_SOCKET_SELECT_SUPPORT -void ESP32BLE::setup_event_notification_() { - // Create UDP socket for event notifications - this->notify_fd_ = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (this->notify_fd_ < 0) { - ESP_LOGW(TAG, "Event socket create failed: %d", errno); - return; - } - - // Bind to loopback with auto-assigned port - struct sockaddr_in addr = {}; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = lwip_htonl(INADDR_LOOPBACK); - addr.sin_port = 0; // Auto-assign port - - if (lwip_bind(this->notify_fd_, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - ESP_LOGW(TAG, "Event socket bind failed: %d", errno); - lwip_close(this->notify_fd_); - this->notify_fd_ = -1; - return; - } - - // Get the assigned address and connect to it - // Connecting a UDP socket allows using send() instead of sendto() for better performance - struct sockaddr_in notify_addr; - socklen_t len = sizeof(notify_addr); - if (lwip_getsockname(this->notify_fd_, (struct sockaddr *) ¬ify_addr, &len) < 0) { - ESP_LOGW(TAG, "Event socket address failed: %d", errno); - lwip_close(this->notify_fd_); - this->notify_fd_ = -1; - return; - } - - // Connect to self (loopback) - allows using send() instead of sendto() - // After connect(), no need to store notify_addr - the socket remembers it - if (lwip_connect(this->notify_fd_, (struct sockaddr *) ¬ify_addr, sizeof(notify_addr)) < 0) { - ESP_LOGW(TAG, "Event socket connect failed: %d", errno); - lwip_close(this->notify_fd_); - this->notify_fd_ = -1; - return; - } - - // Set non-blocking mode - int flags = lwip_fcntl(this->notify_fd_, F_GETFL, 0); - lwip_fcntl(this->notify_fd_, F_SETFL, flags | O_NONBLOCK); - - // Register with application's select() loop - if (!App.register_socket_fd(this->notify_fd_)) { - ESP_LOGW(TAG, "Event socket register failed"); - lwip_close(this->notify_fd_); - this->notify_fd_ = -1; - return; - } - - ESP_LOGD(TAG, "Event socket ready"); -} - -void ESP32BLE::cleanup_event_notification_() { - if (this->notify_fd_ >= 0) { - App.unregister_socket_fd(this->notify_fd_); - lwip_close(this->notify_fd_); - this->notify_fd_ = -1; - ESP_LOGD(TAG, "Event socket closed"); - } -} - -void ESP32BLE::drain_event_notifications_() { - // Called from main loop to drain any pending notifications - // Must check is_socket_ready() to avoid blocking on empty socket - if (this->notify_fd_ >= 0 && App.is_socket_ready(this->notify_fd_)) { - char buffer[BLE_EVENT_NOTIFY_DRAIN_BUFFER_SIZE]; - // Drain all pending notifications with non-blocking reads - // Multiple BLE events may have triggered multiple writes, so drain until EWOULDBLOCK - // We control both ends of this loopback socket (always write 1 byte per event), - // so no error checking needed - any errors indicate catastrophic system failure - while (lwip_recvfrom(this->notify_fd_, buffer, sizeof(buffer), 0, nullptr, nullptr) > 0) { - // Just draining, no action needed - actual BLE events are already queued - } - } -} - -#endif // USE_SOCKET_SELECT_SUPPORT - uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) { uint64_t u = 0; u |= uint64_t(address[0] & 0xFF) << 40; diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 7c3195db6d..3be6a7048d 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -166,12 +166,10 @@ class ESP32BLE : public Component { void advertising_init_(); #endif -#ifdef USE_SOCKET_SELECT_SUPPORT - void setup_event_notification_(); // Create notification socket - void cleanup_event_notification_(); // Close and unregister socket - inline void notify_main_loop_(); // Wake up select() from BLE thread (hot path - inlined) - void drain_event_notifications_(); // Read pending notifications in main loop -#endif + // BLE uses the core wake_loop_threadsafe() mechanism to wake the main event loop + // from BLE tasks. This enables low-latency (~12μs) event processing instead of + // waiting for select() timeout (0-16ms). The wake socket is shared with other + // components that need this functionality. private: template friend void enqueue_ble_event(Args... args); @@ -207,13 +205,6 @@ class ESP32BLE : public Component { esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; // 4 bytes (enum) uint32_t advertising_cycle_time_{}; // 4 bytes -#ifdef USE_SOCKET_SELECT_SUPPORT - // Event notification socket for waking up main loop from BLE thread - // Uses connected UDP loopback socket to wake lwip_select() with ~12μs latency vs 0-16ms timeout - // Socket is connected during setup, allowing use of send() instead of sendto() for efficiency - int notify_fd_{-1}; // 4 bytes (file descriptor) -#endif - // 2-byte aligned members uint16_t appearance_{0}; // 2 bytes @@ -225,29 +216,6 @@ class ESP32BLE : public Component { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern ESP32BLE *global_ble; -#ifdef USE_SOCKET_SELECT_SUPPORT -// Inline implementations for hot-path functions -// These are called from BLE thread (notify) and main loop (drain) on every event - -// Small buffer for draining notification bytes (1 byte sent per BLE event) -// Size allows draining multiple notifications per recvfrom() without wasting stack -static constexpr size_t BLE_EVENT_NOTIFY_DRAIN_BUFFER_SIZE = 16; - -inline void ESP32BLE::notify_main_loop_() { - // Called from BLE thread context when events are queued - // Wakes up lwip_select() in main loop by writing to connected loopback socket - if (this->notify_fd_ >= 0) { - const char dummy = 1; - // Non-blocking send - if it fails (unlikely), select() will wake on timeout anyway - // No error checking needed: we control both ends of this loopback socket, and the - // BLE event is already queued. Notification is best-effort to reduce latency. - // This is safe to call from BLE thread - send() is thread-safe in lwip - // Socket is already connected to loopback address, so send() is faster than sendto() - lwip_send(this->notify_fd_, &dummy, 1, 0); - } -} -#endif // USE_SOCKET_SELECT_SUPPORT - template class BLEEnabledCondition : public Condition { public: bool check(Ts... x) override { return global_ble->is_active(); } diff --git a/esphome/components/socket/__init__.py b/esphome/components/socket/__init__.py index e6a4cfc07f..49e074a6ee 100644 --- a/esphome/components/socket/__init__.py +++ b/esphome/components/socket/__init__.py @@ -15,6 +15,9 @@ IMPLEMENTATION_BSD_SOCKETS = "bsd_sockets" # Components register their socket needs and platforms read this to configure appropriately KEY_SOCKET_CONSUMERS = "socket_consumers" +# Wake loop threadsafe support tracking +KEY_WAKE_LOOP_THREADSAFE_REQUIRED = "wake_loop_threadsafe_required" + def consume_sockets( value: int, consumer: str @@ -37,6 +40,30 @@ def consume_sockets( return _consume_sockets +def require_wake_loop_threadsafe() -> None: + """Mark that wake_loop_threadsafe support is required by a component. + + Call this from components that need to wake the main event loop from background threads. + This enables the shared UDP loopback socket mechanism (~208 bytes RAM). + The socket is shared across all components that use this feature. + + IMPORTANT: This is for background thread context only, NOT ISR context. + Socket operations are not safe to call from ISR handlers. + + Example: + from esphome.components import socket + + async def to_code(config): + socket.require_wake_loop_threadsafe() + """ + # Only set up once (idempotent - multiple components can call this) + if not CORE.data.get(KEY_WAKE_LOOP_THREADSAFE_REQUIRED, False): + CORE.data[KEY_WAKE_LOOP_THREADSAFE_REQUIRED] = True + cg.add_define("USE_WAKE_LOOP_THREADSAFE") + # Consume 1 socket for the shared wake notification socket + consume_sockets(1, "socket.wake_loop_threadsafe")({}) + + CONFIG_SCHEMA = cv.Schema( { cv.SplitDefault( diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 61cfcc7585..75814ae253 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -122,6 +122,11 @@ void Application::setup() { // Clear setup priority overrides to free memory clear_setup_priority_overrides(); +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + // Set up wake socket for waking main loop from tasks + this->setup_wake_loop_threadsafe_(); +#endif + this->schedule_dump_config(); } void Application::loop() { @@ -472,6 +477,11 @@ void Application::enable_pending_loops_() { } void Application::before_loop_tasks_(uint32_t loop_start_time) { +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + // Drain wake notifications first to clear socket for next wake + this->drain_wake_notifications_(); +#endif + // Process scheduled tasks this->scheduler.call(loop_start_time); @@ -625,4 +635,73 @@ void Application::yield_with_select_(uint32_t delay_ms) { Application App; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) +void Application::setup_wake_loop_threadsafe_() { + // Create UDP socket for wake notifications + this->wake_socket_fd_ = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (this->wake_socket_fd_ < 0) { + ESP_LOGW(TAG, "Wake socket create failed: %d", errno); + return; + } + + // Bind to loopback with auto-assigned port + struct sockaddr_in addr = {}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = lwip_htonl(INADDR_LOOPBACK); + addr.sin_port = 0; // Auto-assign port + + if (lwip_bind(this->wake_socket_fd_, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + ESP_LOGW(TAG, "Wake socket bind failed: %d", errno); + lwip_close(this->wake_socket_fd_); + this->wake_socket_fd_ = -1; + return; + } + + // Get the assigned address and connect to it + // Connecting a UDP socket allows using send() instead of sendto() for better performance + struct sockaddr_in wake_addr; + socklen_t len = sizeof(wake_addr); + if (lwip_getsockname(this->wake_socket_fd_, (struct sockaddr *) &wake_addr, &len) < 0) { + ESP_LOGW(TAG, "Wake socket address failed: %d", errno); + lwip_close(this->wake_socket_fd_); + this->wake_socket_fd_ = -1; + return; + } + + // Connect to self (loopback) - allows using send() instead of sendto() + // After connect(), no need to store wake_addr - the socket remembers it + if (lwip_connect(this->wake_socket_fd_, (struct sockaddr *) &wake_addr, sizeof(wake_addr)) < 0) { + ESP_LOGW(TAG, "Wake socket connect failed: %d", errno); + lwip_close(this->wake_socket_fd_); + this->wake_socket_fd_ = -1; + return; + } + + // Set non-blocking mode + int flags = lwip_fcntl(this->wake_socket_fd_, F_GETFL, 0); + lwip_fcntl(this->wake_socket_fd_, F_SETFL, flags | O_NONBLOCK); + + // Register with application's select() loop + if (!this->register_socket_fd(this->wake_socket_fd_)) { + ESP_LOGW(TAG, "Wake socket register failed"); + lwip_close(this->wake_socket_fd_); + this->wake_socket_fd_ = -1; + return; + } +} + +void Application::wake_loop_threadsafe() { + // Called from FreeRTOS task context when events need immediate processing + // Wakes up lwip_select() in main loop by writing to connected loopback socket + if (this->wake_socket_fd_ >= 0) { + const char dummy = 1; + // Non-blocking send - if it fails (unlikely), select() will wake on timeout anyway + // No error checking needed: we control both ends of this loopback socket. + // This is safe to call from FreeRTOS tasks - send() is thread-safe in lwip + // Socket is already connected to loopback address, so send() is faster than sendto() + lwip_send(this->wake_socket_fd_, &dummy, 1, 0); + } +} +#endif // defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + } // namespace esphome diff --git a/esphome/core/application.h b/esphome/core/application.h index 29a734f000..dae44d8902 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -21,7 +21,10 @@ #ifdef USE_SOCKET_SELECT_SUPPORT #include +#ifdef USE_WAKE_LOOP_THREADSAFE +#include #endif +#endif // USE_SOCKET_SELECT_SUPPORT #ifdef USE_BINARY_SENSOR #include "esphome/components/binary_sensor/binary_sensor.h" @@ -429,6 +432,13 @@ class Application { /// Check if there's data available on a socket without blocking /// This function is thread-safe for reading, but should be called after select() has run bool is_socket_ready(int fd) const; + +#ifdef USE_WAKE_LOOP_THREADSAFE + /// Wake the main event loop from a FreeRTOS task + /// Thread-safe, can be called from task context to immediately wake select() + /// IMPORTANT: NOT safe to call from ISR context (socket operations not ISR-safe) + void wake_loop_threadsafe(); +#endif #endif protected: @@ -454,6 +464,11 @@ class Application { /// Perform a delay while also monitoring socket file descriptors for readiness void yield_with_select_(uint32_t delay_ms); +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + void setup_wake_loop_threadsafe_(); // Create wake notification socket + inline void drain_wake_notifications_(); // Read pending wake notifications in main loop (hot path - inlined) +#endif + // === Member variables ordered by size to minimize padding === // Pointer-sized members first @@ -481,6 +496,9 @@ class Application { FixedVector looping_components_{}; #ifdef USE_SOCKET_SELECT_SUPPORT std::vector socket_fds_; // Vector of all monitored socket file descriptors +#ifdef USE_WAKE_LOOP_THREADSAFE + int wake_socket_fd_{-1}; // Shared wake notification socket for waking main loop from tasks +#endif #endif // std::string members (typically 24-32 bytes each) @@ -597,4 +615,28 @@ class Application { /// Global storage of Application pointer - only one Application can exist. extern Application App; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) +// Inline implementations for hot-path functions +// drain_wake_notifications_() is called on every loop iteration + +// Small buffer for draining wake notification bytes (1 byte sent per wake) +// Size allows draining multiple notifications per recvfrom() without wasting stack +static constexpr size_t WAKE_NOTIFY_DRAIN_BUFFER_SIZE = 16; + +inline void Application::drain_wake_notifications_() { + // Called from main loop to drain any pending wake notifications + // Must check is_socket_ready() to avoid blocking on empty socket + if (this->wake_socket_fd_ >= 0 && this->is_socket_ready(this->wake_socket_fd_)) { + char buffer[WAKE_NOTIFY_DRAIN_BUFFER_SIZE]; + // Drain all pending notifications with non-blocking reads + // Multiple wake events may have triggered multiple writes, so drain until EWOULDBLOCK + // We control both ends of this loopback socket (always write 1 byte per wake), + // so no error checking needed - any errors indicate catastrophic system failure + while (lwip_recvfrom(this->wake_socket_fd_, buffer, sizeof(buffer), 0, nullptr, nullptr) > 0) { + // Just draining, no action needed - wake has already occurred + } + } +} +#endif // defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + } // namespace esphome diff --git a/esphome/core/defines.h b/esphome/core/defines.h index dc37dcbc0e..2be32058ea 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -196,6 +196,7 @@ #define USE_PSRAM #define USE_SOCKET_IMPL_BSD_SOCKETS #define USE_SOCKET_SELECT_SUPPORT +#define USE_WAKE_LOOP_THREADSAFE #define USE_SPEAKER #define USE_SPI #define USE_VOICE_ASSISTANT diff --git a/tests/components/socket/conftest.py b/tests/components/socket/conftest.py new file mode 100644 index 0000000000..5d93cac232 --- /dev/null +++ b/tests/components/socket/conftest.py @@ -0,0 +1,12 @@ +"""Configuration file for socket component tests.""" + +import pytest + +from esphome.core import CORE + + +@pytest.fixture(autouse=True) +def reset_core(): + """Reset CORE after each test.""" + yield + CORE.reset() diff --git a/tests/components/socket/test_wake_loop_threadsafe.py b/tests/components/socket/test_wake_loop_threadsafe.py new file mode 100644 index 0000000000..45e5ea2211 --- /dev/null +++ b/tests/components/socket/test_wake_loop_threadsafe.py @@ -0,0 +1,42 @@ +from esphome.components import socket +from esphome.core import CORE + + +def test_require_wake_loop_threadsafe__first_call() -> None: + """Test that first call sets up define and consumes socket.""" + socket.require_wake_loop_threadsafe() + + # Verify CORE.data was updated + assert CORE.data[socket.KEY_WAKE_LOOP_THREADSAFE_REQUIRED] is True + + # Verify the define was added + assert any(d.name == "USE_WAKE_LOOP_THREADSAFE" for d in CORE.defines) + + +def test_require_wake_loop_threadsafe__idempotent() -> None: + """Test that subsequent calls are idempotent.""" + # Set up initial state as if already called + CORE.data[socket.KEY_WAKE_LOOP_THREADSAFE_REQUIRED] = True + + # Call again - should not raise or fail + socket.require_wake_loop_threadsafe() + + # Verify state is still True + assert CORE.data[socket.KEY_WAKE_LOOP_THREADSAFE_REQUIRED] is True + + # Define should not be added since flag was already True + assert not any(d.name == "USE_WAKE_LOOP_THREADSAFE" for d in CORE.defines) + + +def test_require_wake_loop_threadsafe__multiple_calls() -> None: + """Test that multiple calls only set up once.""" + # Call three times + socket.require_wake_loop_threadsafe() + socket.require_wake_loop_threadsafe() + socket.require_wake_loop_threadsafe() + + # Verify CORE.data was set + assert CORE.data[socket.KEY_WAKE_LOOP_THREADSAFE_REQUIRED] is True + + # Verify the define was added (only once, but we can just check it exists) + assert any(d.name == "USE_WAKE_LOOP_THREADSAFE" for d in CORE.defines) From fb7dbc99103fa1225a88df22833f433d093cc623 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Nov 2025 13:50:39 -0600 Subject: [PATCH 365/526] [usb_host] Add wake_loop_threadsafe() for low-latency USB event processing (#11683) --- esphome/components/usb_host/__init__.py | 8 +++++++- esphome/components/usb_host/usb_host_client.cpp | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/esphome/components/usb_host/__init__.py b/esphome/components/usb_host/__init__.py index d452e0e9fa..cccabcf646 100644 --- a/esphome/components/usb_host/__init__.py +++ b/esphome/components/usb_host/__init__.py @@ -1,4 +1,5 @@ import esphome.codegen as cg +from esphome.components import socket from esphome.components.esp32 import ( VARIANT_ESP32P4, VARIANT_ESP32S2, @@ -11,7 +12,7 @@ from esphome.const import CONF_DEVICES, CONF_ID from esphome.cpp_types import Component from esphome.types import ConfigType -AUTO_LOAD = ["bytebuffer"] +AUTO_LOAD = ["bytebuffer", "socket"] CODEOWNERS = ["@clydebarrow"] DEPENDENCIES = ["esp32"] usb_host_ns = cg.esphome_ns.namespace("usb_host") @@ -71,6 +72,11 @@ async def to_code(config: ConfigType) -> None: max_requests = config[CONF_MAX_TRANSFER_REQUESTS] cg.add_define("USB_HOST_MAX_REQUESTS", max_requests) + # USB uses the socket wake_loop_threadsafe() mechanism to wake the main loop from USB task + # This enables low-latency (~12μs) USB event processing instead of waiting for + # select() timeout (0-16ms). The wake socket is shared across all components. + socket.require_wake_loop_threadsafe() + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) for device in config.get(CONF_DEVICES) or (): diff --git a/esphome/components/usb_host/usb_host_client.cpp b/esphome/components/usb_host/usb_host_client.cpp index 2139ed869a..0dda36b9d7 100644 --- a/esphome/components/usb_host/usb_host_client.cpp +++ b/esphome/components/usb_host/usb_host_client.cpp @@ -3,6 +3,7 @@ #include "usb_host.h" #include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/application.h" #include "esphome/components/bytebuffer/bytebuffer.h" #include @@ -174,6 +175,11 @@ static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void * // Push to lock-free queue (always succeeds since pool size == queue size) client->event_queue.push(event); + + // Wake main loop immediately to process USB event instead of waiting for select() timeout +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + App.wake_loop_threadsafe(); +#endif } void USBClient::setup() { usb_host_client_config_t config{.is_synchronous = false, From 9c7cb30ae597a4d132b2242cc1fca55067a6e4b3 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:08:50 -0500 Subject: [PATCH 366/526] [esp32_hosted] Initial OTA implementation (#11562) --- CODEOWNERS | 1 + .../esp32_hosted/update/__init__.py | 78 +++++++++ .../update/esp32_hosted_update.cpp | 164 ++++++++++++++++++ .../esp32_hosted/update/esp32_hosted_update.h | 32 ++++ esphome/idf_component.yml | 6 +- script/ci-custom.py | 1 + tests/components/esp32_hosted/.gitattributes | 1 + .../esp32_hosted/test.esp32-p4-idf.yaml | 6 + .../components/esp32_hosted/test_firmware.bin | Bin 0 -> 65536 bytes 9 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 esphome/components/esp32_hosted/update/__init__.py create mode 100644 esphome/components/esp32_hosted/update/esp32_hosted_update.cpp create mode 100644 esphome/components/esp32_hosted/update/esp32_hosted_update.h create mode 100644 tests/components/esp32_hosted/.gitattributes create mode 100644 tests/components/esp32_hosted/test_firmware.bin diff --git a/CODEOWNERS b/CODEOWNERS index 667a44fc03..fee0e98f46 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -155,6 +155,7 @@ esphome/components/esp32_ble_tracker/* @bdraco esphome/components/esp32_camera_web_server/* @ayufan esphome/components/esp32_can/* @Sympatron esphome/components/esp32_hosted/* @swoboda1337 +esphome/components/esp32_hosted/update/* @swoboda1337 esphome/components/esp32_improv/* @jesserockz esphome/components/esp32_rmt/* @jesserockz esphome/components/esp32_rmt_led_strip/* @jesserockz diff --git a/esphome/components/esp32_hosted/update/__init__.py b/esphome/components/esp32_hosted/update/__init__.py new file mode 100644 index 0000000000..040f989a64 --- /dev/null +++ b/esphome/components/esp32_hosted/update/__init__.py @@ -0,0 +1,78 @@ +import hashlib +from typing import Any + +import esphome.codegen as cg +from esphome.components import esp32, update +import esphome.config_validation as cv +from esphome.const import CONF_PATH, CONF_RAW_DATA_ID +from esphome.core import CORE, HexInt + +CODEOWNERS = ["@swoboda1337"] +AUTO_LOAD = ["sha256", "watchdog"] +DEPENDENCIES = ["esp32_hosted"] + +CONF_SHA256 = "sha256" + +esp32_hosted_ns = cg.esphome_ns.namespace("esp32_hosted") +Esp32HostedUpdate = esp32_hosted_ns.class_( + "Esp32HostedUpdate", update.UpdateEntity, cg.Component +) + + +def _validate_sha256(value: Any) -> str: + value = cv.string_strict(value) + if len(value) != 64: + raise cv.Invalid("SHA256 must be 64 hexadecimal characters") + try: + bytes.fromhex(value) + except ValueError as e: + raise cv.Invalid(f"SHA256 must be valid hexadecimal: {e}") from e + return value + + +CONFIG_SCHEMA = cv.All( + update.update_schema(Esp32HostedUpdate, device_class="firmware").extend( + { + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), + cv.Required(CONF_PATH): cv.file_, + cv.Required(CONF_SHA256): _validate_sha256, + } + ), + esp32.only_on_variant( + supported=[ + esp32.const.VARIANT_ESP32H2, + esp32.const.VARIANT_ESP32P4, + ] + ), +) + + +def _validate_firmware(config: dict[str, Any]) -> None: + path = CORE.relative_config_path(config[CONF_PATH]) + with open(path, "rb") as f: + firmware_data = f.read() + calculated = hashlib.sha256(firmware_data).hexdigest() + expected = config[CONF_SHA256].lower() + if calculated != expected: + raise cv.Invalid( + f"SHA256 mismatch for {config[CONF_PATH]}: expected {expected}, got {calculated}" + ) + + +FINAL_VALIDATE_SCHEMA = _validate_firmware + + +async def to_code(config: dict[str, Any]) -> None: + var = await update.new_update(config) + + path = config[CONF_PATH] + with open(CORE.relative_config_path(path), "rb") as f: + firmware_data = f.read() + rhs = [HexInt(x) for x in firmware_data] + prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) + + sha256_bytes = bytes.fromhex(config[CONF_SHA256]) + cg.add(var.set_firmware_sha256([HexInt(b) for b in sha256_bytes])) + cg.add(var.set_firmware_data(prog_arr)) + cg.add(var.set_firmware_size(len(firmware_data))) + await cg.register_component(var, config) diff --git a/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp b/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp new file mode 100644 index 0000000000..adbcc5bf11 --- /dev/null +++ b/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp @@ -0,0 +1,164 @@ +#if defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32P4) +#include "esp32_hosted_update.h" +#include "esphome/components/watchdog/watchdog.h" +#include "esphome/components/sha256/sha256.h" +#include "esphome/core/application.h" +#include "esphome/core/log.h" +#include +#include +#include + +extern "C" { +#include +} + +namespace esphome::esp32_hosted { + +static const char *const TAG = "esp32_hosted.update"; + +// older coprocessor firmware versions have a 1500-byte limit per RPC call +constexpr size_t CHUNK_SIZE = 1500; + +void Esp32HostedUpdate::setup() { + this->update_info_.title = "ESP32 Hosted Coprocessor"; + + // get coprocessor version + esp_hosted_coprocessor_fwver_t ver_info; + if (esp_hosted_get_coprocessor_fwversion(&ver_info) == ESP_OK) { + this->update_info_.current_version = str_sprintf("%d.%d.%d", ver_info.major1, ver_info.minor1, ver_info.patch1); + } else { + this->update_info_.current_version = "unknown"; + } + ESP_LOGD(TAG, "Coprocessor version: %s", this->update_info_.current_version.c_str()); + + // get image version + const int app_desc_offset = sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t); + if (this->firmware_size_ >= app_desc_offset + sizeof(esp_app_desc_t)) { + esp_app_desc_t *app_desc = (esp_app_desc_t *) (this->firmware_data_ + app_desc_offset); + if (app_desc->magic_word == ESP_APP_DESC_MAGIC_WORD) { + ESP_LOGD(TAG, "Firmware version: %s", app_desc->version); + ESP_LOGD(TAG, "Project name: %s", app_desc->project_name); + ESP_LOGD(TAG, "Build date: %s", app_desc->date); + ESP_LOGD(TAG, "Build time: %s", app_desc->time); + ESP_LOGD(TAG, "IDF version: %s", app_desc->idf_ver); + this->update_info_.latest_version = app_desc->version; + if (this->update_info_.latest_version != this->update_info_.current_version) { + this->state_ = update::UPDATE_STATE_AVAILABLE; + } else { + this->state_ = update::UPDATE_STATE_NO_UPDATE; + } + } else { + ESP_LOGW(TAG, "Invalid app description magic word: 0x%08x (expected 0x%08x)", app_desc->magic_word, + ESP_APP_DESC_MAGIC_WORD); + this->state_ = update::UPDATE_STATE_NO_UPDATE; + } + } else { + ESP_LOGW(TAG, "Firmware too small to contain app description"); + this->state_ = update::UPDATE_STATE_NO_UPDATE; + } + + // publish state + this->status_clear_error(); + this->publish_state(); +} + +void Esp32HostedUpdate::dump_config() { + ESP_LOGCONFIG(TAG, + "ESP32 Hosted Update:\n" + " Current Version: %s\n" + " Latest Version: %s\n" + " Latest Size: %zu bytes", + this->update_info_.current_version.c_str(), this->update_info_.latest_version.c_str(), + this->firmware_size_); +} + +void Esp32HostedUpdate::perform(bool force) { + if (this->state_ != update::UPDATE_STATE_AVAILABLE && !force) { + ESP_LOGW(TAG, "Update not available"); + return; + } + + if (this->firmware_data_ == nullptr || this->firmware_size_ == 0) { + ESP_LOGE(TAG, "No firmware data available"); + return; + } + + sha256::SHA256 hasher; + hasher.init(); + hasher.add(this->firmware_data_, this->firmware_size_); + hasher.calculate(); + if (!hasher.equals_bytes(this->firmware_sha256_.data())) { + this->status_set_error("SHA256 verification failed"); + this->publish_state(); + return; + } + + ESP_LOGI(TAG, "Starting OTA update (%zu bytes)", this->firmware_size_); + + watchdog::WatchdogManager watchdog(20000); + update::UpdateState prev_state = this->state_; + this->state_ = update::UPDATE_STATE_INSTALLING; + this->update_info_.has_progress = false; + this->publish_state(); + + esp_err_t err = esp_hosted_slave_ota_begin(); // NOLINT + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to begin OTA: %s", esp_err_to_name(err)); + this->state_ = prev_state; + this->status_set_error("Failed to begin OTA"); + this->publish_state(); + return; + } + + uint8_t chunk[CHUNK_SIZE]; + const uint8_t *data_ptr = this->firmware_data_; + size_t remaining = this->firmware_size_; + while (remaining > 0) { + size_t chunk_size = std::min(remaining, static_cast(CHUNK_SIZE)); + memcpy(chunk, data_ptr, chunk_size); + err = esp_hosted_slave_ota_write(chunk, chunk_size); // NOLINT + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write OTA data: %s", esp_err_to_name(err)); + esp_hosted_slave_ota_end(); // NOLINT + this->state_ = prev_state; + this->status_set_error("Failed to write OTA data"); + this->publish_state(); + return; + } + data_ptr += chunk_size; + remaining -= chunk_size; + App.feed_wdt(); + } + + err = esp_hosted_slave_ota_end(); // NOLINT + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to end OTA: %s", esp_err_to_name(err)); + this->state_ = prev_state; + this->status_set_error("Failed to end OTA"); + this->publish_state(); + return; + } + + // activate new firmware + err = esp_hosted_slave_ota_activate(); // NOLINT + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to activate OTA: %s", esp_err_to_name(err)); + this->state_ = prev_state; + this->status_set_error("Failed to activate OTA"); + this->publish_state(); + return; + } + + // update state + ESP_LOGI(TAG, "OTA update successful"); + this->state_ = update::UPDATE_STATE_NO_UPDATE; + this->status_clear_error(); + this->publish_state(); + + // schedule a restart to ensure everything is in sync + ESP_LOGI(TAG, "Restarting in 1 second"); + this->set_timeout(1000, []() { App.safe_reboot(); }); +} + +} // namespace esphome::esp32_hosted +#endif diff --git a/esphome/components/esp32_hosted/update/esp32_hosted_update.h b/esphome/components/esp32_hosted/update/esp32_hosted_update.h new file mode 100644 index 0000000000..9c087bf72a --- /dev/null +++ b/esphome/components/esp32_hosted/update/esp32_hosted_update.h @@ -0,0 +1,32 @@ +#pragma once + +#if defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32P4) + +#include "esphome/core/component.h" +#include "esphome/components/update/update_entity.h" +#include + +namespace esphome::esp32_hosted { + +class Esp32HostedUpdate : public update::UpdateEntity, public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + + void perform(bool force) override; + void check() override {} + + void set_firmware_data(const uint8_t *data) { this->firmware_data_ = data; } + void set_firmware_size(size_t size) { this->firmware_size_ = size; } + void set_firmware_sha256(const std::array &sha256) { this->firmware_sha256_ = sha256; } + + protected: + const uint8_t *firmware_data_{nullptr}; + size_t firmware_size_{0}; + std::array firmware_sha256_; +}; + +} // namespace esphome::esp32_hosted + +#endif diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index 1a6dc8b97d..31112caf0a 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -6,15 +6,15 @@ dependencies: espressif/mdns: version: 1.8.2 espressif/esp_wifi_remote: - version: 0.10.2 + version: 1.1.5 rules: - if: "target in [esp32h2, esp32p4]" espressif/eppp_link: - version: 0.2.0 + version: 1.1.3 rules: - if: "target in [esp32h2, esp32p4]" espressif/esp_hosted: - version: 2.0.11 + version: 2.6.1 rules: - if: "target in [esp32h2, esp32p4]" zorxx/multipart-parser: diff --git a/script/ci-custom.py b/script/ci-custom.py index 6b01623d92..106aa438fe 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -71,6 +71,7 @@ ignore_types = ( ".apng", ".gif", ".webp", + ".bin", ) LINT_FILE_CHECKS = [] diff --git a/tests/components/esp32_hosted/.gitattributes b/tests/components/esp32_hosted/.gitattributes new file mode 100644 index 0000000000..6abdc56117 --- /dev/null +++ b/tests/components/esp32_hosted/.gitattributes @@ -0,0 +1 @@ +*.bin -text diff --git a/tests/components/esp32_hosted/test.esp32-p4-idf.yaml b/tests/components/esp32_hosted/test.esp32-p4-idf.yaml index dade44d145..2a76f17e2f 100644 --- a/tests/components/esp32_hosted/test.esp32-p4-idf.yaml +++ b/tests/components/esp32_hosted/test.esp32-p4-idf.yaml @@ -1 +1,7 @@ <<: !include common.yaml + +update: + - platform: esp32_hosted + name: "Coprocessor Firmware Update" + path: $component_dir/test_firmware.bin + sha256: de2f256064a0af797747c2b97505dc0b9f3df0de4f489eac731c23ae9ca9cc31 diff --git a/tests/components/esp32_hosted/test_firmware.bin b/tests/components/esp32_hosted/test_firmware.bin new file mode 100644 index 0000000000000000000000000000000000000000..c97c12f9b0a24bfc19c74a2b265a97c924137775 GIT binary patch literal 65536 zcmeIufdBvi0Dz$VsTV1P3IhfV7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ u0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFi-;k4*&rG literal 0 HcmV?d00001 From 8aa8bb8f9898760dfb169c57706b91e9833da785 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 4 Nov 2025 07:45:32 +1000 Subject: [PATCH 367/526] [epaper_spi] Refactoring (#11540) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/display/display.h | 2 +- esphome/components/epaper_spi/display.py | 124 ++++++++-- esphome/components/epaper_spi/epaper_spi.cpp | 210 ++++++++++------- esphome/components/epaper_spi/epaper_spi.h | 104 ++++++--- .../epaper_spi_model_7p3in_spectra_e6.cpp | 42 ---- .../epaper_spi_model_7p3in_spectra_e6.h | 45 ---- .../epaper_spi/epaper_spi_spectra_e6.cpp | 221 ++++++++++-------- .../epaper_spi/epaper_spi_spectra_e6.h | 15 +- .../components/epaper_spi/models/__init__.py | 65 ++++++ .../epaper_spi/models/spectra_e6.py | 51 ++++ esphome/components/mipi/__init__.py | 23 +- .../components/split_buffer/split_buffer.cpp | 43 ++-- .../components/split_buffer/split_buffer.h | 10 +- .../epaper_spi/test.esp32-s3-idf.yaml | 8 +- 14 files changed, 619 insertions(+), 344 deletions(-) delete mode 100644 esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.cpp delete mode 100644 esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.h create mode 100644 esphome/components/epaper_spi/models/__init__.py create mode 100644 esphome/components/epaper_spi/models/spectra_e6.py diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index f2d79d12d9..14205da853 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -210,7 +210,7 @@ class Display : public PollingComponent { /// Fill the entire screen with the given color. virtual void fill(Color color); /// Clear the entire screen by filling it with OFF pixels. - void clear(); + virtual void clear(); /// Get the calculated width of the display in pixels with rotation applied. virtual int get_width() { return this->get_width_internal(); } diff --git a/esphome/components/epaper_spi/display.py b/esphome/components/epaper_spi/display.py index 20549f049d..9ff393b397 100644 --- a/esphome/components/epaper_spi/display.py +++ b/esphome/components/epaper_spi/display.py @@ -1,21 +1,35 @@ +import importlib +import pkgutil + from esphome import core, pins import esphome.codegen as cg from esphome.components import display, spi +from esphome.components.mipi import flatten_sequence, map_sequence import esphome.config_validation as cv from esphome.const import ( CONF_BUSY_PIN, + CONF_CS_PIN, + CONF_DATA_RATE, CONF_DC_PIN, + CONF_DIMENSIONS, + CONF_ENABLE_PIN, + CONF_HEIGHT, CONF_ID, + CONF_INIT_SEQUENCE, CONF_LAMBDA, CONF_MODEL, - CONF_PAGES, CONF_RESET_DURATION, CONF_RESET_PIN, + CONF_WIDTH, ) +from . import models + AUTO_LOAD = ["split_buffer"] DEPENDENCIES = ["spi"] +CONF_INIT_SEQUENCE_ID = "init_sequence_id" + epaper_spi_ns = cg.esphome_ns.namespace("epaper_spi") EPaperBase = epaper_spi_ns.class_( "EPaperBase", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer @@ -24,30 +38,79 @@ EPaperBase = epaper_spi_ns.class_( EPaperSpectraE6 = epaper_spi_ns.class_("EPaperSpectraE6", EPaperBase) EPaper7p3InSpectraE6 = epaper_spi_ns.class_("EPaper7p3InSpectraE6", EPaperSpectraE6) -MODELS = { - "7.3in-spectra-e6": EPaper7p3InSpectraE6, -} +# Import all models dynamically from the models package +for module_info in pkgutil.iter_modules(models.__path__): + importlib.import_module(f".models.{module_info.name}", package=__package__) -CONFIG_SCHEMA = cv.All( - display.FULL_DISPLAY_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(EPaperBase), - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_MODEL): cv.one_of(*MODELS, lower=True, space="-"), - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema, - cv.Optional(CONF_RESET_DURATION): cv.All( - cv.positive_time_period_milliseconds, - cv.Range(max=core.TimePeriod(milliseconds=500)), - ), - } - ) - .extend(cv.polling_component_schema("60s")) - .extend(spi.spi_device_schema()), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +MODELS = models.EpaperModel.models + +DIMENSION_SCHEMA = cv.Schema( + { + cv.Required(CONF_WIDTH): cv.int_, + cv.Required(CONF_HEIGHT): cv.int_, + } ) + +def model_schema(config): + model = MODELS[config[CONF_MODEL]] + class_name = epaper_spi_ns.class_(model.class_name, EPaperBase) + cv_dimensions = cv.Optional if model.get_default(CONF_WIDTH) else cv.Required + return ( + display.FULL_DISPLAY_SCHEMA.extend( + spi.spi_device_schema( + cs_pin_required=False, + default_mode="MODE0", + default_data_rate=model.get_default(CONF_DATA_RATE, 10_000_000), + ) + ) + .extend( + { + model.option(pin): pins.gpio_output_pin_schema + for pin in (CONF_RESET_PIN, CONF_CS_PIN, CONF_BUSY_PIN) + } + ) + .extend( + { + cv.Required(CONF_MODEL): cv.one_of(model.name, upper=True), + model.option(CONF_DC_PIN, fallback=None): pins.gpio_output_pin_schema, + cv.GenerateID(): cv.declare_id(class_name), + cv.GenerateID(CONF_INIT_SEQUENCE_ID): cv.declare_id(cg.uint8), + cv_dimensions(CONF_DIMENSIONS): DIMENSION_SCHEMA, + model.option(CONF_ENABLE_PIN): cv.ensure_list( + pins.gpio_output_pin_schema + ), + model.option(CONF_INIT_SEQUENCE, cv.UNDEFINED): cv.ensure_list( + map_sequence + ), + model.option(CONF_RESET_DURATION, cv.UNDEFINED): cv.All( + cv.positive_time_period_milliseconds, + cv.Range(max=core.TimePeriod(milliseconds=500)), + ), + } + ) + ) + + +def customise_schema(config): + """ + Create a customised config schema for a specific model and validate the configuration. + :param config: The configuration dictionary to validate + :return: The validated configuration dictionary + :raises cv.Invalid: If the configuration is invalid + """ + config = cv.Schema( + { + cv.Required(CONF_MODEL): cv.one_of(*MODELS, upper=True), + }, + extra=cv.ALLOW_EXTRA, + )(config) + return model_schema(config)(config) + + +CONFIG_SCHEMA = customise_schema + FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( "epaper_spi", require_miso=False, require_mosi=True ) @@ -56,8 +119,23 @@ FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( async def to_code(config): model = MODELS[config[CONF_MODEL]] - rhs = model.new() - var = cg.Pvariable(config[CONF_ID], rhs, model) + init_sequence = config.get(CONF_INIT_SEQUENCE) + if init_sequence is None: + init_sequence = model.get_init_sequence(config) + init_sequence = flatten_sequence(init_sequence) + init_sequence_length = len(init_sequence) + init_sequence_id = cg.static_const_array( + config[CONF_INIT_SEQUENCE_ID], init_sequence + ) + width, height = model.get_dimensions(config) + var = cg.new_Pvariable( + config[CONF_ID], + model.name, + width, + height, + init_sequence_id, + init_sequence_length, + ) await display.register_display(var, config) await spi.register_spi_device(var, config) diff --git a/esphome/components/epaper_spi/epaper_spi.cpp b/esphome/components/epaper_spi/epaper_spi.cpp index 9630ea7f8b..cf6a0b0c3d 100644 --- a/esphome/components/epaper_spi/epaper_spi.cpp +++ b/esphome/components/epaper_spi/epaper_spi.cpp @@ -8,33 +8,20 @@ namespace esphome::epaper_spi { static const char *const TAG = "epaper_spi"; -static const LogString *epaper_state_to_string(EPaperState state) { - switch (state) { - case EPaperState::IDLE: - return LOG_STR("IDLE"); - case EPaperState::UPDATE: - return LOG_STR("UPDATE"); - case EPaperState::RESET: - return LOG_STR("RESET"); - case EPaperState::INITIALISE: - return LOG_STR("INITIALISE"); - case EPaperState::TRANSFER_DATA: - return LOG_STR("TRANSFER_DATA"); - case EPaperState::POWER_ON: - return LOG_STR("POWER_ON"); - case EPaperState::REFRESH_SCREEN: - return LOG_STR("REFRESH_SCREEN"); - case EPaperState::POWER_OFF: - return LOG_STR("POWER_OFF"); - case EPaperState::DEEP_SLEEP: - return LOG_STR("DEEP_SLEEP"); - default: - return LOG_STR("UNKNOWN"); - } +static constexpr const char *const EPAPER_STATE_STRINGS[] = { + "IDLE", "UPDATE", "RESET", "RESET_END", + + "SHOULD_WAIT", "INITIALISE", "TRANSFER_DATA", "POWER_ON", "REFRESH_SCREEN", "POWER_OFF", "DEEP_SLEEP", +}; + +const char *EPaperBase::epaper_state_to_string_() { + if (auto idx = static_cast(this->state_); idx < std::size(EPAPER_STATE_STRINGS)) + return EPAPER_STATE_STRINGS[idx]; + return "Unknown"; } void EPaperBase::setup() { - if (!this->init_buffer_(this->get_buffer_length())) { + if (!this->init_buffer_(this->buffer_length_)) { this->mark_failed("Failed to initialise buffer"); return; } @@ -50,7 +37,7 @@ bool EPaperBase::init_buffer_(size_t buffer_length) { return true; } -void EPaperBase::setup_pins_() { +void EPaperBase::setup_pins_() const { this->dc_pin_->setup(); // OUTPUT this->dc_pin_->digital_write(false); @@ -81,11 +68,7 @@ void EPaperBase::data(uint8_t value) { // write a command followed by zero or more bytes of data. // The command is the first byte, length is the length of data only in the second byte, followed by the data. // [COMMAND, LENGTH, DATA...] -void EPaperBase::cmd_data(const uint8_t *data) { - const uint8_t command = data[0]; - const uint8_t length = data[1]; - const uint8_t *ptr = data + 2; - +void EPaperBase::cmd_data(uint8_t command, const uint8_t *ptr, size_t length) { ESP_LOGVV(TAG, "Command: 0x%02X, Length: %d, Data: %s", command, length, format_hex_pretty(ptr, length, '.', false).c_str()); @@ -99,91 +82,146 @@ void EPaperBase::cmd_data(const uint8_t *data) { this->disable(); } -bool EPaperBase::is_idle_() { +bool EPaperBase::is_idle_() const { if (this->busy_pin_ == nullptr) { return true; } - return this->busy_pin_->digital_read(); + return !this->busy_pin_->digital_read(); } -void EPaperBase::reset() { +bool EPaperBase::reset_() const { if (this->reset_pin_ != nullptr) { - this->reset_pin_->digital_write(false); - this->disable_loop(); - this->set_timeout(this->reset_duration_, [this] { - this->reset_pin_->digital_write(true); - this->set_timeout(20, [this] { this->enable_loop(); }); - }); + if (this->state_ == EPaperState::RESET) { + this->reset_pin_->digital_write(false); + return false; + } + this->reset_pin_->digital_write(true); } + return true; } void EPaperBase::update() { - if (!this->state_queue_.empty()) { - ESP_LOGE(TAG, "Display update already in progress - %s", - LOG_STR_ARG(epaper_state_to_string(this->state_queue_.front()))); + if (this->state_ != EPaperState::IDLE) { + ESP_LOGE(TAG, "Display already in state %s", epaper_state_to_string_()); return; } - - this->state_queue_.push(EPaperState::UPDATE); - this->state_queue_.push(EPaperState::RESET); - this->state_queue_.push(EPaperState::INITIALISE); - this->state_queue_.push(EPaperState::TRANSFER_DATA); - this->state_queue_.push(EPaperState::POWER_ON); - this->state_queue_.push(EPaperState::REFRESH_SCREEN); - this->state_queue_.push(EPaperState::POWER_OFF); - this->state_queue_.push(EPaperState::DEEP_SLEEP); - this->state_queue_.push(EPaperState::IDLE); - + this->set_state_(EPaperState::RESET); this->enable_loop(); } +void EPaperBase::wait_for_idle_(bool should_wait) { +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE + if (should_wait) { + this->waiting_for_idle_start_ = millis(); + this->waiting_for_idle_last_print_ = this->waiting_for_idle_start_; + } +#endif + this->waiting_for_idle_ = should_wait; +} + +/** + * Called during the loop task. + * First defer for any pending delays, then check if we are waiting for the display to become idle. + * If not waiting for idle, process the state machine. + */ + void EPaperBase::loop() { + auto now = millis(); + if (this->delay_until_ != 0) { + // using modulus arithmetic to handle wrap-around + int diff = now - this->delay_until_; + if (diff < 0) { + return; + } + this->delay_until_ = 0; + } if (this->waiting_for_idle_) { if (this->is_idle_()) { this->waiting_for_idle_ = false; + ESP_LOGV(TAG, "Screen now idle after %u ms", (unsigned) (millis() - this->waiting_for_idle_start_)); } else { - if (App.get_loop_component_start_time() - this->waiting_for_idle_last_print_ >= 1000) { - ESP_LOGV(TAG, "Waiting for idle"); - this->waiting_for_idle_last_print_ = App.get_loop_component_start_time(); +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE + if (now - this->waiting_for_idle_last_print_ >= 1000) { + ESP_LOGV(TAG, "Waiting for idle in state %s", this->epaper_state_to_string_()); + this->waiting_for_idle_last_print_ = millis(); } +#endif return; } } + this->process_state_(); +} - auto state = this->state_queue_.front(); - - switch (state) { +/** + * Process the state machine. + * Typical state sequence: + * IDLE -> RESET -> RESET_END -> UPDATE -> INITIALISE -> TRANSFER_DATA -> POWER_ON -> REFRESH_SCREEN -> POWER_OFF -> + * DEEP_SLEEP -> IDLE + * + * Should a subclassed class need to override this, the method will need to be made virtual. + */ +void EPaperBase::process_state_() { + ESP_LOGV(TAG, "Process state entered in state %s", epaper_state_to_string_()); + switch (this->state_) { + default: + ESP_LOGD(TAG, "Display is in unhandled state %s", epaper_state_to_string_()); + this->disable_loop(); + break; case EPaperState::IDLE: this->disable_loop(); break; + case EPaperState::RESET: + case EPaperState::RESET_END: + if (this->reset_()) { + this->set_state_(EPaperState::UPDATE); + } else { + this->set_state_(EPaperState::RESET_END); + } + break; case EPaperState::UPDATE: this->do_update_(); // Calls ESPHome (current page) lambda - break; - case EPaperState::RESET: - this->reset(); + this->set_state_(EPaperState::INITIALISE); break; case EPaperState::INITIALISE: this->initialise_(); + this->set_state_(EPaperState::TRANSFER_DATA); break; case EPaperState::TRANSFER_DATA: if (!this->transfer_data()) { return; // Not done yet, come back next loop } + this->set_state_(EPaperState::POWER_ON); break; case EPaperState::POWER_ON: this->power_on(); + this->set_state_(EPaperState::REFRESH_SCREEN); break; case EPaperState::REFRESH_SCREEN: this->refresh_screen(); + this->set_state_(EPaperState::POWER_OFF); break; case EPaperState::POWER_OFF: this->power_off(); + this->set_state_(EPaperState::DEEP_SLEEP); break; case EPaperState::DEEP_SLEEP: this->deep_sleep(); + this->set_state_(EPaperState::IDLE); break; } - this->state_queue_.pop(); +} + +void EPaperBase::set_state_(EPaperState state, uint16_t delay) { + ESP_LOGV(TAG, "Exit state %s", this->epaper_state_to_string_()); + this->state_ = state; + this->wait_for_idle_(state > EPaperState::SHOULD_WAIT); + if (delay != 0) { + this->delay_until_ = millis() + delay; + } else { + this->delay_until_ = 0; + } + ESP_LOGV(TAG, "Enter state %s, delay %u, wait_for_idle=%s", this->epaper_state_to_string_(), delay, + TRUEFALSE(this->waiting_for_idle_)); } void EPaperBase::start_command_() { @@ -203,25 +241,39 @@ void EPaperBase::on_safe_shutdown() { this->deep_sleep(); } void EPaperBase::initialise_() { size_t index = 0; - const auto &sequence = this->init_sequence_; - const size_t sequence_size = this->init_sequence_length_; - while (index != sequence_size) { - if (sequence_size - index < 2) { - this->mark_failed("Malformed init sequence"); - return; - } - const auto *ptr = sequence + index; - const uint8_t length = ptr[1]; - if (sequence_size - index < length + 2) { - this->mark_failed("Malformed init sequence"); - return; - } - this->cmd_data(ptr); - index += length + 2; + auto *sequence = this->init_sequence_; + auto length = this->init_sequence_length_; + while (index != length) { + if (length - index < 2) { + this->mark_failed("Malformed init sequence"); + return; + } + const uint8_t cmd = sequence[index++]; + if (const uint8_t x = sequence[index++]; x == DELAY_FLAG) { + ESP_LOGV(TAG, "Delay %dms", cmd); + delay(cmd); + } else { + const uint8_t num_args = x & 0x7F; + if (length - index < num_args) { + ESP_LOGE(TAG, "Malformed init sequence, cmd = %X, num_args = %u", cmd, num_args); + this->mark_failed(); + return; + } + ESP_LOGV(TAG, "Command %02X, length %d", cmd, num_args); + this->cmd_data(cmd, sequence + index, num_args); + index += num_args; + } } +} - this->power_on(); +void EPaperBase::dump_config() { + LOG_DISPLAY("", "E-Paper SPI", this); + ESP_LOGCONFIG(TAG, " Model: %s", this->name_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); } } // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi.h b/esphome/components/epaper_spi/epaper_spi.h index f6b2d41c65..4745ec7339 100644 --- a/esphome/components/epaper_spi/epaper_spi.h +++ b/esphome/components/epaper_spi/epaper_spi.h @@ -8,36 +8,48 @@ #include namespace esphome::epaper_spi { +using namespace display; enum class EPaperState : uint8_t { - IDLE, - UPDATE, - RESET, - INITIALISE, - TRANSFER_DATA, - POWER_ON, - REFRESH_SCREEN, - POWER_OFF, - DEEP_SLEEP, + IDLE, // not doing anything + UPDATE, // update the buffer + RESET, // drive reset low (active) + RESET_END, // drive reset high (inactive) + + SHOULD_WAIT, // states higher than this should wait for the display to be not busy + INITIALISE, // send the init sequence + TRANSFER_DATA, // transfer data to the display + POWER_ON, // power on the display + REFRESH_SCREEN, // send refresh command + POWER_OFF, // power off the display + DEEP_SLEEP, // deep sleep the display }; -static const uint8_t MAX_TRANSFER_TIME = 10; // Transfer in 10ms blocks to allow the loop to run +static constexpr uint8_t MAX_TRANSFER_TIME = 10; // Transfer in 10ms blocks to allow the loop to run +static constexpr uint8_t DELAY_FLAG = 0xFF; -class EPaperBase : public display::DisplayBuffer, +class EPaperBase : public DisplayBuffer, public spi::SPIDevice { public: - EPaperBase(const uint8_t *init_sequence, const size_t init_sequence_length) - : init_sequence_length_(init_sequence_length), init_sequence_(init_sequence) {} + EPaperBase(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, + size_t init_sequence_length, DisplayType display_type = DISPLAY_TYPE_BINARY) + : name_(name), + width_(width), + height_(height), + init_sequence_(init_sequence), + init_sequence_length_(init_sequence_length), + display_type_(display_type) {} void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; } float get_setup_priority() const override; void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; } void set_busy_pin(GPIOPin *busy) { this->busy_pin_ = busy; } void set_reset_duration(uint32_t reset_duration) { this->reset_duration_ = reset_duration; } + void dump_config() override; void command(uint8_t value); void data(uint8_t value); - void cmd_data(const uint8_t *data); + void cmd_data(uint8_t command, const uint8_t *ptr, size_t length); void update() override; void loop() override; @@ -46,48 +58,84 @@ class EPaperBase : public display::DisplayBuffer, void on_safe_shutdown() override; + DisplayType get_display_type() override { return this->display_type_; }; + protected: - bool is_idle_(); - void setup_pins_(); - virtual void reset(); + int get_height_internal() override { return this->height_; }; + int get_width_internal() override { return this->width_; }; + void process_state_(); + + const char *epaper_state_to_string_(); + bool is_idle_() const; + void setup_pins_() const; + bool reset_() const; void initialise_(); + void wait_for_idle_(bool should_wait); bool init_buffer_(size_t buffer_length); virtual int get_width_controller() { return this->get_width_internal(); }; - virtual void deep_sleep() = 0; + + /** + * Methods that must be implemented by concrete classes to control the display + */ /** * Send data to the device via SPI - * @return true if done, false if should be called next loop + * @return true if done, false if it should be called next loop */ virtual bool transfer_data() = 0; + /** + * Refresh the screen after data transfer + */ virtual void refresh_screen() = 0; + /** + * Power the display on + */ virtual void power_on() = 0; + /** + * Power the display off + */ virtual void power_off() = 0; - virtual uint32_t get_buffer_length() = 0; + + /** + * Place the display into deep sleep + */ + virtual void deep_sleep() = 0; + + void set_state_(EPaperState state, uint16_t delay = 0); void start_command_(); void end_command_(); void start_data_(); void end_data_(); - const size_t init_sequence_length_{0}; + // properties initialised in the constructor + const char *name_; + uint16_t width_; + uint16_t height_; + const uint8_t *init_sequence_; + size_t init_sequence_length_; + DisplayType display_type_; - size_t current_data_index_{0}; + size_t buffer_length_{}; + size_t current_data_index_{0}; // used by data transfer to track progress uint32_t reset_duration_{200}; +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE + uint32_t transfer_start_time_{}; uint32_t waiting_for_idle_last_print_{0}; + uint32_t waiting_for_idle_start_{0}; +#endif - GPIOPin *dc_pin_; - GPIOPin *busy_pin_{nullptr}; - GPIOPin *reset_pin_{nullptr}; - - const uint8_t *init_sequence_{nullptr}; + GPIOPin *dc_pin_{}; + GPIOPin *busy_pin_{}; + GPIOPin *reset_pin_{}; bool waiting_for_idle_{false}; + uint32_t delay_until_{0}; split_buffer::SplitBuffer buffer_; - std::queue state_queue_{{EPaperState::IDLE}}; + EPaperState state_{EPaperState::IDLE}; }; } // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.cpp b/esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.cpp deleted file mode 100644 index f6273b392f..0000000000 --- a/esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "epaper_spi_model_7p3in_spectra_e6.h" - -namespace esphome::epaper_spi { - -static constexpr const char *const TAG = "epaper_spi.7.3in-spectra-e6"; - -void EPaper7p3InSpectraE6::power_on() { - ESP_LOGI(TAG, "Power on"); - this->command(0x04); - this->waiting_for_idle_ = true; -} - -void EPaper7p3InSpectraE6::power_off() { - ESP_LOGI(TAG, "Power off"); - this->command(0x02); - this->data(0x00); - this->waiting_for_idle_ = true; -} - -void EPaper7p3InSpectraE6::refresh_screen() { - ESP_LOGI(TAG, "Refresh"); - this->command(0x12); - this->data(0x00); - this->waiting_for_idle_ = true; -} - -void EPaper7p3InSpectraE6::deep_sleep() { - ESP_LOGI(TAG, "Deep sleep"); - this->command(0x07); - this->data(0xA5); -} - -void EPaper7p3InSpectraE6::dump_config() { - LOG_DISPLAY("", "E-Paper SPI", this); - ESP_LOGCONFIG(TAG, " Model: 7.3in Spectra E6"); - LOG_PIN(" Reset Pin: ", this->reset_pin_); - LOG_PIN(" DC Pin: ", this->dc_pin_); - LOG_PIN(" Busy Pin: ", this->busy_pin_); - LOG_UPDATE_INTERVAL(this); -} - -} // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.h b/esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.h deleted file mode 100644 index 6e850085ac..0000000000 --- a/esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "epaper_spi_spectra_e6.h" - -namespace esphome::epaper_spi { - -class EPaper7p3InSpectraE6 : public EPaperSpectraE6 { - static constexpr const uint16_t WIDTH = 800; - static constexpr const uint16_t HEIGHT = 480; - // clang-format off - - // Command, data length, data - static constexpr uint8_t INIT_SEQUENCE[] = { - 0xAA, 6, 0x49, 0x55, 0x20, 0x08, 0x09, 0x18, - 0x01, 1, 0x3F, - 0x00, 2, 0x5F, 0x69, - 0x03, 4, 0x00, 0x54, 0x00, 0x44, - 0x05, 4, 0x40, 0x1F, 0x1F, 0x2C, - 0x06, 4, 0x6F, 0x1F, 0x17, 0x49, - 0x08, 4, 0x6F, 0x1F, 0x1F, 0x22, - 0x30, 1, 0x03, - 0x50, 1, 0x3F, - 0x60, 2, 0x02, 0x00, - 0x61, 4, WIDTH / 256, WIDTH % 256, HEIGHT / 256, HEIGHT % 256, - 0x84, 1, 0x01, - 0xE3, 1, 0x2F, - }; - // clang-format on - - public: - EPaper7p3InSpectraE6() : EPaperSpectraE6(INIT_SEQUENCE, sizeof(INIT_SEQUENCE)) {} - - void dump_config() override; - - protected: - int get_width_internal() override { return WIDTH; }; - int get_height_internal() override { return HEIGHT; }; - - void refresh_screen() override; - void power_on() override; - void power_off() override; - void deep_sleep() override; -}; - -} // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp b/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp index dccc691252..8e4cbdde2a 100644 --- a/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp +++ b/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp @@ -1,135 +1,166 @@ #include "epaper_spi_spectra_e6.h" +#include + #include "esphome/core/log.h" namespace esphome::epaper_spi { - static constexpr const char *const TAG = "epaper_spi.6c"; +static constexpr size_t MAX_TRANSFER_SIZE = 128; +static constexpr unsigned char GRAY_THRESHOLD = 50; -static inline uint8_t color_to_hex(Color color) { - if (color.red > 127) { - if (color.green > 170) { - if (color.blue > 127) { - return 0x1; // White - } else { - return 0x2; // Yellow - } - } else { - return 0x3; // Red (or Magenta) - } - } else { - if (color.green > 127) { - if (color.blue > 127) { - return 0x5; // Cyan -> Blue - } else { - return 0x6; // Green - } - } else { - if (color.blue > 127) { - return 0x5; // Blue - } else { - return 0x0; // Black - } +enum E6Color { + BLACK, + WHITE, + YELLOW, + RED, + SKIP_1, + BLUE, + GREEN, + CYAN, + SKIP_2, +}; + +static uint8_t color_to_hex(Color color) { + // --- Step 1: Check for Grayscale (Black or White) --- + // We define "grayscale" as a color where the min and max components + // are close to each other. + unsigned char max_rgb = std::max({color.r, color.g, color.b}); + unsigned char min_rgb = std::min({color.r, color.g, color.b}); + + if ((max_rgb - min_rgb) < GRAY_THRESHOLD) { + // It's a shade of gray. Map to BLACK or WHITE. + // We split the luminance at the halfway point (382 = (255*3)/2) + if ((static_cast(color.r) + color.g + color.b) > 382) { + return WHITE; } + return BLACK; } + // --- Step 2: Check for Primary/Secondary Colors --- + // If it's not gray, it's a color. We check which components are + // "on" (over 128) vs "off". This divides the RGB cube into 8 corners. + bool r_on = (color.r > 128); + bool g_on = (color.g > 128); + bool b_on = (color.b > 128); + + if (r_on && g_on && !b_on) { + return YELLOW; + } + if (r_on && !g_on && !b_on) { + return RED; + } + if (!r_on && g_on && !b_on) { + return GREEN; + } + if (!r_on && !g_on && b_on) { + return BLUE; + } + // Handle "impure" colors (Cyan, Magenta) + if (!r_on && g_on && b_on) { + // Cyan (G+B) -> Closest is Green or Blue. Pick Green. + return GREEN; + } + if (r_on && !g_on) { + // Magenta (R+B) -> Closest is Red or Blue. Pick Red. + return RED; + } + // Handle the remaining corners (White-ish, Black-ish) + if (r_on) { + // All high (but not gray) -> White + return WHITE; + } + // !r_on && !g_on && !b_on + // All low (but not gray) -> Black + return BLACK; +} + +void EPaperSpectraE6::power_on() { + ESP_LOGD(TAG, "Power on"); + this->command(0x04); +} + +void EPaperSpectraE6::power_off() { + ESP_LOGD(TAG, "Power off"); + this->command(0x02); + this->data(0x00); +} + +void EPaperSpectraE6::refresh_screen() { + ESP_LOGD(TAG, "Refresh"); + this->command(0x12); + this->data(0x00); +} + +void EPaperSpectraE6::deep_sleep() { + ESP_LOGD(TAG, "Deep sleep"); + this->command(0x07); + this->data(0xA5); } void EPaperSpectraE6::fill(Color color) { - uint8_t pixel_color; - if (color.is_on()) { - pixel_color = color_to_hex(color); - } else { - pixel_color = 0x1; - } + auto pixel_color = color_to_hex(color); - // We store 8 bitset<3> in 3 bytes - // | byte 1 | byte 2 | byte 3 | - // |aaabbbaa|abbbaaab|bbaaabbb| - uint8_t byte_1 = pixel_color << 5 | pixel_color << 2 | pixel_color >> 1; - uint8_t byte_2 = pixel_color << 7 | pixel_color << 4 | pixel_color << 1 | pixel_color >> 2; - uint8_t byte_3 = pixel_color << 6 | pixel_color << 3 | pixel_color << 0; - - const size_t buffer_length = this->get_buffer_length(); - for (size_t i = 0; i < buffer_length; i += 3) { - this->buffer_[i + 0] = byte_1; - this->buffer_[i + 1] = byte_2; - this->buffer_[i + 2] = byte_3; - } + // We store 2 pixels per byte + this->buffer_.fill(pixel_color + (pixel_color << 4)); } -uint32_t EPaperSpectraE6::get_buffer_length() { - // 6 colors buffer, 1 pixel = 3 bits, we will store 8 pixels in 24 bits = 3 bytes - return this->get_width_controller() * this->get_height_internal() / 8u * 3u; +void EPaperSpectraE6::clear() { + // clear buffer to white, just like real paper. + this->fill(COLOR_ON); } void HOT EPaperSpectraE6::draw_absolute_pixel_internal(int x, int y, Color color) { - if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0) + if (x >= this->width_ || y >= this->height_ || x < 0 || y < 0) return; - uint8_t pixel_bits = color_to_hex(color); + auto pixel_bits = color_to_hex(color); uint32_t pixel_position = x + y * this->get_width_controller(); - uint32_t first_bit_position = pixel_position * 3; - uint32_t byte_position = first_bit_position / 8u; - uint32_t byte_subposition = first_bit_position % 8u; - - if (byte_subposition <= 5) { - this->buffer_[byte_position] = (this->buffer_[byte_position] & (0xFF ^ (0b111 << (5 - byte_subposition)))) | - (pixel_bits << (5 - byte_subposition)); + uint32_t byte_position = pixel_position / 2; + auto original = this->buffer_[byte_position]; + if ((pixel_position & 1) != 0) { + this->buffer_[byte_position] = (original & 0xF0) | pixel_bits; } else { - this->buffer_[byte_position] = (this->buffer_[byte_position] & (0xFF ^ (0b111 >> (byte_subposition - 5)))) | - (pixel_bits >> (byte_subposition - 5)); - - this->buffer_[byte_position + 1] = - (this->buffer_[byte_position + 1] & (0xFF ^ (0xFF & (0b111 << (13 - byte_subposition))))) | - (pixel_bits << (13 - byte_subposition)); + this->buffer_[byte_position] = (original & 0x0F) | (pixel_bits << 4); } } bool HOT EPaperSpectraE6::transfer_data() { const uint32_t start_time = App.get_loop_component_start_time(); + const size_t buffer_length = this->buffer_length_; if (this->current_data_index_ == 0) { - ESP_LOGV(TAG, "Sending data"); +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE + this->transfer_start_time_ = millis(); +#endif + ESP_LOGV(TAG, "Start sending data at %ums", (unsigned) millis()); this->command(0x10); } - uint8_t bytes_to_send[4]{0}; - const size_t buffer_length = this->get_buffer_length(); - for (size_t i = this->current_data_index_; i < buffer_length; i += 3) { - const uint32_t triplet = encode_uint24(this->buffer_[i + 0], this->buffer_[i + 1], this->buffer_[i + 2]); - // 8 pixels are stored in 3 bytes - // |aaabbbaa|abbbaaab|bbaaabbb| - // | byte 1 | byte 2 | byte 3 | - bytes_to_send[0] = ((triplet >> 17) & 0b01110000) | ((triplet >> 18) & 0b00000111); - bytes_to_send[1] = ((triplet >> 11) & 0b01110000) | ((triplet >> 12) & 0b00000111); - bytes_to_send[2] = ((triplet >> 5) & 0b01110000) | ((triplet >> 6) & 0b00000111); - bytes_to_send[3] = ((triplet << 1) & 0b01110000) | ((triplet << 0) & 0b00000111); + size_t buf_idx = 0; + uint8_t bytes_to_send[MAX_TRANSFER_SIZE]; + while (this->current_data_index_ != buffer_length) { + bytes_to_send[buf_idx++] = this->buffer_[this->current_data_index_++]; - this->start_data_(); - this->write_array(bytes_to_send, sizeof(bytes_to_send)); - this->end_data_(); + if (buf_idx == sizeof bytes_to_send) { + this->start_data_(); + this->write_array(bytes_to_send, buf_idx); + this->end_data_(); + ESP_LOGV(TAG, "Wrote %d bytes at %ums", buf_idx, (unsigned) millis()); + buf_idx = 0; - if (millis() - start_time > MAX_TRANSFER_TIME) { - // Let the main loop run and come back next loop - this->current_data_index_ = i + 3; - return false; + if (millis() - start_time > MAX_TRANSFER_TIME) { + // Let the main loop run and come back next loop + return false; + } } } // Finished the entire dataset + if (buf_idx != 0) { + this->start_data_(); + this->write_array(bytes_to_send, buf_idx); + this->end_data_(); + } this->current_data_index_ = 0; + ESP_LOGV(TAG, "Sent data in %" PRIu32 " ms", millis() - this->transfer_start_time_); return true; } - -void EPaperSpectraE6::reset() { - if (this->reset_pin_ != nullptr) { - this->disable_loop(); - this->reset_pin_->digital_write(true); - this->set_timeout(20, [this] { - this->reset_pin_->digital_write(false); - delay(2); - this->reset_pin_->digital_write(true); - this->set_timeout(20, [this] { this->enable_loop(); }); - }); - } -} - } // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi_spectra_e6.h b/esphome/components/epaper_spi/epaper_spi_spectra_e6.h index 9f0652f79d..48356ad74b 100644 --- a/esphome/components/epaper_spi/epaper_spi_spectra_e6.h +++ b/esphome/components/epaper_spi/epaper_spi_spectra_e6.h @@ -6,18 +6,23 @@ namespace esphome::epaper_spi { class EPaperSpectraE6 : public EPaperBase { public: - EPaperSpectraE6(const uint8_t *init_sequence, const size_t init_sequence_length) - : EPaperBase(init_sequence, init_sequence_length) {} + EPaperSpectraE6(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, + size_t init_sequence_length) + : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR) { + this->buffer_length_ = width * height / 2; // 2 pixels per byte + } - display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } void fill(Color color) override; + void clear() override; protected: + void refresh_screen() override; + void power_on() override; + void power_off() override; + void deep_sleep() override; void draw_absolute_pixel_internal(int x, int y, Color color) override; - uint32_t get_buffer_length() override; bool transfer_data() override; - void reset() override; }; } // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/models/__init__.py b/esphome/components/epaper_spi/models/__init__.py new file mode 100644 index 0000000000..019eb31d18 --- /dev/null +++ b/esphome/components/epaper_spi/models/__init__.py @@ -0,0 +1,65 @@ +from typing import Any, Self + +import esphome.config_validation as cv +from esphome.const import CONF_DIMENSIONS, CONF_HEIGHT, CONF_WIDTH + + +class EpaperModel: + models: dict[str, Self] = {} + + def __init__( + self, + name: str, + class_name: str, + initsequence=None, + **defaults, + ): + name = name.upper() + self.name = name + self.class_name = class_name + self.initsequence = initsequence + self.defaults = defaults + EpaperModel.models[name] = self + + def get_default(self, key, fallback: Any = False) -> Any: + return self.defaults.get(key, fallback) + + def get_init_sequence(self, config: dict): + return self.initsequence + + def option(self, name, fallback=cv.UNDEFINED) -> cv.Optional | cv.Required: + if fallback is None and self.get_default(name, None) is None: + return cv.Required(name) + return cv.Optional(name, default=self.get_default(name, fallback)) + + def get_dimensions(self, config) -> tuple[int, int]: + if CONF_DIMENSIONS in config: + # Explicit dimensions, just use as is + dimensions = config[CONF_DIMENSIONS] + if isinstance(dimensions, dict): + width = dimensions[CONF_WIDTH] + height = dimensions[CONF_HEIGHT] + else: + (width, height) = dimensions + + else: + # Default dimensions, use model defaults + width = self.get_default(CONF_WIDTH) + height = self.get_default(CONF_HEIGHT) + return width, height + + def extend(self, name, **kwargs) -> "EpaperModel": + """ + Extend the current model with additional parameters or a modified init sequence. + Parameters supplied here will override the defaults of the current model. + if the initsequence is not provided, the current model's initsequence will be used. + If add_init_sequence is provided, it will be appended to the current initsequence. + :param name: + :param kwargs: + :return: + """ + initsequence = list(kwargs.pop("initsequence", self.initsequence) or ()) + initsequence.extend(kwargs.pop("add_init_sequence", ())) + defaults = self.defaults.copy() + defaults.update(kwargs) + return self.__class__(name, initsequence=tuple(initsequence), **defaults) diff --git a/esphome/components/epaper_spi/models/spectra_e6.py b/esphome/components/epaper_spi/models/spectra_e6.py new file mode 100644 index 0000000000..9f0b673d69 --- /dev/null +++ b/esphome/components/epaper_spi/models/spectra_e6.py @@ -0,0 +1,51 @@ +from typing import Any + +from . import EpaperModel + + +class SpectraE6(EpaperModel): + def __init__(self, name, class_name="EPaperSpectraE6", **kwargs): + super().__init__(name, class_name, **kwargs) + + # fmt: off + def get_init_sequence(self, config: dict): + width, height = self.get_dimensions(config) + return ( + (0xAA, 0x49, 0x55, 0x20, 0x08, 0x09, 0x18,), + (0x01, 0x3F,), + (0x00, 0x5F, 0x69,), + (0x03, 0x00, 0x54, 0x00, 0x44,), + (0x05, 0x40, 0x1F, 0x1F, 0x2C,), + (0x06, 0x6F, 0x1F, 0x17, 0x49,), + (0x08, 0x6F, 0x1F, 0x1F, 0x22,), + (0x30, 0x03,), + (0x50, 0x3F,), + (0x60, 0x02, 0x00,), + (0x61, width // 256, width % 256, height // 256, height % 256,), + (0x84, 0x01,), + (0xE3, 0x2F,), + ) + + def get_default(self, key, fallback: Any = False) -> Any: + return self.defaults.get(key, fallback) + + +spectra_e6 = SpectraE6("spectra-e6") + +spectra_e6.extend( + "Seeed-reTerminal-E1002", + width=800, + height=480, + data_rate="20MHz", + cs_pin=10, + dc_pin=11, + reset_pin=12, + busy_pin={ + "number": 13, + "inverted": True, + "mode": { + "input": True, + "pullup": True, + }, + }, +) diff --git a/esphome/components/mipi/__init__.py b/esphome/components/mipi/__init__.py index 93d1750cd6..4dbc81caa2 100644 --- a/esphome/components/mipi/__init__.py +++ b/esphome/components/mipi/__init__.py @@ -218,6 +218,21 @@ def map_sequence(value): return tuple(value) +def flatten_sequence(sequence: tuple | list): + """ + Flatten an init sequence into a single list of bytes. + :param sequence: The list of tuples + :return: a list of bytes + """ + return sum( + tuple( + (x[1], 0xFF) if x[0] == DELAY_FLAG else (x[0], len(x) - 1) + x[1:] + for x in sequence + ), + (), + ) + + def delay(ms): return DELAY_FLAG, ms @@ -456,13 +471,7 @@ class DriverChip: # Flatten the sequence into a list of bytes, with the length of each command # or the delay flag inserted where needed - return sum( - tuple( - (x[1], 0xFF) if x[0] == DELAY_FLAG else (x[0], len(x) - 1) + x[1:] - for x in sequence - ), - (), - ), madctl + return flatten_sequence(sequence), madctl def requires_buffer(config) -> bool: diff --git a/esphome/components/split_buffer/split_buffer.cpp b/esphome/components/split_buffer/split_buffer.cpp index a710670a5d..526a19c71c 100644 --- a/esphome/components/split_buffer/split_buffer.cpp +++ b/esphome/components/split_buffer/split_buffer.cpp @@ -4,7 +4,6 @@ #include "esphome/core/log.h" namespace esphome::split_buffer { - static constexpr const char *const TAG = "split_buffer"; SplitBuffer::~SplitBuffer() { this->free(); } @@ -102,32 +101,44 @@ void SplitBuffer::free() { this->total_length_ = 0; } -uint8_t &SplitBuffer::operator[](size_t index) { +const uint8_t &SplitBuffer::operator[](size_t index) const { if (index >= this->total_length_) { ESP_LOGE(TAG, "Out of bounds - %zu >= %zu", index, this->total_length_); - // Return reference to a static dummy byte to avoid crash + // Return reference to a static dummy byte since we can't throw exceptions. + // the byte is non-const since it will also be used by the non-const [] overload. static uint8_t dummy = 0; return dummy; } - size_t buffer_index = index / this->buffer_size_; - size_t offset_in_buffer = index - this->buffer_size_ * buffer_index; + const auto buffer_index = index / this->buffer_size_; + const auto offset_in_buffer = index % this->buffer_size_; return this->buffers_[buffer_index][offset_in_buffer]; } -const uint8_t &SplitBuffer::operator[](size_t index) const { - if (index >= this->total_length_) { - ESP_LOGE(TAG, "Out of bounds - %zu >= %zu", index, this->total_length_); - // Return reference to a static dummy byte to avoid crash - static const uint8_t DUMMY = 0; - return DUMMY; +// non-const version of operator[] for write access +uint8_t &SplitBuffer::operator[](size_t index) { + // avoid code duplication. These casts are safe since we know the object is not const. + return const_cast(static_cast(this)->operator[](index)); +} + +/** + * Fill the entire buffer with a single byte value + * @param value Fill value + */ +void SplitBuffer::fill(uint8_t value) const { + if (this->buffer_count_ == 0) + return; + // clear all the full sized buffers + size_t i = 0; + for (; i != this->buffer_count_ - 1; i++) { + memset(this->buffers_[i], value, this->buffer_size_); } - - size_t buffer_index = index / this->buffer_size_; - size_t offset_in_buffer = index - this->buffer_size_ * buffer_index; - - return this->buffers_[buffer_index][offset_in_buffer]; + // clear the last, potentially short, buffer. + // `i` is guaranteed to equal the last index since the loop terminates at that value. + // where all buffers are the same size, the modulus must return the size, not 0. + auto size_last = ((this->total_length_ - 1) % this->buffer_size_) + 1; + memset(this->buffers_[i], value, size_last); } } // namespace esphome::split_buffer diff --git a/esphome/components/split_buffer/split_buffer.h b/esphome/components/split_buffer/split_buffer.h index c3490f3d6e..b615ddce74 100644 --- a/esphome/components/split_buffer/split_buffer.h +++ b/esphome/components/split_buffer/split_buffer.h @@ -4,7 +4,13 @@ #include namespace esphome::split_buffer { - +/** + * A SplitBuffer allocates a large memory buffer potentially as multiple smaller buffers + * to facilitate allocation of large buffers on devices with fragmented memory spaces. + * Each sub-buffer is the same size, except for the last one which may be smaller. + * Standard array indexing using `[]` is possible on the buffer, but, since the buffer may not be contiguous in memory, + * there is no easy way to access the buffer as a single array, i.e. no `.data()` access like a vector. + */ class SplitBuffer { public: SplitBuffer() = default; @@ -19,13 +25,13 @@ class SplitBuffer { // Access operators uint8_t &operator[](size_t index); const uint8_t &operator[](size_t index) const; + void fill(uint8_t value) const; // Get the total length size_t size() const { return this->total_length_; } // Get buffer information size_t get_buffer_count() const { return this->buffer_count_; } - size_t get_buffer_size() const { return this->buffer_size_; } // Check if successfully initialized bool is_valid() const { return this->buffers_ != nullptr && this->buffer_count_ > 0; } diff --git a/tests/components/epaper_spi/test.esp32-s3-idf.yaml b/tests/components/epaper_spi/test.esp32-s3-idf.yaml index 34aefb82b4..cff1f51897 100644 --- a/tests/components/epaper_spi/test.esp32-s3-idf.yaml +++ b/tests/components/epaper_spi/test.esp32-s3-idf.yaml @@ -4,7 +4,10 @@ packages: display: - platform: epaper_spi spi_id: spi_bus - model: 7.3in-spectra-e6 + model: spectra-e6 + dimensions: + width: 800 + height: 480 cs_pin: GPIO5 dc_pin: GPIO17 reset_pin: GPIO16 @@ -13,3 +16,6 @@ display: update_interval: 60s lambda: |- it.circle(64, 64, 50, Color::BLACK); + + - platform: epaper_spi + model: seeed-reterminal-e1002 From 99ce989eaedd59fdad9ea28a0468d63d31568d95 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Nov 2025 16:30:35 -0600 Subject: [PATCH 368/526] [micro_wake_word] Add wake_loop_threadsafe() for low-latency wake word detection (#11698) --- esphome/components/micro_wake_word/__init__.py | 7 ++++++- esphome/components/micro_wake_word/micro_wake_word.cpp | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py index 8cd7115368..575fb97799 100644 --- a/esphome/components/micro_wake_word/__init__.py +++ b/esphome/components/micro_wake_word/__init__.py @@ -7,7 +7,7 @@ from urllib.parse import urljoin from esphome import automation, external_files, git from esphome.automation import register_action, register_condition import esphome.codegen as cg -from esphome.components import esp32, microphone +from esphome.components import esp32, microphone, socket import esphome.config_validation as cv from esphome.const import ( CONF_FILE, @@ -32,6 +32,7 @@ _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@kahrendt", "@jesserockz"] DEPENDENCIES = ["microphone"] +AUTO_LOAD = ["socket"] DOMAIN = "micro_wake_word" @@ -443,6 +444,10 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) + # Enable wake_loop_threadsafe() for low-latency wake word detection + # The inference task queues detection events that need immediate processing + socket.require_wake_loop_threadsafe() + mic_source = await microphone.microphone_source_to_code(config[CONF_MICROPHONE]) cg.add(var.set_microphone_source(mic_source)) diff --git a/esphome/components/micro_wake_word/micro_wake_word.cpp b/esphome/components/micro_wake_word/micro_wake_word.cpp index 6fca48a5bd..a0547b158e 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.cpp +++ b/esphome/components/micro_wake_word/micro_wake_word.cpp @@ -2,6 +2,7 @@ #ifdef USE_ESP_IDF +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" @@ -426,6 +427,12 @@ void MicroWakeWord::process_probabilities_() { if (vad_state.detected) { #endif xQueueSend(this->detection_queue_, &wake_word_state, portMAX_DELAY); + + // Wake main loop immediately to process wake word detection +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + App.wake_loop_threadsafe(); +#endif + model->reset_probabilities(); #ifdef USE_MICRO_WAKE_WORD_VAD } else { From 99d1a9cf6ef89fae36d749d3f640891ad1265471 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 4 Nov 2025 09:23:45 +1000 Subject: [PATCH 369/526] [usb_uart] Fixes for transfer queue allocation (#11548) --- esphome/components/usb_host/usb_host.h | 12 ++--- .../components/usb_host/usb_host_client.cpp | 54 +++++++++---------- esphome/components/usb_uart/usb_uart.cpp | 22 +++++--- tests/components/usb_uart/common.yaml | 3 ++ 4 files changed, 51 insertions(+), 40 deletions(-) diff --git a/esphome/components/usb_host/usb_host.h b/esphome/components/usb_host/usb_host.h index 43b24a54a5..31bdde2df8 100644 --- a/esphome/components/usb_host/usb_host.h +++ b/esphome/components/usb_host/usb_host.h @@ -55,7 +55,7 @@ static const uint8_t USB_DIR_IN = 1 << 7; static const uint8_t USB_DIR_OUT = 0; static const size_t SETUP_PACKET_SIZE = 8; -static const size_t MAX_REQUESTS = USB_HOST_MAX_REQUESTS; // maximum number of outstanding requests possible. +static constexpr size_t MAX_REQUESTS = USB_HOST_MAX_REQUESTS; // maximum number of outstanding requests possible. static_assert(MAX_REQUESTS >= 1 && MAX_REQUESTS <= 32, "MAX_REQUESTS must be between 1 and 32"); // Select appropriate bitmask type for tracking allocation of TransferRequest slots. @@ -65,6 +65,7 @@ static_assert(MAX_REQUESTS >= 1 && MAX_REQUESTS <= 32, "MAX_REQUESTS must be bet // This is tied to the static_assert above, which enforces MAX_REQUESTS is between 1 and 32. // If MAX_REQUESTS is increased above 32, this logic and the static_assert must be updated. using trq_bitmask_t = std::conditional<(MAX_REQUESTS <= 16), uint16_t, uint32_t>::type; +static constexpr trq_bitmask_t ALL_REQUESTS_IN_USE = MAX_REQUESTS == 32 ? ~0 : (1 << MAX_REQUESTS) - 1; static constexpr size_t USB_EVENT_QUEUE_SIZE = 32; // Size of event queue between USB task and main loop static constexpr size_t USB_TASK_STACK_SIZE = 4096; // Stack size for USB task (same as ESP-IDF USB examples) @@ -133,11 +134,11 @@ class USBClient : public Component { float get_setup_priority() const override { return setup_priority::IO; } void on_opened(uint8_t addr); void on_removed(usb_device_handle_t handle); - void control_transfer_callback(const usb_transfer_t *xfer) const; - void transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length); - void transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length); + bool transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length); + bool transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length); void dump_config() override; void release_trq(TransferRequest *trq); + trq_bitmask_t get_trq_in_use() const { return trq_in_use_; } bool control_transfer(uint8_t type, uint8_t request, uint16_t value, uint16_t index, const transfer_cb_t &callback, const std::vector &data = {}); @@ -147,7 +148,6 @@ class USBClient : public Component { EventPool event_pool; protected: - bool register_(); TransferRequest *get_trq_(); // Lock-free allocation using atomic bitmask (multi-consumer safe) virtual void disconnect(); virtual void on_connected() {} @@ -158,7 +158,7 @@ class USBClient : public Component { // USB task management static void usb_task_fn(void *arg); - void usb_task_loop(); + [[noreturn]] void usb_task_loop() const; TaskHandle_t usb_task_handle_{nullptr}; diff --git a/esphome/components/usb_host/usb_host_client.cpp b/esphome/components/usb_host/usb_host_client.cpp index 0dda36b9d7..4c09cf8a49 100644 --- a/esphome/components/usb_host/usb_host_client.cpp +++ b/esphome/components/usb_host/usb_host_client.cpp @@ -194,9 +194,9 @@ void USBClient::setup() { } // Pre-allocate USB transfer buffers for all slots at startup // This avoids any dynamic allocation during runtime - for (size_t i = 0; i < MAX_REQUESTS; i++) { - usb_host_transfer_alloc(64, 0, &this->requests_[i].transfer); - this->requests_[i].client = this; // Set once, never changes + for (auto &request : this->requests_) { + usb_host_transfer_alloc(64, 0, &request.transfer); + request.client = this; // Set once, never changes } // Create and start USB task @@ -216,8 +216,7 @@ void USBClient::usb_task_fn(void *arg) { auto *client = static_cast(arg); client->usb_task_loop(); } - -void USBClient::usb_task_loop() { +void USBClient::usb_task_loop() const { while (true) { usb_host_client_handle_events(this->handle_, portMAX_DELAY); } @@ -340,22 +339,23 @@ static void control_callback(const usb_transfer_t *xfer) { // This multi-threaded access is intentional for performance - USB task can // immediately restart transfers without waiting for main loop scheduling. TransferRequest *USBClient::get_trq_() { - trq_bitmask_t mask = this->trq_in_use_.load(std::memory_order_relaxed); + trq_bitmask_t mask = this->trq_in_use_.load(std::memory_order_acquire); // Find first available slot (bit = 0) and try to claim it atomically // We use a while loop to allow retrying the same slot after CAS failure - size_t i = 0; - while (i != MAX_REQUESTS) { - if (mask & (static_cast(1) << i)) { - // Slot is in use, move to next slot - i++; - continue; + for (;;) { + if (mask == ALL_REQUESTS_IN_USE) { + ESP_LOGE(TAG, "All %zu transfer slots in use", MAX_REQUESTS); + return nullptr; } + // find the least significant zero bit + trq_bitmask_t lsb = ~mask & (mask + 1); // Slot i appears available, try to claim it atomically - trq_bitmask_t desired = mask | (static_cast(1) << i); // Set bit i to mark as in-use + trq_bitmask_t desired = mask | lsb; - if (this->trq_in_use_.compare_exchange_weak(mask, desired, std::memory_order_acquire, std::memory_order_relaxed)) { + if (this->trq_in_use_.compare_exchange_weak(mask, desired, std::memory_order::acquire)) { + auto i = __builtin_ctz(lsb); // count trailing zeroes // Successfully claimed slot i - prepare the TransferRequest auto *trq = &this->requests_[i]; trq->transfer->context = trq; @@ -364,13 +364,9 @@ TransferRequest *USBClient::get_trq_() { } // CAS failed - another thread modified the bitmask // mask was already updated by compare_exchange_weak with the current value - // No need to reload - the CAS already did that for us - i = 0; } - - ESP_LOGE(TAG, "All %zu transfer slots in use", MAX_REQUESTS); - return nullptr; } + void USBClient::disconnect() { this->on_disconnected(); auto err = usb_host_device_close(this->handle_, this->device_handle_); @@ -452,11 +448,11 @@ static void transfer_callback(usb_transfer_t *xfer) { * * @throws None. */ -void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length) { +bool USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length) { auto *trq = this->get_trq_(); if (trq == nullptr) { ESP_LOGE(TAG, "Too many requests queued"); - return; + return false; } trq->callback = callback; trq->transfer->callback = transfer_callback; @@ -466,7 +462,9 @@ void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, u if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to submit transfer, address=%x, length=%d, err=%x", ep_address, length, err); this->release_trq(trq); + return false; } + return true; } /** @@ -482,11 +480,11 @@ void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, u * * @throws None. */ -void USBClient::transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length) { +bool USBClient::transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length) { auto *trq = this->get_trq_(); if (trq == nullptr) { ESP_LOGE(TAG, "Too many requests queued"); - return; + return false; } trq->callback = callback; trq->transfer->callback = transfer_callback; @@ -497,7 +495,9 @@ void USBClient::transfer_out(uint8_t ep_address, const transfer_cb_t &callback, if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to submit transfer, address=%x, length=%d, err=%x", ep_address, length, err); this->release_trq(trq); + return false; } + return true; } void USBClient::dump_config() { ESP_LOGCONFIG(TAG, @@ -511,7 +511,7 @@ void USBClient::dump_config() { // - Main loop: When transfer submission fails // // THREAD SAFETY: Lock-free using atomic AND to clear bit -// Thread-safe atomic operation allows multi-threaded deallocation +// Thread-safe atomic operation allows multithreaded deallocation void USBClient::release_trq(TransferRequest *trq) { if (trq == nullptr) return; @@ -523,10 +523,10 @@ void USBClient::release_trq(TransferRequest *trq) { return; } - // Atomically clear bit i to mark slot as available + // Atomically clear the bit to mark slot as available // fetch_and with inverted bitmask clears the bit atomically - trq_bitmask_t bit = static_cast(1) << index; - this->trq_in_use_.fetch_and(static_cast(~bit), std::memory_order_release); + trq_bitmask_t mask = ~(static_cast(1) << index); + this->trq_in_use_.fetch_and(mask, std::memory_order_release); } } // namespace usb_host diff --git a/esphome/components/usb_uart/usb_uart.cpp b/esphome/components/usb_uart/usb_uart.cpp index 29003e071e..c24fffb11d 100644 --- a/esphome/components/usb_uart/usb_uart.cpp +++ b/esphome/components/usb_uart/usb_uart.cpp @@ -214,7 +214,7 @@ void USBUartComponent::dump_config() { } } void USBUartComponent::start_input(USBUartChannel *channel) { - if (!channel->initialised_.load() || channel->input_started_.load()) + if (!channel->initialised_.load()) return; // THREAD CONTEXT: Called from both USB task and main loop threads // - USB task: Immediate restart after successful transfer for continuous data flow @@ -226,12 +226,18 @@ void USBUartComponent::start_input(USBUartChannel *channel) { // // The underlying transfer_in() uses lock-free atomic allocation from the // TransferRequest pool, making this multi-threaded access safe + + // if already started, don't restart. A spurious failure in compare_exchange_weak + // is not a problem, as it will be retried on the next read_array() + auto started = false; + if (!channel->input_started_.compare_exchange_weak(started, true)) + return; const auto *ep = channel->cdc_dev_.in_ep; // CALLBACK CONTEXT: This lambda is executed in USB task via transfer_callback auto callback = [this, channel](const usb_host::TransferStatus &status) { ESP_LOGV(TAG, "Transfer result: length: %u; status %X", status.data_len, status.error_code); if (!status.success) { - ESP_LOGE(TAG, "Control transfer failed, status=%s", esp_err_to_name(status.error_code)); + ESP_LOGE(TAG, "Input transfer failed, status=%s", esp_err_to_name(status.error_code)); // On failure, don't restart - let next read_array() trigger it channel->input_started_.store(false); return; @@ -263,8 +269,9 @@ void USBUartComponent::start_input(USBUartChannel *channel) { channel->input_started_.store(false); this->start_input(channel); }; - channel->input_started_.store(true); - this->transfer_in(ep->bEndpointAddress, callback, ep->wMaxPacketSize); + if (!this->transfer_in(ep->bEndpointAddress, callback, ep->wMaxPacketSize)) { + channel->input_started_.store(false); + } } void USBUartComponent::start_output(USBUartChannel *channel) { @@ -357,11 +364,12 @@ void USBUartTypeCdcAcm::on_disconnected() { usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress); } usb_host_interface_release(this->handle_, this->device_handle_, channel->cdc_dev_.bulk_interface_number); - channel->initialised_.store(false); - channel->input_started_.store(false); - channel->output_started_.store(false); + // Reset the input and output started flags to their initial state to avoid the possibility of spurious restarts + channel->input_started_.store(true); + channel->output_started_.store(true); channel->input_buffer_.clear(); channel->output_buffer_.clear(); + channel->initialised_.store(false); } USBClient::on_disconnected(); } diff --git a/tests/components/usb_uart/common.yaml b/tests/components/usb_uart/common.yaml index 46ad6291f9..474c3f5c8d 100644 --- a/tests/components/usb_uart/common.yaml +++ b/tests/components/usb_uart/common.yaml @@ -1,3 +1,6 @@ +usb_host: + max_transfer_requests: 32 + usb_uart: - id: uart_0 type: cdc_acm From 266e4ae91fb2414528163109e708af6246b57952 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 3 Nov 2025 17:30:37 -0600 Subject: [PATCH 370/526] [helpers] Add `get_mac_address_into_buffer()` (#11700) --- esphome/core/helpers.cpp | 6 ++++++ esphome/core/helpers.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index fb8b220b2f..568acb9f1b 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -643,6 +643,12 @@ std::string get_mac_address_pretty() { return format_mac_address_pretty(mac); } +void get_mac_address_into_buffer(std::span buf) { + uint8_t mac[6]; + get_mac_address_raw(mac); + format_mac_addr_lower_no_sep(mac, buf.data()); +} + #ifndef USE_ESP32 bool has_custom_mac_address() { return false; } #endif diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index cf21ddc16d..91ddc70afa 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -1027,6 +1028,10 @@ std::string get_mac_address(); /// Get the device MAC address as a string, in colon-separated uppercase hex notation. std::string get_mac_address_pretty(); +/// Get the device MAC address into the given buffer, in lowercase hex notation. +/// Assumes buffer length is 13 (12 digits for hexadecimal representation followed by null terminator). +void get_mac_address_into_buffer(std::span buf); + #ifdef USE_ESP32 /// Set the MAC address to use from the provided byte array (6 bytes). void set_mac_address(uint8_t *mac); From 59326f137ed7c6a932bb63f38a10fe4fa7baaf9b Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 3 Nov 2025 18:29:30 -0600 Subject: [PATCH 371/526] [tinyusb] New component (#11678) --- CODEOWNERS | 1 + esphome/components/tinyusb/__init__.py | 60 ++++++++++++++++ .../components/tinyusb/tinyusb_component.cpp | 44 ++++++++++++ .../components/tinyusb/tinyusb_component.h | 72 +++++++++++++++++++ esphome/idf_component.yml | 4 ++ tests/components/tinyusb/common.yaml | 8 +++ .../components/tinyusb/test.esp32-p4-idf.yaml | 1 + .../components/tinyusb/test.esp32-s2-idf.yaml | 1 + .../components/tinyusb/test.esp32-s3-idf.yaml | 1 + 9 files changed, 192 insertions(+) create mode 100644 esphome/components/tinyusb/__init__.py create mode 100644 esphome/components/tinyusb/tinyusb_component.cpp create mode 100644 esphome/components/tinyusb/tinyusb_component.h create mode 100644 tests/components/tinyusb/common.yaml create mode 100644 tests/components/tinyusb/test.esp32-p4-idf.yaml create mode 100644 tests/components/tinyusb/test.esp32-s2-idf.yaml create mode 100644 tests/components/tinyusb/test.esp32-s3-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index fee0e98f46..b8a4df6a85 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -480,6 +480,7 @@ esphome/components/template/fan/* @ssieb esphome/components/text/* @mauritskorse esphome/components/thermostat/* @kbx81 esphome/components/time/* @esphome/core +esphome/components/tinyusb/* @kbx81 esphome/components/tlc5947/* @rnauber esphome/components/tlc5971/* @IJIJI esphome/components/tm1621/* @Philippe12 diff --git a/esphome/components/tinyusb/__init__.py b/esphome/components/tinyusb/__init__.py new file mode 100644 index 0000000000..72afc18387 --- /dev/null +++ b/esphome/components/tinyusb/__init__.py @@ -0,0 +1,60 @@ +import esphome.codegen as cg +from esphome.components import esp32 +from esphome.components.esp32 import add_idf_component, add_idf_sdkconfig_option +from esphome.components.esp32.const import ( + VARIANT_ESP32P4, + VARIANT_ESP32S2, + VARIANT_ESP32S3, +) +import esphome.config_validation as cv +from esphome.const import CONF_ID + +CODEOWNERS = ["@kbx81"] +CONFLICTS_WITH = ["usb_host"] + +CONF_USB_LANG_ID = "usb_lang_id" +CONF_USB_MANUFACTURER_STR = "usb_manufacturer_str" +CONF_USB_PRODUCT_ID = "usb_product_id" +CONF_USB_PRODUCT_STR = "usb_product_str" +CONF_USB_SERIAL_STR = "usb_serial_str" +CONF_USB_VENDOR_ID = "usb_vendor_id" + +tinyusb_ns = cg.esphome_ns.namespace("tinyusb") +TinyUSB = tinyusb_ns.class_("TinyUSB", cg.Component) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TinyUSB), + cv.Optional(CONF_USB_PRODUCT_ID, default=0x4001): cv.uint16_t, + cv.Optional(CONF_USB_VENDOR_ID, default=0x303A): cv.uint16_t, + cv.Optional(CONF_USB_LANG_ID, default=0x0409): cv.uint16_t, + cv.Optional(CONF_USB_MANUFACTURER_STR, default="ESPHome"): cv.string, + cv.Optional(CONF_USB_PRODUCT_STR, default="ESPHome"): cv.string, + cv.Optional(CONF_USB_SERIAL_STR, default=""): cv.string, + } + ).extend(cv.COMPONENT_SCHEMA), + esp32.only_on_variant( + supported=[VARIANT_ESP32P4, VARIANT_ESP32S2, VARIANT_ESP32S3], + ), +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + # Set USB device descriptor properties + cg.add(var.set_usb_desc_product_id(config[CONF_USB_PRODUCT_ID])) + cg.add(var.set_usb_desc_vendor_id(config[CONF_USB_VENDOR_ID])) + cg.add(var.set_usb_desc_lang_id(config[CONF_USB_LANG_ID])) + cg.add(var.set_usb_desc_manufacturer(config[CONF_USB_MANUFACTURER_STR])) + cg.add(var.set_usb_desc_product(config[CONF_USB_PRODUCT_STR])) + if config[CONF_USB_SERIAL_STR]: + cg.add(var.set_usb_desc_serial(config[CONF_USB_SERIAL_STR])) + + add_idf_component(name="espressif/esp_tinyusb", ref="1.7.6~1") + + add_idf_sdkconfig_option("CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID", False) + add_idf_sdkconfig_option("CONFIG_TINYUSB_DESC_USE_DEFAULT_PID", False) + add_idf_sdkconfig_option("CONFIG_TINYUSB_DESC_BCD_DEVICE", 0x0100) diff --git a/esphome/components/tinyusb/tinyusb_component.cpp b/esphome/components/tinyusb/tinyusb_component.cpp new file mode 100644 index 0000000000..a2057c90ce --- /dev/null +++ b/esphome/components/tinyusb/tinyusb_component.cpp @@ -0,0 +1,44 @@ +#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#include "tinyusb_component.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome::tinyusb { + +static const char *TAG = "tinyusb"; + +void TinyUSB::setup() { + // Use the device's MAC address as its serial number if no serial number is defined + if (this->string_descriptor_[SERIAL_NUMBER] == nullptr) { + static char mac_addr_buf[13]; + get_mac_address_into_buffer(mac_addr_buf); + this->string_descriptor_[SERIAL_NUMBER] = mac_addr_buf; + } + + this->tusb_cfg_ = { + .descriptor = &this->usb_descriptor_, + .string_descriptor = this->string_descriptor_, + .string_descriptor_count = SIZE, + .external_phy = false, + }; + + esp_err_t result = tinyusb_driver_install(&this->tusb_cfg_); + if (result != ESP_OK) { + this->mark_failed(); + } +} + +void TinyUSB::dump_config() { + ESP_LOGCONFIG(TAG, + "TinyUSB:\n" + " Product ID: 0x%04X\n" + " Vendor ID: 0x%04X\n" + " Manufacturer: '%s'\n" + " Product: '%s'\n" + " Serial: '%s'\n", + this->usb_descriptor_.idProduct, this->usb_descriptor_.idVendor, this->string_descriptor_[MANUFACTURER], + this->string_descriptor_[PRODUCT], this->string_descriptor_[SERIAL_NUMBER]); +} + +} // namespace esphome::tinyusb +#endif diff --git a/esphome/components/tinyusb/tinyusb_component.h b/esphome/components/tinyusb/tinyusb_component.h new file mode 100644 index 0000000000..56c286f455 --- /dev/null +++ b/esphome/components/tinyusb/tinyusb_component.h @@ -0,0 +1,72 @@ +#pragma once +#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#include "esphome/core/component.h" + +#include "tinyusb.h" +#include "tusb.h" + +namespace esphome::tinyusb { + +enum USBDStringDescriptor : uint8_t { + LANGUAGE_ID = 0, + MANUFACTURER = 1, + PRODUCT = 2, + SERIAL_NUMBER = 3, + INTERFACE = 4, + TERMINATOR = 5, + SIZE = 6, +}; + +static const char *DEFAULT_USB_STR = "ESPHome"; + +class TinyUSB : public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void set_usb_desc_product_id(uint16_t product_id) { this->usb_descriptor_.idProduct = product_id; } + void set_usb_desc_vendor_id(uint16_t vendor_id) { this->usb_descriptor_.idVendor = vendor_id; } + void set_usb_desc_lang_id(uint16_t lang_id) { + this->usb_desc_lang_id_[0] = lang_id & 0xFF; + this->usb_desc_lang_id_[1] = lang_id >> 8; + } + void set_usb_desc_manufacturer(const char *usb_desc_manufacturer) { + this->string_descriptor_[MANUFACTURER] = usb_desc_manufacturer; + } + void set_usb_desc_product(const char *usb_desc_product) { this->string_descriptor_[PRODUCT] = usb_desc_product; } + void set_usb_desc_serial(const char *usb_desc_serial) { this->string_descriptor_[SERIAL_NUMBER] = usb_desc_serial; } + + protected: + char usb_desc_lang_id_[2] = {0x09, 0x04}; // defaults to english + + const char *string_descriptor_[SIZE] = { + this->usb_desc_lang_id_, // 0: supported language is English (0x0409) + DEFAULT_USB_STR, // 1: Manufacturer + DEFAULT_USB_STR, // 2: Product + nullptr, // 3: Serial Number + nullptr, // 4: Interface + nullptr, // 5: Terminator + }; + + tinyusb_config_t tusb_cfg_{}; + tusb_desc_device_t usb_descriptor_{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = 0x303A, + .idProduct = 0x4001, + .bcdDevice = CONFIG_TINYUSB_DESC_BCD_DEVICE, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, + }; +}; + +} // namespace esphome::tinyusb +#endif diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index 31112caf0a..fcb3a4f438 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -23,3 +23,7 @@ dependencies: version: "2.0.0" rules: - if: "target in [esp32, esp32p4]" + espressif/esp_tinyusb: + version: "1.7.6~1" + rules: + - if: "target in [esp32s2, esp32s3, esp32p4]" diff --git a/tests/components/tinyusb/common.yaml b/tests/components/tinyusb/common.yaml new file mode 100644 index 0000000000..cb3f48836a --- /dev/null +++ b/tests/components/tinyusb/common.yaml @@ -0,0 +1,8 @@ +tinyusb: + id: tinyusb_test + usb_lang_id: 0x0123 + usb_manufacturer_str: ESPHomeTestManufacturer + usb_product_id: 0x1234 + usb_product_str: ESPHomeTestProduct + usb_serial_str: ESPHomeTestSerialNumber + usb_vendor_id: 0x2345 diff --git a/tests/components/tinyusb/test.esp32-p4-idf.yaml b/tests/components/tinyusb/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/tinyusb/test.esp32-p4-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/tinyusb/test.esp32-s2-idf.yaml b/tests/components/tinyusb/test.esp32-s2-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/tinyusb/test.esp32-s2-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/tinyusb/test.esp32-s3-idf.yaml b/tests/components/tinyusb/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/tinyusb/test.esp32-s3-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 6220084fe684f357c99f4261832f09c8cf7a76c9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Nov 2025 19:23:04 -0600 Subject: [PATCH 372/526] [ci] Fix memory impact analysis to filter incompatible platform components (#11706) --- script/determine-jobs.py | 33 ++++++++- tests/script/test_determine_jobs.py | 108 ++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 4a0edebb0d..6f908b7150 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -94,6 +94,22 @@ class Platform(StrEnum): MEMORY_IMPACT_FALLBACK_COMPONENT = "api" # Representative component for core changes MEMORY_IMPACT_FALLBACK_PLATFORM = Platform.ESP32_IDF # Most representative platform +# Platform-specific components that can only be built on their respective platforms +# These components contain platform-specific code and cannot be cross-compiled +# Regular components (wifi, logger, api, etc.) are cross-platform and not listed here +PLATFORM_SPECIFIC_COMPONENTS = frozenset( + { + "esp32", # ESP32 platform implementation + "esp8266", # ESP8266 platform implementation + "rp2040", # Raspberry Pi Pico / RP2040 platform implementation + "bk72xx", # Beken BK72xx platform implementation (uses LibreTiny) + "rtl87xx", # Realtek RTL87xx platform implementation (uses LibreTiny) + "ln882x", # Winner Micro LN882x platform implementation (uses LibreTiny) + "host", # Host platform (for testing on development machine) + "nrf52", # Nordic nRF52 platform implementation + } +) + # Platform preference order for memory impact analysis # This order is used when no platform-specific hints are detected from filenames # Priority rationale: @@ -568,6 +584,20 @@ def detect_memory_impact_config( ) platform = _select_platform_by_count(platform_counts) + # Filter out platform-specific components that are incompatible with selected platform + # Platform components (esp32, esp8266, rp2040, etc.) can only build on their own platform + # Other components (wifi, logger, etc.) are cross-platform and can build anywhere + compatible_components = [ + component + for component in components_with_tests + if component not in PLATFORM_SPECIFIC_COMPONENTS + or platform in component_platforms_map.get(component, set()) + ] + + # If no components are compatible with the selected platform, don't run + if not compatible_components: + return {"should_run": "false"} + # Debug output print("Memory impact analysis:", file=sys.stderr) print(f" Changed components: {sorted(changed_component_set)}", file=sys.stderr) @@ -579,10 +609,11 @@ def detect_memory_impact_config( print(f" Platform hints from filenames: {platform_hints}", file=sys.stderr) print(f" Common platforms: {sorted(common_platforms)}", file=sys.stderr) print(f" Selected platform: {platform}", file=sys.stderr) + print(f" Compatible components: {compatible_components}", file=sys.stderr) return { "should_run": "true", - "components": components_with_tests, + "components": compatible_components, "platform": platform, "use_merged_config": "true", } diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index e73c134151..a33eca5b19 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -1130,3 +1130,111 @@ def test_main_core_files_changed_still_detects_components( assert "select" in output["changed_components"] assert "api" in output["changed_components"] assert len(output["changed_components"]) > 0 + + +def test_detect_memory_impact_config_filters_incompatible_esp32_on_esp8266( + tmp_path: Path, +) -> None: + """Test that ESP32 components are filtered out when ESP8266 platform is selected. + + This test verifies the fix for the issue where ESP32 components were being included + when ESP8266 was selected as the platform, causing build failures in PR 10387. + """ + # Create test directory structure + tests_dir = tmp_path / "tests" / "components" + + # esp32 component only has esp32-idf tests (NOT compatible with esp8266) + esp32_dir = tests_dir / "esp32" + esp32_dir.mkdir(parents=True) + (esp32_dir / "test.esp32-idf.yaml").write_text("test: esp32") + (esp32_dir / "test.esp32-s3-idf.yaml").write_text("test: esp32") + + # esp8266 component only has esp8266-ard test (NOT compatible with esp32) + esp8266_dir = tests_dir / "esp8266" + esp8266_dir.mkdir(parents=True) + (esp8266_dir / "test.esp8266-ard.yaml").write_text("test: esp8266") + + # Mock changed_files to return both esp32 and esp8266 component changes + # Include esp8266-specific filename to trigger esp8266 platform hint + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + ): + mock_changed_files.return_value = [ + "tests/components/esp32/common.yaml", + "tests/components/esp8266/test.esp8266-ard.yaml", + "esphome/core/helpers_esp8266.h", # ESP8266-specific file to hint platform + ] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + # Memory impact should run + assert result["should_run"] == "true" + + # Platform should be esp8266-ard (due to ESP8266 filename hint) + assert result["platform"] == "esp8266-ard" + + # CRITICAL: Only esp8266 component should be included, not esp32 + # This prevents trying to build ESP32 components on ESP8266 platform + assert result["components"] == ["esp8266"], ( + "When esp8266-ard platform is selected, only esp8266 component should be included, " + "not esp32. This prevents trying to build ESP32 components on ESP8266 platform." + ) + + assert result["use_merged_config"] == "true" + + +def test_detect_memory_impact_config_filters_incompatible_esp8266_on_esp32( + tmp_path: Path, +) -> None: + """Test that ESP8266 components are filtered out when ESP32 platform is selected. + + This is the inverse of the ESP8266 test - ensures filtering works both ways. + """ + # Create test directory structure + tests_dir = tmp_path / "tests" / "components" + + # esp32 component only has esp32-idf tests (NOT compatible with esp8266) + esp32_dir = tests_dir / "esp32" + esp32_dir.mkdir(parents=True) + (esp32_dir / "test.esp32-idf.yaml").write_text("test: esp32") + (esp32_dir / "test.esp32-s3-idf.yaml").write_text("test: esp32") + + # esp8266 component only has esp8266-ard test (NOT compatible with esp32) + esp8266_dir = tests_dir / "esp8266" + esp8266_dir.mkdir(parents=True) + (esp8266_dir / "test.esp8266-ard.yaml").write_text("test: esp8266") + + # Mock changed_files to return both esp32 and esp8266 component changes + # Include MORE esp32-specific filenames to ensure esp32-idf wins the hint count + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + ): + mock_changed_files.return_value = [ + "tests/components/esp32/common.yaml", + "tests/components/esp8266/test.esp8266-ard.yaml", + "esphome/components/wifi/wifi_component_esp_idf.cpp", # ESP-IDF hint + "esphome/components/ethernet/ethernet_esp32.cpp", # ESP32 hint + ] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + # Memory impact should run + assert result["should_run"] == "true" + + # Platform should be esp32-idf (due to more ESP32-IDF hints) + assert result["platform"] == "esp32-idf" + + # CRITICAL: Only esp32 component should be included, not esp8266 + # This prevents trying to build ESP8266 components on ESP32 platform + assert result["components"] == ["esp32"], ( + "When esp32-idf platform is selected, only esp32 component should be included, " + "not esp8266. This prevents trying to build ESP8266 components on ESP32 platform." + ) + + assert result["use_merged_config"] == "true" From 326975ccad0d8c819042981e801b20e79c6c3567 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 3 Nov 2025 21:09:34 -0500 Subject: [PATCH 373/526] [core] Fix ESPTime crash (#11705) --- esphome/core/time.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/core/time.h b/esphome/core/time.h index ffcfced418..68826dabdc 100644 --- a/esphome/core/time.h +++ b/esphome/core/time.h @@ -84,6 +84,9 @@ struct ESPTime { */ static ESPTime from_epoch_local(time_t epoch) { struct tm *c_tm = ::localtime(&epoch); + if (c_tm == nullptr) { + return ESPTime{}; // Return an invalid ESPTime + } return ESPTime::from_c_tm(c_tm, epoch); } /** Convert an UTC epoch timestamp to a UTC time ESPTime instance. @@ -93,6 +96,9 @@ struct ESPTime { */ static ESPTime from_epoch_utc(time_t epoch) { struct tm *c_tm = ::gmtime(&epoch); + if (c_tm == nullptr) { + return ESPTime{}; // Return an invalid ESPTime + } return ESPTime::from_c_tm(c_tm, epoch); } From 758ac583431af02f9482de3240de2c6a806ac8e6 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:38:43 +1000 Subject: [PATCH 374/526] [psram] Require mode for S3 (#11470) Co-authored-by: clydeps --- esphome/components/esp32_camera/__init__.py | 18 ++++++--- esphome/components/inkplate/display.py | 3 +- esphome/components/psram/__init__.py | 12 ++++++ .../speaker/media_player/__init__.py | 40 +++++++++---------- tests/component_tests/psram/test_psram.py | 12 ++++-- tests/components/inkplate/test.esp32-idf.yaml | 3 ++ .../mipi_spi/test-lvgl.esp32-s3-idf.yaml | 1 + .../common/i2c_camera/esp32-idf.yaml | 2 + 8 files changed, 60 insertions(+), 31 deletions(-) diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index d8ba098645..d9d9bc0a56 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -4,6 +4,7 @@ from esphome import automation, pins import esphome.codegen as cg from esphome.components import i2c from esphome.components.esp32 import add_idf_component +from esphome.components.psram import DOMAIN as psram_domain import esphome.config_validation as cv from esphome.const import ( CONF_BRIGHTNESS, @@ -26,10 +27,9 @@ import esphome.final_validate as fv _LOGGER = logging.getLogger(__name__) +AUTO_LOAD = ["camera"] DEPENDENCIES = ["esp32"] -AUTO_LOAD = ["camera", "psram"] - esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera") ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.EntityBase) ESP32CameraImageData = esp32_camera_ns.struct("CameraImageData") @@ -163,6 +163,14 @@ CONF_ON_IMAGE = "on_image" camera_range_param = cv.int_range(min=-2, max=2) + +def validate_fb_location_(value): + validator = cv.enum(ENUM_FB_LOCATION, upper=True) + if value.lower() == psram_domain: + validator = cv.All(validator, cv.requires_component(psram_domain)) + return validator(value) + + CONFIG_SCHEMA = cv.All( cv.ENTITY_BASE_SCHEMA.extend( { @@ -236,9 +244,9 @@ CONFIG_SCHEMA = cv.All( cv.framerate, cv.Range(min=0, max=1) ), cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2), - cv.Optional(CONF_FRAME_BUFFER_LOCATION, default="PSRAM"): cv.enum( - ENUM_FB_LOCATION, upper=True - ), + cv.Optional( + CONF_FRAME_BUFFER_LOCATION, default="PSRAM" + ): validate_fb_location_, cv.Optional(CONF_ON_STREAM_START): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( diff --git a/esphome/components/inkplate/display.py b/esphome/components/inkplate/display.py index a0b0265cf1..89518dcfab 100644 --- a/esphome/components/inkplate/display.py +++ b/esphome/components/inkplate/display.py @@ -20,8 +20,7 @@ import esphome.final_validate as fv from .const import INKPLATE_10_CUSTOM_WAVEFORMS, WAVEFORMS -DEPENDENCIES = ["i2c", "esp32"] -AUTO_LOAD = ["psram"] +DEPENDENCIES = ["i2c", "esp32", "psram"] CONF_DISPLAY_DATA_0_PIN = "display_data_0_pin" CONF_DISPLAY_DATA_1_PIN = "display_data_1_pin" diff --git a/esphome/components/psram/__init__.py b/esphome/components/psram/__init__.py index 8e4f9d7eac..df49e08879 100644 --- a/esphome/components/psram/__init__.py +++ b/esphome/components/psram/__init__.py @@ -1,4 +1,5 @@ import logging +import textwrap import esphome.codegen as cg from esphome.components.esp32 import ( @@ -104,6 +105,17 @@ def get_config_schema(config): if not speeds: raise cv.Invalid("PSRAM is not supported on this chip") modes = SPIRAM_MODES[variant] + if CONF_MODE not in config and len(modes) != 1: + raise ( + cv.Invalid( + textwrap.dedent( + f""" + {variant} requires PSRAM mode selection; one of {", ".join(modes)} + Selection of the wrong mode for the board will cause a runtime failure to initialise PSRAM + """ + ) + ) + ) return cv.Schema( { cv.GenerateID(): cv.declare_id(PsramComponent), diff --git a/esphome/components/speaker/media_player/__init__.py b/esphome/components/speaker/media_player/__init__.py index 7537a61e4e..e50656e723 100644 --- a/esphome/components/speaker/media_player/__init__.py +++ b/esphome/components/speaker/media_player/__init__.py @@ -26,21 +26,12 @@ from esphome.const import ( from esphome.core import CORE, HexInt from esphome.core.entity_helpers import inherit_property_from from esphome.external_files import download_content -from esphome.types import ConfigType +from esphome.final_validate import full_config _LOGGER = logging.getLogger(__name__) -def AUTO_LOAD(config: ConfigType) -> list[str]: - load = ["audio"] - if ( - not config - or config.get(CONF_TASK_STACK_IN_PSRAM) - or config.get(CONF_CODEC_SUPPORT_ENABLED) - ): - return load + ["psram"] - return load - +AUTO_LOAD = ["audio"] CODEOWNERS = ["@kahrendt", "@synesthesiam"] DOMAIN = "media_player" @@ -226,12 +217,19 @@ def _validate_repeated_speaker(config): return config -def _validate_supported_local_file(config): +def _final_validate(config): + # Default to using codec if psram is enabled + if (use_codec := config.get(CONF_CODEC_SUPPORT_ENABLED)) is None: + use_codec = psram.DOMAIN in full_config.get() + conf_id = config[CONF_ID].id + core_data = CORE.data.setdefault(DOMAIN, {conf_id: {}}) + core_data[conf_id][CONF_CODEC_SUPPORT_ENABLED] = use_codec + for file_config in config.get(CONF_FILES, []): _, media_file_type = _read_audio_file_and_type(file_config) if str(media_file_type) == str(audio.AUDIO_FILE_TYPE_ENUM["NONE"]): raise cv.Invalid("Unsupported local media file") - if not config[CONF_CODEC_SUPPORT_ENABLED] and str(media_file_type) != str( + if not use_codec and str(media_file_type) != str( audio.AUDIO_FILE_TYPE_ENUM["WAV"] ): # Only wav files are supported @@ -290,11 +288,11 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range( min=4000, max=4000000 ), - cv.Optional( - CONF_CODEC_SUPPORT_ENABLED, default=psram.supported() - ): cv.boolean, + cv.Optional(CONF_CODEC_SUPPORT_ENABLED): cv.boolean, cv.Optional(CONF_FILES): cv.ensure_list(MEDIA_FILE_TYPE_SCHEMA), - cv.Optional(CONF_TASK_STACK_IN_PSRAM, default=False): cv.boolean, + cv.Optional(CONF_TASK_STACK_IN_PSRAM): cv.All( + cv.boolean, cv.requires_component(psram.DOMAIN) + ), cv.Optional(CONF_VOLUME_INCREMENT, default=0.05): cv.percentage, cv.Optional(CONF_VOLUME_INITIAL, default=0.5): cv.percentage, cv.Optional(CONF_VOLUME_MAX, default=1.0): cv.percentage, @@ -317,12 +315,12 @@ FINAL_VALIDATE_SCHEMA = cv.All( }, extra=cv.ALLOW_EXTRA, ), - _validate_supported_local_file, + _final_validate, ) async def to_code(config): - if config[CONF_CODEC_SUPPORT_ENABLED]: + if CORE.data[DOMAIN][config[CONF_ID].id][CONF_CODEC_SUPPORT_ENABLED]: # Compile all supported audio codecs and optimize the wifi settings cg.add_define("USE_AUDIO_FLAC_SUPPORT", True) @@ -352,8 +350,8 @@ async def to_code(config): cg.add(var.set_buffer_size(config[CONF_BUFFER_SIZE])) - cg.add(var.set_task_stack_in_psram(config[CONF_TASK_STACK_IN_PSRAM])) - if config[CONF_TASK_STACK_IN_PSRAM]: + if config.get(CONF_TASK_STACK_IN_PSRAM): + cg.add(var.set_task_stack_in_psram(True)) esp32.add_idf_sdkconfig_option( "CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY", True ) diff --git a/tests/component_tests/psram/test_psram.py b/tests/component_tests/psram/test_psram.py index 3e40a8d192..f8ad013689 100644 --- a/tests/component_tests/psram/test_psram.py +++ b/tests/component_tests/psram/test_psram.py @@ -34,6 +34,12 @@ SUPPORTED_PSRAM_VARIANTS = [ VARIANT_ESP32S3, VARIANT_ESP32P4, ] +SUPPORTED_PSRAM_MODES = { + VARIANT_ESP32: ["quad"], + VARIANT_ESP32S2: ["quad"], + VARIANT_ESP32S3: ["quad", "octal"], + VARIANT_ESP32P4: ["hex"], +} @pytest.mark.parametrize( @@ -86,7 +92,7 @@ def test_psram_configuration_valid_supported_variants( from esphome.components.psram import CONFIG_SCHEMA, FINAL_VALIDATE_SCHEMA # This should not raise an exception - config = CONFIG_SCHEMA({}) + config = CONFIG_SCHEMA({"mode": SUPPORTED_PSRAM_MODES[variant][0]}) FINAL_VALIDATE_SCHEMA(config) @@ -122,7 +128,7 @@ def _setup_psram_final_validation_test( ("config", "esp32_config", "expect_error", "error_match"), [ pytest.param( - {"speed": "120MHz"}, + {"mode": "quad", "speed": "120MHz"}, {"cpu_frequency": "160MHz"}, True, r"PSRAM 120MHz requires 240MHz CPU frequency", @@ -143,7 +149,7 @@ def _setup_psram_final_validation_test( id="ecc_only_in_octal_mode", ), pytest.param( - {"speed": "120MHZ"}, + {"mode": "quad", "speed": "120MHZ"}, {"cpu_frequency": "240MHZ"}, False, None, diff --git a/tests/components/inkplate/test.esp32-idf.yaml b/tests/components/inkplate/test.esp32-idf.yaml index b47e39c389..17e58ce390 100644 --- a/tests/components/inkplate/test.esp32-idf.yaml +++ b/tests/components/inkplate/test.esp32-idf.yaml @@ -1,4 +1,7 @@ packages: i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml +psram: + mode: quad + <<: !include common.yaml diff --git a/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml index 48f34f3449..14f864d326 100644 --- a/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml +++ b/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml @@ -9,3 +9,4 @@ display: lvgl: psram: + mode: quad diff --git a/tests/test_build_components/common/i2c_camera/esp32-idf.yaml b/tests/test_build_components/common/i2c_camera/esp32-idf.yaml index a6e7c264cb..443ebbebd9 100644 --- a/tests/test_build_components/common/i2c_camera/esp32-idf.yaml +++ b/tests/test_build_components/common/i2c_camera/esp32-idf.yaml @@ -1,4 +1,6 @@ # I2C bus for camera sensor +psram: + i2c: - id: i2c_camera_bus sda: 25 From 0b04361fc0be415a28bf5ffb9c223bf2a262f5b0 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:39:27 +1000 Subject: [PATCH 375/526] [lvgl] Layout improvements (#10149) Co-authored-by: clydeps Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/lvgl/__init__.py | 25 +- esphome/components/lvgl/defines.py | 3 + esphome/components/lvgl/layout.py | 357 +++++++++++++++++++ esphome/components/lvgl/lv_validation.py | 3 + esphome/components/lvgl/lvcode.py | 5 +- esphome/components/lvgl/schemas.py | 259 ++++---------- esphome/components/lvgl/types.py | 31 +- esphome/components/lvgl/widgets/__init__.py | 7 +- esphome/components/lvgl/widgets/canvas.py | 60 ++-- esphome/components/lvgl/widgets/checkbox.py | 9 +- esphome/components/lvgl/widgets/container.py | 39 ++ esphome/components/lvgl/widgets/label.py | 11 +- esphome/components/lvgl/widgets/qrcode.py | 13 +- esphome/components/lvgl/widgets/textarea.py | 17 +- tests/components/lvgl/lvgl-package.yaml | 22 +- 15 files changed, 572 insertions(+), 289 deletions(-) create mode 100644 esphome/components/lvgl/layout.py create mode 100644 esphome/components/lvgl/widgets/container.py diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index aa6935c5fc..861999d0b7 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -41,10 +41,7 @@ from .lv_validation import lv_bool, lv_images_used from .lvcode import LvContext, LvglComponent, lvgl_static from .schemas import ( DISP_BG_SCHEMA, - FLEX_OBJ_SCHEMA, FULL_STYLE_SCHEMA, - GRID_CELL_SCHEMA, - LAYOUT_SCHEMAS, WIDGET_TYPES, any_widget_schema, container_schema, @@ -78,6 +75,7 @@ from .widgets.button import button_spec from .widgets.buttonmatrix import buttonmatrix_spec from .widgets.canvas import canvas_spec from .widgets.checkbox import checkbox_spec +from .widgets.container import container_spec from .widgets.dropdown import dropdown_spec from .widgets.img import img_spec from .widgets.keyboard import keyboard_spec @@ -130,20 +128,10 @@ for w_type in ( tileview_spec, qr_code_spec, canvas_spec, + container_spec, ): WIDGET_TYPES[w_type.name] = w_type -WIDGET_SCHEMA = any_widget_schema() - -LAYOUT_SCHEMAS[df.TYPE_GRID] = { - cv.Optional(df.CONF_WIDGETS): cv.ensure_list(any_widget_schema(GRID_CELL_SCHEMA)) -} -LAYOUT_SCHEMAS[df.TYPE_FLEX] = { - cv.Optional(df.CONF_WIDGETS): cv.ensure_list(any_widget_schema(FLEX_OBJ_SCHEMA)) -} -LAYOUT_SCHEMAS[df.TYPE_NONE] = { - cv.Optional(df.CONF_WIDGETS): cv.ensure_list(any_widget_schema()) -} for w_type in WIDGET_TYPES.values(): register_action( f"lvgl.{w_type.name}.update", @@ -410,7 +398,7 @@ def display_schema(config): def add_hello_world(config): if df.CONF_WIDGETS not in config and CONF_PAGES not in config: LOGGER.info("No pages or widgets configured, creating default hello_world page") - config[df.CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world()) + config[df.CONF_WIDGETS] = any_widget_schema()(get_hello_world()) return config @@ -450,6 +438,7 @@ LVGL_SCHEMA = cv.All( ), } ), + cv.Optional(CONF_PAGES): cv.ensure_list(container_schema(page_spec)), **{ cv.Optional(x): validate_automation( { @@ -459,12 +448,6 @@ LVGL_SCHEMA = cv.All( ) for x in SIMPLE_TRIGGERS }, - cv.Exclusive(df.CONF_WIDGETS, CONF_PAGES): cv.ensure_list( - WIDGET_SCHEMA - ), - cv.Exclusive(CONF_PAGES, CONF_PAGES): cv.ensure_list( - container_schema(page_spec) - ), cv.Optional(df.CONF_MSGBOXES): cv.ensure_list(MSGBOX_SCHEMA), cv.Optional(df.CONF_PAGE_WRAP, default=True): lv_bool, cv.Optional(df.CONF_TOP_LAYER): container_schema(obj_spec), diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 3241ba9c3f..f2bcb6cc06 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -394,6 +394,8 @@ LV_FLEX_ALIGNMENTS = LvConstant( "SPACE_BETWEEN", ) +LV_FLEX_CROSS_ALIGNMENTS = LV_FLEX_ALIGNMENTS.extend("STRETCH") + LV_MENU_MODES = LvConstant( "LV_MENU_HEADER_", "TOP_FIXED", @@ -436,6 +438,7 @@ CONF_BUTTONS = "buttons" CONF_BYTE_ORDER = "byte_order" CONF_CHANGE_RATE = "change_rate" CONF_CLOSE_BUTTON = "close_button" +CONF_CONTAINER = "container" CONF_CONTROL = "control" CONF_DEFAULT_FONT = "default_font" CONF_DEFAULT_GROUP = "default_group" diff --git a/esphome/components/lvgl/layout.py b/esphome/components/lvgl/layout.py new file mode 100644 index 0000000000..0aed525e16 --- /dev/null +++ b/esphome/components/lvgl/layout.py @@ -0,0 +1,357 @@ +import re + +import esphome.config_validation as cv +from esphome.const import CONF_HEIGHT, CONF_TYPE, CONF_WIDTH + +from .defines import ( + CONF_FLEX_ALIGN_CROSS, + CONF_FLEX_ALIGN_MAIN, + CONF_FLEX_ALIGN_TRACK, + CONF_FLEX_FLOW, + CONF_FLEX_GROW, + CONF_GRID_CELL_COLUMN_POS, + CONF_GRID_CELL_COLUMN_SPAN, + CONF_GRID_CELL_ROW_POS, + CONF_GRID_CELL_ROW_SPAN, + CONF_GRID_CELL_X_ALIGN, + CONF_GRID_CELL_Y_ALIGN, + CONF_GRID_COLUMN_ALIGN, + CONF_GRID_COLUMNS, + CONF_GRID_ROW_ALIGN, + CONF_GRID_ROWS, + CONF_LAYOUT, + CONF_PAD_COLUMN, + CONF_PAD_ROW, + CONF_WIDGETS, + FLEX_FLOWS, + LV_CELL_ALIGNMENTS, + LV_FLEX_ALIGNMENTS, + LV_FLEX_CROSS_ALIGNMENTS, + LV_GRID_ALIGNMENTS, + TYPE_FLEX, + TYPE_GRID, + TYPE_NONE, + LvConstant, +) +from .lv_validation import padding, size + +cell_alignments = LV_CELL_ALIGNMENTS.one_of +grid_alignments = LV_GRID_ALIGNMENTS.one_of +flex_alignments = LV_FLEX_ALIGNMENTS.one_of + +FLEX_LAYOUT_SCHEMA = { + cv.Required(CONF_TYPE): cv.one_of(TYPE_FLEX, lower=True), + cv.Optional(CONF_FLEX_FLOW, default="row_wrap"): FLEX_FLOWS.one_of, + cv.Optional(CONF_FLEX_ALIGN_MAIN, default="start"): flex_alignments, + cv.Optional( + CONF_FLEX_ALIGN_CROSS, default="start" + ): LV_FLEX_CROSS_ALIGNMENTS.one_of, + cv.Optional(CONF_FLEX_ALIGN_TRACK, default="start"): flex_alignments, + cv.Optional(CONF_PAD_ROW): padding, + cv.Optional(CONF_PAD_COLUMN): padding, + cv.Optional(CONF_FLEX_GROW): cv.int_, +} + +FLEX_HV_STYLE = { + CONF_FLEX_ALIGN_MAIN: "LV_FLEX_ALIGN_SPACE_EVENLY", + CONF_FLEX_ALIGN_TRACK: "LV_FLEX_ALIGN_CENTER", + CONF_FLEX_ALIGN_CROSS: "LV_FLEX_ALIGN_CENTER", + CONF_TYPE: TYPE_FLEX, +} + +FLEX_OBJ_SCHEMA = { + cv.Optional(CONF_FLEX_GROW): cv.int_, +} + + +def flex_hv_schema(dir): + dir = CONF_HEIGHT if dir == "horizontal" else CONF_WIDTH + return { + cv.Optional(CONF_FLEX_GROW, default=1): cv.int_, + cv.Optional(dir, default="100%"): size, + } + + +def grid_free_space(value): + value = cv.Upper(value) + if value.startswith("FR(") and value.endswith(")"): + value = value.removesuffix(")").removeprefix("FR(") + return f"LV_GRID_FR({cv.positive_int(value)})" + raise cv.Invalid("must be a size in pixels, CONTENT or FR(nn)") + + +grid_spec = cv.Any(size, LvConstant("LV_GRID_", "CONTENT").one_of, grid_free_space) + +GRID_CELL_SCHEMA = { + cv.Optional(CONF_GRID_CELL_ROW_POS): cv.positive_int, + cv.Optional(CONF_GRID_CELL_COLUMN_POS): cv.positive_int, + cv.Optional(CONF_GRID_CELL_ROW_SPAN, default=1): cv.positive_int, + cv.Optional(CONF_GRID_CELL_COLUMN_SPAN, default=1): cv.positive_int, + cv.Optional(CONF_GRID_CELL_X_ALIGN): grid_alignments, + cv.Optional(CONF_GRID_CELL_Y_ALIGN): grid_alignments, +} + + +class Layout: + """ + Define properties for a layout + The base class is layout "none" + """ + + def get_type(self): + return TYPE_NONE + + def get_layout_schemas(self, config: dict) -> tuple: + """ + Get the layout and child schema for a given widget based on its layout type. + """ + return None, {} + + def validate(self, config): + """ + Validate the layout configuration. This is called late in the schema validation + :param config: The input configuration + :return: The validated configuration + """ + return config + + +class FlexLayout(Layout): + def get_type(self): + return TYPE_FLEX + + def get_layout_schemas(self, config: dict) -> tuple: + layout = config.get(CONF_LAYOUT) + if not isinstance(layout, dict) or layout.get(CONF_TYPE) != TYPE_FLEX: + return None, {} + child_schema = FLEX_OBJ_SCHEMA + if grow := layout.get(CONF_FLEX_GROW): + child_schema = {cv.Optional(CONF_FLEX_GROW, default=grow): cv.int_} + # Polyfill to implement stretch alignment for flex containers + # LVGL does not support this natively, so we add a 100% size property to the children in the cross-axis + if layout.get(CONF_FLEX_ALIGN_CROSS) == "LV_FLEX_ALIGN_STRETCH": + dimension = ( + CONF_WIDTH + if "COLUMN" in layout[CONF_FLEX_FLOW].upper() + else CONF_HEIGHT + ) + child_schema[cv.Optional(dimension, default="100%")] = size + return FLEX_LAYOUT_SCHEMA, child_schema + + def validate(self, config): + """ + Perform validation on the container and its children for this layout + :param config: + :return: + """ + return config + + +class DirectionalLayout(FlexLayout): + def __init__(self, direction: str, flow): + """ + :param direction: "horizontal" or "vertical" + :param flow: "row" or "column" + """ + super().__init__() + self.direction = direction + self.flow = flow + + def get_type(self): + return self.direction + + def get_layout_schemas(self, config: dict) -> tuple: + if config.get(CONF_LAYOUT, "").lower() != self.direction: + return None, {} + return cv.one_of(self.direction, lower=True), flex_hv_schema(self.direction) + + def validate(self, config): + assert config[CONF_LAYOUT].lower() == self.direction + config[CONF_LAYOUT] = { + **FLEX_HV_STYLE, + CONF_FLEX_FLOW: "LV_FLEX_FLOW_" + self.flow.upper(), + } + return config + + +class GridLayout(Layout): + _GRID_LAYOUT_REGEX = re.compile(r"^\s*(\d+)\s*x\s*(\d+)\s*$") + + def get_type(self): + return TYPE_GRID + + def get_layout_schemas(self, config: dict) -> tuple: + layout = config.get(CONF_LAYOUT) + if isinstance(layout, str): + if GridLayout._GRID_LAYOUT_REGEX.match(layout): + return ( + cv.string, + { + cv.Optional(CONF_GRID_CELL_ROW_POS): cv.positive_int, + cv.Optional(CONF_GRID_CELL_COLUMN_POS): cv.positive_int, + cv.Optional( + CONF_GRID_CELL_ROW_SPAN, default=1 + ): cv.positive_int, + cv.Optional( + CONF_GRID_CELL_COLUMN_SPAN, default=1 + ): cv.positive_int, + cv.Optional( + CONF_GRID_CELL_X_ALIGN, default="center" + ): grid_alignments, + cv.Optional( + CONF_GRID_CELL_Y_ALIGN, default="center" + ): grid_alignments, + }, + ) + # Not a valid grid layout string + return None, {} + + if not isinstance(layout, dict) or layout.get(CONF_TYPE) != TYPE_GRID: + return None, {} + return ( + { + cv.Required(CONF_TYPE): cv.one_of(TYPE_GRID, lower=True), + cv.Required(CONF_GRID_ROWS): [grid_spec], + cv.Required(CONF_GRID_COLUMNS): [grid_spec], + cv.Optional(CONF_GRID_COLUMN_ALIGN): grid_alignments, + cv.Optional(CONF_GRID_ROW_ALIGN): grid_alignments, + cv.Optional(CONF_PAD_ROW): padding, + cv.Optional(CONF_PAD_COLUMN): padding, + }, + { + cv.Optional(CONF_GRID_CELL_ROW_POS): cv.positive_int, + cv.Optional(CONF_GRID_CELL_COLUMN_POS): cv.positive_int, + cv.Optional(CONF_GRID_CELL_ROW_SPAN, default=1): cv.positive_int, + cv.Optional(CONF_GRID_CELL_COLUMN_SPAN, default=1): cv.positive_int, + cv.Optional(CONF_GRID_CELL_X_ALIGN): grid_alignments, + cv.Optional(CONF_GRID_CELL_Y_ALIGN): grid_alignments, + }, + ) + + def validate(self, config: dict): + """ + Validate the grid layout. + The `layout:` key may be a dictionary with `rows` and `columns` keys, or a string in the format "rows x columns". + Either all cells must have a row and column, + or none, in which case the grid layout is auto-generated. + :param config: + :return: The config updated with auto-generated values + """ + layout = config.get(CONF_LAYOUT) + if isinstance(layout, str): + # If the layout is a string, assume it is in the format "rows x columns", implying + # a grid layout with the specified number of rows and columns each with CONTENT sizing. + layout = layout.strip() + match = GridLayout._GRID_LAYOUT_REGEX.match(layout) + if match: + rows = int(match.group(1)) + cols = int(match.group(2)) + layout = { + CONF_TYPE: TYPE_GRID, + CONF_GRID_ROWS: ["LV_GRID_FR(1)"] * rows, + CONF_GRID_COLUMNS: ["LV_GRID_FR(1)"] * cols, + } + config[CONF_LAYOUT] = layout + else: + raise cv.Invalid( + f"Invalid grid layout format: {config}, expected 'rows x columns'", + [CONF_LAYOUT], + ) + # should be guaranteed to be a dict at this point + assert isinstance(layout, dict) + assert layout.get(CONF_TYPE) == TYPE_GRID + rows = len(layout[CONF_GRID_ROWS]) + columns = len(layout[CONF_GRID_COLUMNS]) + used_cells = [[None] * columns for _ in range(rows)] + for index, widget in enumerate(config.get(CONF_WIDGETS, [])): + _, w = next(iter(widget.items())) + if (CONF_GRID_CELL_COLUMN_POS in w) != (CONF_GRID_CELL_ROW_POS in w): + raise cv.Invalid( + "Both row and column positions must be specified, or both omitted", + [CONF_WIDGETS, index], + ) + if CONF_GRID_CELL_ROW_POS in w: + row = w[CONF_GRID_CELL_ROW_POS] + column = w[CONF_GRID_CELL_COLUMN_POS] + else: + try: + row, column = next( + (r_idx, c_idx) + for r_idx, row in enumerate(used_cells) + for c_idx, value in enumerate(row) + if value is None + ) + except StopIteration: + raise cv.Invalid( + "No free cells available in grid layout", [CONF_WIDGETS, index] + ) from None + w[CONF_GRID_CELL_ROW_POS] = row + w[CONF_GRID_CELL_COLUMN_POS] = column + + for i in range(w[CONF_GRID_CELL_ROW_SPAN]): + for j in range(w[CONF_GRID_CELL_COLUMN_SPAN]): + if row + i >= rows or column + j >= columns: + raise cv.Invalid( + f"Cell at {row}/{column} span {w[CONF_GRID_CELL_ROW_SPAN]}x{w[CONF_GRID_CELL_COLUMN_SPAN]} " + f"exceeds grid size {rows}x{columns}", + [CONF_WIDGETS, index], + ) + if used_cells[row + i][column + j] is not None: + raise cv.Invalid( + f"Cell span {row + i}/{column + j} already occupied by widget at index {used_cells[row + i][column + j]}", + [CONF_WIDGETS, index], + ) + used_cells[row + i][column + j] = index + + return config + + +LAYOUT_CLASSES = ( + FlexLayout(), + GridLayout(), + DirectionalLayout("horizontal", "row"), + DirectionalLayout("vertical", "column"), +) +LAYOUT_CHOICES = [x.get_type() for x in LAYOUT_CLASSES] + + +def append_layout_schema(schema, config: dict): + """ + Get the child layout schema for a given widget based on its layout type. + :param config: The config to check + :return: A schema for the layout including a widgets key + """ + # Local import to avoid circular dependencies + if CONF_WIDGETS not in config: + if CONF_LAYOUT in config: + raise cv.Invalid( + f"Layout {config[CONF_LAYOUT]} requires a {CONF_WIDGETS} key", + [CONF_LAYOUT], + ) + return schema + + from .schemas import any_widget_schema + + if CONF_LAYOUT not in config: + # If no layout is specified, return the schema as is + return schema.extend({cv.Optional(CONF_WIDGETS): any_widget_schema()}) + + for layout_class in LAYOUT_CLASSES: + layout_schema, child_schema = layout_class.get_layout_schemas(config) + if layout_schema: + layout_schema = cv.Schema( + { + cv.Required(CONF_LAYOUT): layout_schema, + cv.Required(CONF_WIDGETS): any_widget_schema(child_schema), + } + ) + layout_schema.add_extra(layout_class.validate) + return layout_schema.extend(schema) + + # If no layout class matched, return a default schema + return cv.Schema( + { + cv.Optional(CONF_LAYOUT): cv.one_of(*LAYOUT_CHOICES, lower=True), + cv.Optional(CONF_WIDGETS): any_widget_schema(), + } + ) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index 6f95a32a18..9fe72128ce 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -1,3 +1,4 @@ +import re from typing import TYPE_CHECKING, Any import esphome.codegen as cg @@ -246,6 +247,8 @@ def pixels_or_percent_validator(value): return ["pixels", "..%"] if isinstance(value, str) and value.lower().endswith("px"): value = cv.int_(value[:-2]) + if isinstance(value, str) and re.match(r"^lv_pct\((\d+)\)$", value): + return value value = cv.Any(cv.int_, cv.percentage)(value) if isinstance(value, int): return value diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index ea38845c07..c11597131f 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -299,6 +299,7 @@ class LvExpr(MockLv): # Top level mock for generic lv_ calls to be recorded lv = MockLv("lv_") +LV = MockLv("LV_") # Just generate an expression lv_expr = LvExpr("lv_") # Mock for lv_obj_ calls @@ -327,7 +328,7 @@ def lv_assign(target, expression): lv_add(AssignmentExpression("", "", target, expression)) -def lv_Pvariable(type, name): +def lv_Pvariable(type, name) -> MockObj: """ Create but do not initialise a pointer variable :param type: Type of the variable target @@ -343,7 +344,7 @@ def lv_Pvariable(type, name): return var -def lv_variable(type, name): +def lv_variable(type, name) -> MockObj: """ Create but do not initialise a variable :param type: Type of the variable target diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 959d203c41..dd248d0b94 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -12,17 +12,21 @@ from esphome.const import ( CONF_TEXT, CONF_TIME, CONF_TRIGGER_ID, - CONF_TYPE, CONF_X, CONF_Y, ) from esphome.core import TimePeriod from esphome.core.config import StartupTrigger -from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid -from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR, TYPE_GRID -from .helpers import add_lv_use, requires_component, validate_printf +from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR +from .helpers import requires_component, validate_printf +from .layout import ( + FLEX_OBJ_SCHEMA, + GRID_CELL_SCHEMA, + append_layout_schema, + grid_alignments, +) from .lv_validation import lv_color, lv_font, lv_gradient, lv_image, opacity from .lvcode import LvglComponent, lv_event_t_ptr from .types import ( @@ -72,11 +76,9 @@ def _validate_text(value): # A schema for text properties -TEXT_SCHEMA = cv.Schema( - { - cv.Optional(CONF_TEXT): _validate_text, - } -) +TEXT_SCHEMA = { + cv.Optional(CONF_TEXT): _validate_text, +} LIST_ACTION_SCHEMA = cv.ensure_list( cv.maybe_simple_value( @@ -136,7 +138,7 @@ STYLE_PROPS = { "arc_opa": lvalid.opacity, "arc_color": lvalid.lv_color, "arc_rounded": lvalid.lv_bool, - "arc_width": lvalid.lv_positive_int, + "arc_width": lvalid.pixels, "anim_time": lvalid.lv_milliseconds, "bg_color": lvalid.lv_color, "bg_grad": lv_gradient, @@ -223,10 +225,6 @@ STYLE_REMAP = { "image_recolor_opa": "img_recolor_opa", } -cell_alignments = df.LV_CELL_ALIGNMENTS.one_of -grid_alignments = df.LV_GRID_ALIGNMENTS.one_of -flex_alignments = df.LV_FLEX_ALIGNMENTS.one_of - # Complete object style schema STYLE_SCHEMA = cv.Schema({cv.Optional(k): v for k, v in STYLE_PROPS.items()}).extend( { @@ -266,10 +264,8 @@ def part_schema(parts): :param parts: The parts to include :return: The schema """ - return ( - cv.Schema({cv.Optional(part): STATE_SCHEMA for part in parts}) - .extend(STATE_SCHEMA) - .extend(FLAG_SCHEMA) + return STATE_SCHEMA.extend(FLAG_SCHEMA).extend( + {cv.Optional(part): STATE_SCHEMA for part in parts} ) @@ -277,10 +273,10 @@ def automation_schema(typ: LvType): events = df.LV_EVENT_TRIGGERS + df.SWIPE_TRIGGERS if typ.has_on_value: events = events + (CONF_ON_VALUE,) - args = typ.get_arg_type() if isinstance(typ, LvType) else [] + args = typ.get_arg_type() args.append(lv_event_t_ptr) - return cv.Schema( - { + return { + **{ cv.Optional(event): validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( @@ -289,14 +285,11 @@ def automation_schema(typ: LvType): } ) for event in events - } - ).extend( - { - cv.Optional(CONF_ON_BOOT): validate_automation( - {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger)} - ) - } - ) + }, + cv.Optional(CONF_ON_BOOT): validate_automation( + {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger)} + ), + } def base_update_schema(widget_type, parts): @@ -335,75 +328,17 @@ def obj_schema(widget_type: WidgetType): """ return ( part_schema(widget_type.parts) - .extend(LAYOUT_SCHEMA) .extend(ALIGN_TO_SCHEMA) .extend(automation_schema(widget_type.w_type)) .extend( - cv.Schema( - { - cv.Optional(CONF_STATE): SET_STATE_SCHEMA, - cv.Optional(CONF_GROUP): cv.use_id(lv_group_t), - } - ) + { + cv.Optional(CONF_STATE): SET_STATE_SCHEMA, + cv.Optional(CONF_GROUP): cv.use_id(lv_group_t), + } ) ) -def _validate_grid_layout(config): - layout = config[df.CONF_LAYOUT] - rows = len(layout[df.CONF_GRID_ROWS]) - columns = len(layout[df.CONF_GRID_COLUMNS]) - used_cells = [[None] * columns for _ in range(rows)] - for index, widget in enumerate(config[df.CONF_WIDGETS]): - _, w = next(iter(widget.items())) - if (df.CONF_GRID_CELL_COLUMN_POS in w) != (df.CONF_GRID_CELL_ROW_POS in w): - # pylint: disable=raise-missing-from - raise cv.Invalid( - "Both row and column positions must be specified, or both omitted", - [df.CONF_WIDGETS, index], - ) - if df.CONF_GRID_CELL_ROW_POS in w: - row = w[df.CONF_GRID_CELL_ROW_POS] - column = w[df.CONF_GRID_CELL_COLUMN_POS] - else: - try: - row, column = next( - (r_idx, c_idx) - for r_idx, row in enumerate(used_cells) - for c_idx, value in enumerate(row) - if value is None - ) - except StopIteration: - # pylint: disable=raise-missing-from - raise cv.Invalid( - "No free cells available in grid layout", [df.CONF_WIDGETS, index] - ) - w[df.CONF_GRID_CELL_ROW_POS] = row - w[df.CONF_GRID_CELL_COLUMN_POS] = column - - for i in range(w[df.CONF_GRID_CELL_ROW_SPAN]): - for j in range(w[df.CONF_GRID_CELL_COLUMN_SPAN]): - if row + i >= rows or column + j >= columns: - # pylint: disable=raise-missing-from - raise cv.Invalid( - f"Cell at {row}/{column} span {w[df.CONF_GRID_CELL_ROW_SPAN]}x{w[df.CONF_GRID_CELL_COLUMN_SPAN]} " - f"exceeds grid size {rows}x{columns}", - [df.CONF_WIDGETS, index], - ) - if used_cells[row + i][column + j] is not None: - # pylint: disable=raise-missing-from - raise cv.Invalid( - f"Cell span {row + i}/{column + j} already occupied by widget at index {used_cells[row + i][column + j]}", - [df.CONF_WIDGETS, index], - ) - used_cells[row + i][column + j] = index - - return config - - -LAYOUT_SCHEMAS = {} -LAYOUT_VALIDATORS = {TYPE_GRID: _validate_grid_layout} - ALIGN_TO_SCHEMA = { cv.Optional(df.CONF_ALIGN_TO): cv.Schema( { @@ -416,57 +351,6 @@ ALIGN_TO_SCHEMA = { } -def grid_free_space(value): - value = cv.Upper(value) - if value.startswith("FR(") and value.endswith(")"): - value = value.removesuffix(")").removeprefix("FR(") - return f"LV_GRID_FR({cv.positive_int(value)})" - raise cv.Invalid("must be a size in pixels, CONTENT or FR(nn)") - - -grid_spec = cv.Any( - lvalid.size, df.LvConstant("LV_GRID_", "CONTENT").one_of, grid_free_space -) - -LAYOUT_SCHEMA = { - cv.Optional(df.CONF_LAYOUT): cv.typed_schema( - { - df.TYPE_GRID: { - cv.Required(df.CONF_GRID_ROWS): [grid_spec], - cv.Required(df.CONF_GRID_COLUMNS): [grid_spec], - cv.Optional(df.CONF_GRID_COLUMN_ALIGN): grid_alignments, - cv.Optional(df.CONF_GRID_ROW_ALIGN): grid_alignments, - cv.Optional(df.CONF_PAD_ROW): lvalid.padding, - cv.Optional(df.CONF_PAD_COLUMN): lvalid.padding, - }, - df.TYPE_FLEX: { - cv.Optional( - df.CONF_FLEX_FLOW, default="row_wrap" - ): df.FLEX_FLOWS.one_of, - cv.Optional(df.CONF_FLEX_ALIGN_MAIN, default="start"): flex_alignments, - cv.Optional(df.CONF_FLEX_ALIGN_CROSS, default="start"): flex_alignments, - cv.Optional(df.CONF_FLEX_ALIGN_TRACK, default="start"): flex_alignments, - cv.Optional(df.CONF_PAD_ROW): lvalid.padding, - cv.Optional(df.CONF_PAD_COLUMN): lvalid.padding, - }, - }, - lower=True, - ) -} - -GRID_CELL_SCHEMA = { - cv.Optional(df.CONF_GRID_CELL_ROW_POS): cv.positive_int, - cv.Optional(df.CONF_GRID_CELL_COLUMN_POS): cv.positive_int, - cv.Optional(df.CONF_GRID_CELL_ROW_SPAN, default=1): cv.positive_int, - cv.Optional(df.CONF_GRID_CELL_COLUMN_SPAN, default=1): cv.positive_int, - cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments, - cv.Optional(df.CONF_GRID_CELL_Y_ALIGN): grid_alignments, -} - -FLEX_OBJ_SCHEMA = { - cv.Optional(df.CONF_FLEX_GROW): cv.int_, -} - DISP_BG_SCHEMA = cv.Schema( { cv.Optional(df.CONF_DISP_BG_IMAGE): cv.Any( @@ -498,48 +382,11 @@ ALL_STYLES = { } -def container_validator(schema, widget_type: WidgetType): - """ - Create a validator for a container given the widget type - :param schema: Base schema to extend - :param widget_type: - :return: - """ - - def validator(value): - if w_sch := widget_type.schema: - if isinstance(w_sch, dict): - w_sch = cv.Schema(w_sch) - # order is important here to preserve extras - result = w_sch.extend(schema) - else: - result = schema - ltype = df.TYPE_NONE - if value and (layout := value.get(df.CONF_LAYOUT)): - if not isinstance(layout, dict): - raise cv.Invalid("Layout value must be a dict") - ltype = layout.get(CONF_TYPE) - if not ltype: - raise (cv.Invalid("Layout schema requires type:")) - add_lv_use(ltype) - if value == SCHEMA_EXTRACT: - return result - result = result.extend( - LAYOUT_SCHEMAS.get(ltype.lower(), LAYOUT_SCHEMAS[df.TYPE_NONE]) - ) - value = result(value) - if layout_validator := LAYOUT_VALIDATORS.get(ltype): - value = layout_validator(value) - return value - - return validator - - def container_schema(widget_type: WidgetType, extras=None): """ Create a schema for a container widget of a given type. All obj properties are available, plus the extras passed in, plus any defined for the specific widget being specified. - :param widget_type: The widget type, e.g. "img" + :param widget_type: The widget type, e.g. "image" :param extras: Additional options to be made available, e.g. layout properties for children :return: The schema for this type of widget. """ @@ -549,31 +396,49 @@ def container_schema(widget_type: WidgetType, extras=None): if extras: schema = schema.extend(extras) # Delayed evaluation for recursion - return container_validator(schema, widget_type) + schema = schema.extend(widget_type.schema) -def widget_schema(widget_type: WidgetType, extras=None): - """ - Create a schema for a given widget type - :param widget_type: The name of the widget - :param extras: - :return: - """ - validator = container_schema(widget_type, extras=extras) - if required := widget_type.required_component: - validator = cv.All(validator, requires_component(required)) - return cv.Exclusive(widget_type.name, df.CONF_WIDGETS), validator + def validator(value): + return append_layout_schema(schema, value)(value) - -# All widget schemas must be defined before this is called. + return validator def any_widget_schema(extras=None): """ - Generate schemas for all possible LVGL widgets. This is what implements the ability to have a list of any kind of + Dynamically generate schemas for all possible LVGL widgets. This is what implements the ability to have a list of any kind of widget under the widgets: key. :param extras: Additional schema to be applied to each generated one - :return: + :return: A validator for the Widgets key """ - return cv.Any(dict(widget_schema(wt, extras) for wt in WIDGET_TYPES.values())) + + def validator(value): + if isinstance(value, dict): + # Convert to list + value = [{k: v} for k, v in value.items()] + if not isinstance(value, list): + raise cv.Invalid("Expected a list of widgets") + result = [] + for index, entry in enumerate(value): + if not isinstance(entry, dict) or len(entry) != 1: + raise cv.Invalid( + "Each widget must be a dictionary with a single key", path=[index] + ) + [(key, value)] = entry.items() + # Validate the widget against its schema + widget_type = WIDGET_TYPES.get(key) + if not widget_type: + raise cv.Invalid(f"Unknown widget type: {key}", path=[index]) + container_validator = container_schema(widget_type, extras=extras) + if required := widget_type.required_component: + container_validator = cv.All( + container_validator, requires_component(required) + ) + # Apply custom validation + value = widget_type.validate(value or {}) + result.append({key: container_validator(value)}) + return result + + return validator diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index 9955b530aa..8c33e13934 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -1,6 +1,7 @@ import sys from esphome import automation, codegen as cg +from esphome.config_validation import Schema from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_TEXT, CONF_VALUE from esphome.cpp_generator import MockObj, MockObjClass from esphome.cpp_types import esphome_ns @@ -135,14 +136,14 @@ class WidgetType: self.lv_name = lv_name or name self.w_type = w_type self.parts = parts - if schema is None: - self.schema = {} - else: - self.schema = schema + if not isinstance(schema, Schema): + schema = Schema(schema or {}) + self.schema = schema if modify_schema is None: - self.modify_schema = self.schema - else: - self.modify_schema = modify_schema + modify_schema = schema + if not isinstance(modify_schema, Schema): + modify_schema = Schema(modify_schema) + self.modify_schema = modify_schema self.mock_obj = MockObj(f"lv_{self.lv_name}", "_") @property @@ -163,7 +164,6 @@ class WidgetType: :param config: Its configuration :return: Generated code as a list of text lines """ - return [] async def obj_creator(self, parent: MockObjClass, config: dict): """ @@ -174,6 +174,13 @@ class WidgetType: """ return lv_expr.call(f"{self.lv_name}_create", parent) + def on_create(self, var: MockObj, config: dict): + """ + Called from to_code when the widget is created, to set up any initial properties + :param var: The variable representing the widget + :param config: Its configuration + """ + def get_uses(self): """ Get a list of other widgets used by this one @@ -193,6 +200,14 @@ class WidgetType: def get_scale(self, config: dict): return 1.0 + def validate(self, value): + """ + Provides an opportunity for custom validation for a given widget type + :param value: + :return: + """ + return value + class NumberType(WidgetType): def get_max(self, config: dict): diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 1f9cdde0a0..7d9f9cb7de 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -339,7 +339,10 @@ async def set_obj_properties(w: Widget, config): if layout_type == TYPE_FLEX: lv_obj.set_flex_flow(w.obj, literal(layout[CONF_FLEX_FLOW])) main = literal(layout[CONF_FLEX_ALIGN_MAIN]) - cross = literal(layout[CONF_FLEX_ALIGN_CROSS]) + cross = layout[CONF_FLEX_ALIGN_CROSS] + if cross == "LV_FLEX_ALIGN_STRETCH": + cross = "LV_FLEX_ALIGN_CENTER" + cross = literal(cross) track = literal(layout[CONF_FLEX_ALIGN_TRACK]) lv_obj.set_flex_align(w.obj, main, cross, track) parts = collect_parts(config) @@ -446,9 +449,11 @@ async def widget_to_code(w_cnfig, w_type: WidgetType, parent): if spec.is_compound(): var = cg.new_Pvariable(wid) lv_add(var.set_obj(creator)) + spec.on_create(var.obj, w_cnfig) else: var = lv_Pvariable(lv_obj_t, wid) lv_assign(var, creator) + spec.on_create(var, w_cnfig) w = Widget.create(wid, var, spec, w_cnfig) if theme := theme_widget_map.get(w_type): diff --git a/esphome/components/lvgl/widgets/canvas.py b/esphome/components/lvgl/widgets/canvas.py index f0a9cd35ba..ead352aa77 100644 --- a/esphome/components/lvgl/widgets/canvas.py +++ b/esphome/components/lvgl/widgets/canvas.py @@ -159,18 +159,15 @@ async def canvas_set_pixel(config, action_id, template_arg, args): ) -DRAW_SCHEMA = cv.Schema( - { - cv.GenerateID(CONF_ID): cv.use_id(lv_canvas_t), - cv.Required(CONF_X): pixels, - cv.Required(CONF_Y): pixels, - } -) -DRAW_OPA_SCHEMA = DRAW_SCHEMA.extend( - { - cv.Optional(CONF_OPA): opacity, - } -) +DRAW_SCHEMA = { + cv.GenerateID(CONF_ID): cv.use_id(lv_canvas_t), + cv.Required(CONF_X): pixels, + cv.Required(CONF_Y): pixels, +} +DRAW_OPA_SCHEMA = { + **DRAW_SCHEMA, + cv.Optional(CONF_OPA): opacity, +} async def draw_to_code(config, dsc_type, props, do_draw, action_id, template_arg, args): @@ -224,12 +221,14 @@ RECT_PROPS = { @automation.register_action( "lvgl.canvas.draw_rectangle", ObjUpdateAction, - DRAW_SCHEMA.extend( + cv.Schema( { + **DRAW_OPA_SCHEMA, cv.Required(CONF_WIDTH): cv.templatable(cv.int_), cv.Required(CONF_HEIGHT): cv.templatable(cv.int_), - }, - ).extend({cv.Optional(prop): STYLE_PROPS[prop] for prop in RECT_PROPS}), + **{cv.Optional(prop): STYLE_PROPS[prop] for prop in RECT_PROPS}, + } + ), ) async def canvas_draw_rect(config, action_id, template_arg, args): width = await pixels.process(config[CONF_WIDTH]) @@ -261,13 +260,14 @@ TEXT_PROPS = { @automation.register_action( "lvgl.canvas.draw_text", ObjUpdateAction, - TEXT_SCHEMA.extend(DRAW_OPA_SCHEMA) - .extend( + cv.Schema( { + **TEXT_SCHEMA, + **DRAW_OPA_SCHEMA, cv.Required(CONF_MAX_WIDTH): cv.templatable(cv.int_), + **{cv.Optional(prop): STYLE_PROPS[f"text_{prop}"] for prop in TEXT_PROPS}, }, - ) - .extend({cv.Optional(prop): STYLE_PROPS[f"text_{prop}"] for prop in TEXT_PROPS}), + ), ) async def canvas_draw_text(config, action_id, template_arg, args): text = await lv_text.process(config[CONF_TEXT]) @@ -293,13 +293,15 @@ IMG_PROPS = { @automation.register_action( "lvgl.canvas.draw_image", ObjUpdateAction, - DRAW_OPA_SCHEMA.extend( + cv.Schema( { + **DRAW_OPA_SCHEMA, cv.Required(CONF_SRC): lv_image, cv.Optional(CONF_PIVOT_X, default=0): pixels, cv.Optional(CONF_PIVOT_Y, default=0): pixels, - }, - ).extend({cv.Optional(prop): validator for prop, validator in IMG_PROPS.items()}), + **{cv.Optional(prop): validator for prop, validator in IMG_PROPS.items()}, + } + ), ) async def canvas_draw_image(config, action_id, template_arg, args): src = await lv_image.process(config[CONF_SRC]) @@ -336,8 +338,9 @@ LINE_PROPS = { cv.GenerateID(CONF_ID): cv.use_id(lv_canvas_t), cv.Optional(CONF_OPA): opacity, cv.Required(CONF_POINTS): cv.ensure_list(point_schema), - }, - ).extend({cv.Optional(prop): validator for prop, validator in LINE_PROPS.items()}), + **{cv.Optional(prop): validator for prop, validator in LINE_PROPS.items()}, + } + ), ) async def canvas_draw_line(config, action_id, template_arg, args): points = [ @@ -363,8 +366,9 @@ async def canvas_draw_line(config, action_id, template_arg, args): { cv.GenerateID(CONF_ID): cv.use_id(lv_canvas_t), cv.Required(CONF_POINTS): cv.ensure_list(point_schema), + **{cv.Optional(prop): STYLE_PROPS[prop] for prop in RECT_PROPS}, }, - ).extend({cv.Optional(prop): STYLE_PROPS[prop] for prop in RECT_PROPS}), + ), ) async def canvas_draw_polygon(config, action_id, template_arg, args): points = [ @@ -395,13 +399,15 @@ ARC_PROPS = { @automation.register_action( "lvgl.canvas.draw_arc", ObjUpdateAction, - DRAW_OPA_SCHEMA.extend( + cv.Schema( { + **DRAW_OPA_SCHEMA, cv.Required(CONF_RADIUS): pixels, cv.Required(CONF_START_ANGLE): lv_angle_degrees, cv.Required(CONF_END_ANGLE): lv_angle_degrees, + **{cv.Optional(prop): validator for prop, validator in ARC_PROPS.items()}, } - ).extend({cv.Optional(prop): validator for prop, validator in ARC_PROPS.items()}), + ), ) async def canvas_draw_arc(config, action_id, template_arg, args): radius = await size.process(config[CONF_RADIUS]) diff --git a/esphome/components/lvgl/widgets/checkbox.py b/esphome/components/lvgl/widgets/checkbox.py index c344fbfe75..ca97e2d843 100644 --- a/esphome/components/lvgl/widgets/checkbox.py +++ b/esphome/components/lvgl/widgets/checkbox.py @@ -17,11 +17,10 @@ class CheckboxType(WidgetType): CONF_CHECKBOX, LvBoolean("lv_checkbox_t"), (CONF_MAIN, CONF_INDICATOR), - TEXT_SCHEMA.extend( - { - Optional(CONF_PAD_COLUMN): padding, - } - ), + { + **TEXT_SCHEMA, + Optional(CONF_PAD_COLUMN): padding, + }, ) async def to_code(self, w: Widget, config): diff --git a/esphome/components/lvgl/widgets/container.py b/esphome/components/lvgl/widgets/container.py new file mode 100644 index 0000000000..2ac1a3b244 --- /dev/null +++ b/esphome/components/lvgl/widgets/container.py @@ -0,0 +1,39 @@ +import esphome.config_validation as cv +from esphome.const import CONF_HEIGHT, CONF_WIDTH +from esphome.cpp_generator import MockObj + +from ..defines import CONF_CONTAINER, CONF_MAIN, CONF_OBJ, CONF_SCROLLBAR +from ..lv_validation import size +from ..lvcode import lv +from ..types import WidgetType, lv_obj_t + +CONTAINER_SCHEMA = cv.Schema( + { + cv.Optional(CONF_HEIGHT, default="100%"): size, + cv.Optional(CONF_WIDTH, default="100%"): size, + } +) + + +class ContainerType(WidgetType): + """ + A simple container widget that can hold other widgets and which defaults to a 100% size. + Made from an obj with all styles removed + """ + + def __init__(self): + super().__init__( + CONF_CONTAINER, + lv_obj_t, + (CONF_MAIN, CONF_SCROLLBAR), + schema=CONTAINER_SCHEMA, + modify_schema={}, + lv_name=CONF_OBJ, + ) + self.styles = {} + + def on_create(self, var: MockObj, config: dict): + lv.obj_remove_style_all(var) + + +container_spec = ContainerType() diff --git a/esphome/components/lvgl/widgets/label.py b/esphome/components/lvgl/widgets/label.py index 6b04235674..3a3a997737 100644 --- a/esphome/components/lvgl/widgets/label.py +++ b/esphome/components/lvgl/widgets/label.py @@ -23,12 +23,11 @@ class LabelType(WidgetType): CONF_LABEL, LvText("lv_label_t"), (CONF_MAIN, CONF_SCROLLBAR, CONF_SELECTED), - TEXT_SCHEMA.extend( - { - cv.Optional(CONF_RECOLOR): lv_bool, - cv.Optional(CONF_LONG_MODE): LV_LONG_MODES.one_of, - } - ), + { + **TEXT_SCHEMA, + cv.Optional(CONF_RECOLOR): lv_bool, + cv.Optional(CONF_LONG_MODE): LV_LONG_MODES.one_of, + }, ) async def to_code(self, w: Widget, config): diff --git a/esphome/components/lvgl/widgets/qrcode.py b/esphome/components/lvgl/widgets/qrcode.py index 028a81b449..ad46f67c6b 100644 --- a/esphome/components/lvgl/widgets/qrcode.py +++ b/esphome/components/lvgl/widgets/qrcode.py @@ -14,13 +14,12 @@ CONF_QRCODE = "qrcode" CONF_DARK_COLOR = "dark_color" CONF_LIGHT_COLOR = "light_color" -QRCODE_SCHEMA = TEXT_SCHEMA.extend( - { - cv.Optional(CONF_DARK_COLOR, default="black"): lv_color, - cv.Optional(CONF_LIGHT_COLOR, default="white"): lv_color, - cv.Required(CONF_SIZE): cv.int_, - } -) +QRCODE_SCHEMA = { + **TEXT_SCHEMA, + cv.Optional(CONF_DARK_COLOR, default="black"): lv_color, + cv.Optional(CONF_LIGHT_COLOR, default="white"): lv_color, + cv.Required(CONF_SIZE): cv.int_, +} class QrCodeType(WidgetType): diff --git a/esphome/components/lvgl/widgets/textarea.py b/esphome/components/lvgl/widgets/textarea.py index 23d50b3894..e5ab884685 100644 --- a/esphome/components/lvgl/widgets/textarea.py +++ b/esphome/components/lvgl/widgets/textarea.py @@ -21,15 +21,14 @@ CONF_TEXTAREA = "textarea" lv_textarea_t = LvText("lv_textarea_t") -TEXTAREA_SCHEMA = TEXT_SCHEMA.extend( - { - cv.Optional(CONF_PLACEHOLDER_TEXT): lv_text, - cv.Optional(CONF_ACCEPTED_CHARS): lv_text, - cv.Optional(CONF_ONE_LINE): lv_bool, - cv.Optional(CONF_PASSWORD_MODE): lv_bool, - cv.Optional(CONF_MAX_LENGTH): lv_int, - } -) +TEXTAREA_SCHEMA = { + **TEXT_SCHEMA, + cv.Optional(CONF_PLACEHOLDER_TEXT): lv_text, + cv.Optional(CONF_ACCEPTED_CHARS): lv_text, + cv.Optional(CONF_ONE_LINE): lv_bool, + cv.Optional(CONF_PASSWORD_MODE): lv_bool, + cv.Optional(CONF_MAX_LENGTH): lv_int, +} class TextareaType(WidgetType): diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 14241a1669..ca669c16e4 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -113,9 +113,10 @@ lvgl: title: Messagebox bg_color: 0xffff widgets: - - label: - text: Hello Msgbox - id: msgbox_label + # Test single widget without list + label: + text: Hello Msgbox + id: msgbox_label body: text: This is a sample messagebox bg_color: 0x808080 @@ -281,7 +282,7 @@ lvgl: #endif return std::string(buf); align: top_left - - obj: + - container: align: center arc_opa: COVER arc_color: 0xFF0000 @@ -414,6 +415,7 @@ lvgl: - buttons: - id: button_e - button: + layout: 2x1 id: button_button width: 20% height: 10% @@ -430,8 +432,13 @@ lvgl: checked: bg_color: 0x000000 widgets: - - label: - text: Button + # Test parse a dict instead of list + label: + text: Button + align: bottom_right + image: + src: cat_image + align: top_left on_click: - lvgl.widget.focus: spin_up - lvgl.widget.focus: next @@ -539,6 +546,7 @@ lvgl: - logger.log: "tile 1 is now showing" tiles: - id: tile_1 + layout: vertical row: 0 column: 0 dir: ALL @@ -554,6 +562,7 @@ lvgl: bg_color: 0x000000 - id: page2 + layout: vertical widgets: - canvas: id: canvas_id @@ -1005,6 +1014,7 @@ lvgl: r_mod: -20 opa: 0% - id: page3 + layout: horizontal widgets: - keyboard: id: lv_keyboard From 3e086c2127eb51ab4c7af219cb75be5fff00bd76 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:43:27 +1000 Subject: [PATCH 376/526] [lvgl] Fix rotation with unusual width (#11680) --- esphome/components/const/__init__.py | 1 + esphome/components/lvgl/lvgl_esphome.cpp | 24 ++++++++++--------- .../components/lvgl/widgets/buttonmatrix.py | 3 ++- esphome/components/matrix_keypad/__init__.py | 3 ++- esphome/const.py | 1 - 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/esphome/components/const/__init__.py b/esphome/components/const/__init__.py index 19924f0da7..2b88bb43a8 100644 --- a/esphome/components/const/__init__.py +++ b/esphome/components/const/__init__.py @@ -11,4 +11,5 @@ CONF_DRAW_ROUNDING = "draw_rounding" CONF_ON_RECEIVE = "on_receive" CONF_ON_STATE_CHANGE = "on_state_change" CONF_REQUEST_HEADERS = "request_headers" +CONF_ROWS = "rows" CONF_USE_PSRAM = "use_psram" diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 947342089c..05005b0217 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -171,6 +171,7 @@ bool LvPageType::is_showing() const { return this->parent_->get_current_page() = void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) { auto width = lv_area_get_width(area); auto height = lv_area_get_height(area); + auto height_rounded = (height + this->draw_rounding - 1) / this->draw_rounding * this->draw_rounding; auto x1 = area->x1; auto y1 = area->y1; lv_color_t *dst = this->rotate_buf_; @@ -178,13 +179,13 @@ void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) { case display::DISPLAY_ROTATION_90_DEGREES: for (lv_coord_t x = height; x-- != 0;) { for (lv_coord_t y = 0; y != width; y++) { - dst[y * height + x] = *ptr++; + dst[y * height_rounded + x] = *ptr++; } } y1 = x1; x1 = this->disp_drv_.ver_res - area->y1 - height; - width = height; - height = lv_area_get_width(area); + height = width; + width = height_rounded; break; case display::DISPLAY_ROTATION_180_DEGREES: @@ -200,13 +201,13 @@ void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) { case display::DISPLAY_ROTATION_270_DEGREES: for (lv_coord_t x = 0; x != height; x++) { for (lv_coord_t y = width; y-- != 0;) { - dst[y * height + x] = *ptr++; + dst[y * height_rounded + x] = *ptr++; } } x1 = y1; y1 = this->disp_drv_.hor_res - area->x1 - width; - width = height; - height = lv_area_get_width(area); + height = width; + width = height_rounded; break; default: @@ -443,8 +444,10 @@ LvglComponent::LvglComponent(std::vector displays, float buf void LvglComponent::setup() { auto *display = this->displays_[0]; - auto width = display->get_width(); - auto height = display->get_height(); + auto rounding = this->draw_rounding; + // cater for displays with dimensions that don't divide by the required rounding + auto width = (display->get_width() + rounding - 1) / rounding * rounding; + auto height = (display->get_height() + rounding - 1) / rounding * rounding; auto frac = this->buffer_frac_; if (frac == 0) frac = 1; @@ -469,9 +472,8 @@ void LvglComponent::setup() { } this->buffer_frac_ = frac; lv_disp_draw_buf_init(&this->draw_buf_, buffer, nullptr, buffer_pixels); - this->disp_drv_.hor_res = width; - this->disp_drv_.ver_res = height; - // this->setup_driver_(display->get_width(), display->get_height()); + this->disp_drv_.hor_res = display->get_width(); + this->disp_drv_.ver_res = display->get_height(); lv_disp_drv_update(this->disp_, &this->disp_drv_); this->rotation = display->get_rotation(); if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) { diff --git a/esphome/components/lvgl/widgets/buttonmatrix.py b/esphome/components/lvgl/widgets/buttonmatrix.py index baeb1c8e3e..fe421aa477 100644 --- a/esphome/components/lvgl/widgets/buttonmatrix.py +++ b/esphome/components/lvgl/widgets/buttonmatrix.py @@ -1,8 +1,9 @@ from esphome import automation import esphome.codegen as cg +from esphome.components.const import CONF_ROWS from esphome.components.key_provider import KeyProvider import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_ITEMS, CONF_ROWS, CONF_TEXT, CONF_WIDTH +from esphome.const import CONF_ID, CONF_ITEMS, CONF_TEXT, CONF_WIDTH from esphome.cpp_generator import MockObj from ..automation import action_to_code diff --git a/esphome/components/matrix_keypad/__init__.py b/esphome/components/matrix_keypad/__init__.py index 2e123323a0..868b149211 100644 --- a/esphome/components/matrix_keypad/__init__.py +++ b/esphome/components/matrix_keypad/__init__.py @@ -1,8 +1,9 @@ from esphome import automation, pins import esphome.codegen as cg from esphome.components import key_provider +from esphome.components.const import CONF_ROWS import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_ON_KEY, CONF_PIN, CONF_ROWS, CONF_TRIGGER_ID +from esphome.const import CONF_ID, CONF_ON_KEY, CONF_PIN, CONF_TRIGGER_ID CODEOWNERS = ["@ssieb"] diff --git a/esphome/const.py b/esphome/const.py index 3bbc6b8b3f..d0d94ed283 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -838,7 +838,6 @@ CONF_RMT_CHANNEL = "rmt_channel" CONF_RMT_SYMBOLS = "rmt_symbols" CONF_ROTATION = "rotation" CONF_ROW = "row" -CONF_ROWS = "rows" CONF_RS_PIN = "rs_pin" CONF_RTD_NOMINAL_RESISTANCE = "rtd_nominal_resistance" CONF_RTD_WIRES = "rtd_wires" From 525790049587e58a2cc72dd06b59ba6be350f019 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Nov 2025 21:45:20 -0600 Subject: [PATCH 377/526] [mqtt] Add wake_loop_threadsafe() for low-latency event processing on ESP32 (#11695) --- esphome/components/mqtt/__init__.py | 12 +++++++++--- esphome/components/mqtt/mqtt_backend_esp32.cpp | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 641c70a367..1fc0c30db1 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -3,7 +3,7 @@ import re from esphome import automation from esphome.automation import Condition import esphome.codegen as cg -from esphome.components import logger +from esphome.components import logger, socket from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv @@ -66,6 +66,9 @@ DEPENDENCIES = ["network"] def AUTO_LOAD(): if CORE.is_esp8266 or CORE.is_libretiny: return ["async_tcp", "json"] + # ESP32 needs socket for wake_loop_threadsafe() + if CORE.is_esp32: + return ["json", "socket"] return ["json"] @@ -213,8 +216,6 @@ def validate_fingerprint(value): def _consume_mqtt_sockets(config: ConfigType) -> ConfigType: """Register socket needs for MQTT component.""" - from esphome.components import socket - # MQTT needs 1 socket for the broker connection socket.consume_sockets(1, "mqtt")(config) return config @@ -341,6 +342,11 @@ async def to_code(config): # https://github.com/heman/async-mqtt-client/blob/master/library.json cg.add_library("heman/AsyncMqttClient-esphome", "2.0.0") + # MQTT on ESP32 uses wake_loop_threadsafe() to wake the main loop from the MQTT event handler + # This enables low-latency MQTT event processing instead of waiting for select() timeout + if CORE.is_esp32: + socket.require_wake_loop_threadsafe() + cg.add_define("USE_MQTT") cg.add_global(mqtt_ns.using) diff --git a/esphome/components/mqtt/mqtt_backend_esp32.cpp b/esphome/components/mqtt/mqtt_backend_esp32.cpp index 623206a0cd..dcc51ed60e 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.cpp +++ b/esphome/components/mqtt/mqtt_backend_esp32.cpp @@ -190,6 +190,11 @@ void MQTTBackendESP32::mqtt_event_handler(void *handler_args, esp_event_base_t b if (instance) { auto event = *static_cast(event_data); instance->mqtt_events_.emplace(event); + + // Wake main loop immediately to process MQTT event instead of waiting for select() timeout +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + App.wake_loop_threadsafe(); +#endif } } From 4c31cb57eaf1d1411c071ef74c190fffdb25be04 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Nov 2025 21:45:57 -0600 Subject: [PATCH 378/526] [espnow] Add wake_loop_threadsafe() for low-latency event processing (#11696) --- esphome/components/espnow/__init__.py | 7 ++++++- esphome/components/espnow/espnow_component.cpp | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/esphome/components/espnow/__init__.py b/esphome/components/espnow/__init__.py index 9d2f17440c..cc2c02d4c0 100644 --- a/esphome/components/espnow/__init__.py +++ b/esphome/components/espnow/__init__.py @@ -1,6 +1,6 @@ from esphome import automation, core import esphome.codegen as cg -from esphome.components import wifi +from esphome.components import socket, wifi from esphome.components.udp import CONF_ON_RECEIVE import esphome.config_validation as cv from esphome.const import ( @@ -17,6 +17,7 @@ from esphome.core import CORE, HexInt from esphome.types import ConfigType CODEOWNERS = ["@jesserockz"] +AUTO_LOAD = ["socket"] byte_vector = cg.std_vector.template(cg.uint8) peer_address_t = cg.std_ns.class_("array").template(cg.uint8, 6) @@ -120,6 +121,10 @@ async def to_code(config): if CORE.using_arduino: cg.add_library("WiFi", None) + # ESP-NOW uses wake_loop_threadsafe() to wake the main loop from ESP-NOW callbacks + # This enables low-latency event processing instead of waiting for select() timeout + socket.require_wake_loop_threadsafe() + cg.add_define("USE_ESPNOW") if wifi_channel := config.get(CONF_CHANNEL): cg.add(var.set_wifi_channel(wifi_channel)) diff --git a/esphome/components/espnow/espnow_component.cpp b/esphome/components/espnow/espnow_component.cpp index b0d5938dba..d2f136d1c7 100644 --- a/esphome/components/espnow/espnow_component.cpp +++ b/esphome/components/espnow/espnow_component.cpp @@ -4,6 +4,7 @@ #include "espnow_err.h" +#include "esphome/core/application.h" #include "esphome/core/defines.h" #include "esphome/core/log.h" @@ -97,6 +98,11 @@ void on_send_report(const uint8_t *mac_addr, esp_now_send_status_t status) // Push the packet to the queue global_esp_now->receive_packet_queue_.push(packet); // Push always because we're the only producer and the pool ensures we never exceed queue size + + // Wake main loop immediately to process ESP-NOW send event instead of waiting for select() timeout +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + App.wake_loop_threadsafe(); +#endif } void on_data_received(const esp_now_recv_info_t *info, const uint8_t *data, int size) { @@ -114,6 +120,11 @@ void on_data_received(const esp_now_recv_info_t *info, const uint8_t *data, int // Push the packet to the queue global_esp_now->receive_packet_queue_.push(packet); // Push always because we're the only producer and the pool ensures we never exceed queue size + + // Wake main loop immediately to process ESP-NOW receive event instead of waiting for select() timeout +#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) + App.wake_loop_threadsafe(); +#endif } ESPNowComponent::ESPNowComponent() { global_esp_now = this; } From 4d2f9db8617d542021ed20aa2fea993e8ffeb384 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Nov 2025 21:46:34 -0600 Subject: [PATCH 379/526] [esp32_ble] Remove leftover lwip/sockets.h include (#11702) --- esphome/components/esp32_ble/ble.cpp | 4 ---- esphome/components/esp32_ble/ble.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index fc26a7fc21..8bbb21e3ca 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -27,10 +27,6 @@ extern "C" { #include #endif -#ifdef USE_SOCKET_SELECT_SUPPORT -#include -#endif - namespace esphome::esp32_ble { static const char *const TAG = "esp32_ble"; diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 3be6a7048d..c3e1ec2ce6 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -25,10 +25,6 @@ #include #include -#ifdef USE_SOCKET_SELECT_SUPPORT -#include -#endif - namespace esphome::esp32_ble { // Maximum size of the BLE event queue From 980098ca77dfe249da4d45943ddf5488d4532ae2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Nov 2025 21:47:11 -0600 Subject: [PATCH 380/526] [ci] Fix non-component files incorrectly detected as components (#11701) --- script/helpers.py | 6 +++++- tests/script/test_helpers.py | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/script/helpers.py b/script/helpers.py index 447d54fa54..33f95d6f8a 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -101,7 +101,11 @@ def get_component_from_path(file_path: str) -> str | None: ): parts = file_path.split("/") if len(parts) >= 3 and parts[2]: - return parts[2] + # Verify that parts[2] is actually a component directory, not a file + # like .gitignore or README.md in the components directory itself + component_name = parts[2] + if "." not in component_name: + return component_name return None diff --git a/tests/script/test_helpers.py b/tests/script/test_helpers.py index 1046512a14..5eb55c0722 100644 --- a/tests/script/test_helpers.py +++ b/tests/script/test_helpers.py @@ -1093,6 +1093,11 @@ def test_parse_list_components_output(output: str, expected: list[str]) -> None: ("tests/components/", None), # No component name ("esphome/components", None), # No trailing slash ("tests/components", None), # No trailing slash + # Files in component directories that are not components + ("tests/components/.gitignore", None), # Hidden file + ("tests/components/README.md", None), # Documentation file + ("esphome/components/__init__.py", None), # Python init file + ("tests/components/main.cpp", None), # File with extension ], ) def test_get_component_from_path( From 060bb4159f6c638d3ce03a1e3169607a751ec1d1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 3 Nov 2025 22:38:57 -0600 Subject: [PATCH 381/526] [ci] Cache component dependency graph for up to 3.4x faster determine-jobs (#11648) --- .github/workflows/ci.yml | 11 ++ script/helpers.py | 71 +++++++- tests/script/test_determine_jobs.py | 2 + tests/script/test_helpers.py | 260 ++++++++++++++++++++++++++++ 4 files changed, 341 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1756d5b765..16837b3186 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -192,6 +192,11 @@ jobs: with: python-version: ${{ env.DEFAULT_PYTHON }} cache-key: ${{ needs.common.outputs.cache-key }} + - name: Restore components graph cache + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: .temp/components_graph.json + key: components-graph-${{ hashFiles('esphome/components/**/*.py') }} - name: Determine which tests to run id: determine env: @@ -216,6 +221,12 @@ jobs: echo "cpp-unit-tests-run-all=$(echo "$output" | jq -r '.cpp_unit_tests_run_all')" >> $GITHUB_OUTPUT echo "cpp-unit-tests-components=$(echo "$output" | jq -c '.cpp_unit_tests_components')" >> $GITHUB_OUTPUT echo "component-test-batches=$(echo "$output" | jq -c '.component_test_batches')" >> $GITHUB_OUTPUT + - name: Save components graph cache + if: github.ref == 'refs/heads/dev' + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: .temp/components_graph.json + key: components-graph-${{ hashFiles('esphome/components/**/*.py') }} integration-tests: name: Run integration tests diff --git a/script/helpers.py b/script/helpers.py index 33f95d6f8a..5b2fe6cd06 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -2,6 +2,7 @@ from __future__ import annotations from collections.abc import Callable from functools import cache +import hashlib import json import os import os.path @@ -52,6 +53,10 @@ BASE_BUS_COMPONENTS = { "remote_receiver", } +# Cache version for components graph +# Increment this when the cache format or graph building logic changes +COMPONENTS_GRAPH_CACHE_VERSION = 1 + def parse_list_components_output(output: str) -> list[str]: """Parse the output from list-components.py script. @@ -756,20 +761,71 @@ def resolve_auto_load( return auto_load() +@cache +def get_components_graph_cache_key() -> str: + """Generate cache key based on all component Python file hashes. + + Uses git ls-files with sha1 hashes to generate a stable cache key that works + across different machines and CI runs. This is faster and more reliable than + reading file contents or using modification times. + + Returns: + SHA256 hex string uniquely identifying the current component state + """ + + # Use git ls-files -s to get sha1 hashes of all component Python files + # Format: + # This is fast and works consistently across CI and local dev + # We hash all .py files because AUTO_LOAD, DEPENDENCIES, etc. can be defined + # in any Python file, not just __init__.py + cmd = ["git", "ls-files", "-s", "esphome/components/**/*.py"] + result = subprocess.run( + cmd, capture_output=True, text=True, check=True, cwd=root_path, close_fds=False + ) + + # Hash the git output (includes file paths and their sha1 hashes) + # This changes only when component Python files actually change + hasher = hashlib.sha256() + hasher.update(result.stdout.encode()) + + return hasher.hexdigest() + + def create_components_graph() -> dict[str, list[str]]: - """Create a graph of component dependencies. + """Create a graph of component dependencies (cached). + + This function is expensive (5-6 seconds) because it imports all ESPHome components + to extract their DEPENDENCIES and AUTO_LOAD metadata. The result is cached based + on component file modification times, so unchanged components don't trigger a rebuild. Returns: Dictionary mapping parent components to their children (dependencies) """ - from pathlib import Path + # Check cache first - use fixed filename since GitHub Actions cache doesn't support wildcards + cache_file = Path(temp_folder) / "components_graph.json" + + if cache_file.exists(): + try: + cached_data = json.loads(cache_file.read_text()) + except (OSError, json.JSONDecodeError): + # Cache file corrupted or unreadable, rebuild + pass + else: + # Verify cache version matches + if cached_data.get("_version") == COMPONENTS_GRAPH_CACHE_VERSION: + # Verify cache is for current component state + cache_key = get_components_graph_cache_key() + if cached_data.get("_cache_key") == cache_key: + return cached_data.get("graph", {}) + # Cache key mismatch - stale cache, rebuild + # Cache version mismatch - incompatible format, rebuild from esphome import const from esphome.core import CORE from esphome.loader import ComponentManifest, get_component, get_platform # The root directory of the repo - root = Path(__file__).parent.parent + root = Path(root_path) components_dir = root / ESPHOME_COMPONENTS_PATH # Fake some directory so that get_component works CORE.config_path = root @@ -846,6 +902,15 @@ def create_components_graph() -> dict[str, list[str]]: # restore config CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] + # Save to cache with version and cache key for validation + cache_data = { + "_version": COMPONENTS_GRAPH_CACHE_VERSION, + "_cache_key": get_components_graph_cache_key(), + "graph": components_graph, + } + cache_file.parent.mkdir(exist_ok=True) + cache_file.write_text(json.dumps(cache_data)) + return components_graph diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index a33eca5b19..e084e2e398 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -543,6 +543,7 @@ def test_main_filters_components_without_tests( with ( patch.object(determine_jobs, "root_path", str(tmp_path)), patch.object(helpers, "root_path", str(tmp_path)), + patch.object(helpers, "create_components_graph", return_value={}), patch("sys.argv", ["determine-jobs.py"]), patch.object( determine_jobs, @@ -640,6 +641,7 @@ def test_main_detects_components_with_variant_tests( with ( patch.object(determine_jobs, "root_path", str(tmp_path)), patch.object(helpers, "root_path", str(tmp_path)), + patch.object(helpers, "create_components_graph", return_value={}), patch("sys.argv", ["determine-jobs.py"]), patch.object( determine_jobs, diff --git a/tests/script/test_helpers.py b/tests/script/test_helpers.py index 5eb55c0722..1bfffef51c 100644 --- a/tests/script/test_helpers.py +++ b/tests/script/test_helpers.py @@ -1,5 +1,6 @@ """Unit tests for script/helpers.py module.""" +from collections.abc import Generator import json import os from pathlib import Path @@ -1106,3 +1107,262 @@ def test_get_component_from_path( """Test extraction of component names from file paths.""" result = helpers.get_component_from_path(file_path) assert result == expected_component + + +# Components graph cache tests + + +@pytest.fixture +def mock_git_output() -> str: + """Fixture for mock git ls-files output with realistic component files. + + Includes examples of AUTO_LOAD in sensor.py and binary_sensor.py files, + which is why we need to hash all .py files, not just __init__.py. + """ + return ( + "100644 abc123... 0 esphome/components/wifi/__init__.py\n" + "100644 def456... 0 esphome/components/api/__init__.py\n" + "100644 ghi789... 0 esphome/components/xiaomi_lywsd03mmc/__init__.py\n" + "100644 jkl012... 0 esphome/components/xiaomi_lywsd03mmc/sensor.py\n" + "100644 mno345... 0 esphome/components/xiaomi_cgpr1/__init__.py\n" + "100644 pqr678... 0 esphome/components/xiaomi_cgpr1/binary_sensor.py\n" + ) + + +@pytest.fixture +def mock_cache_file(tmp_path: Path) -> Path: + """Fixture for a temporary cache file path.""" + return tmp_path / "components_graph.json" + + +@pytest.fixture(autouse=True) +def clear_cache_key_cache() -> None: + """Clear the components graph cache key cache before each test.""" + helpers.get_components_graph_cache_key.cache_clear() + + +@pytest.fixture +def mock_subprocess_run() -> Generator[Mock, None, None]: + """Fixture to mock subprocess.run for git commands.""" + with patch("subprocess.run") as mock_run: + yield mock_run + + +def test_cache_key_generation(mock_git_output: str, mock_subprocess_run: Mock) -> None: + """Test that cache key is generated based on git file hashes.""" + mock_result = Mock() + mock_result.stdout = mock_git_output + mock_subprocess_run.return_value = mock_result + + key = helpers.get_components_graph_cache_key() + + # Should be a 64-character hex string (SHA256) + assert len(key) == 64 + assert all(c in "0123456789abcdef" for c in key) + + +def test_cache_key_consistent_for_same_files( + mock_git_output: str, mock_subprocess_run: Mock +) -> None: + """Test that same git output produces same cache key.""" + mock_result = Mock() + mock_result.stdout = mock_git_output + mock_subprocess_run.return_value = mock_result + + key1 = helpers.get_components_graph_cache_key() + key2 = helpers.get_components_graph_cache_key() + + assert key1 == key2 + + +def test_cache_key_different_for_changed_files(mock_subprocess_run: Mock) -> None: + """Test that different git output produces different cache key. + + This test demonstrates that changes to any .py file (not just __init__.py) + will invalidate the cache, which is important because AUTO_LOAD can be + defined in sensor.py, binary_sensor.py, etc. + """ + mock_result1 = Mock() + mock_result1.stdout = ( + "100644 abc123... 0 esphome/components/xiaomi_lywsd03mmc/sensor.py\n" + ) + + mock_result2 = Mock() + # Same file, different hash - simulates a change to AUTO_LOAD + mock_result2.stdout = ( + "100644 xyz789... 0 esphome/components/xiaomi_lywsd03mmc/sensor.py\n" + ) + + mock_subprocess_run.return_value = mock_result1 + key1 = helpers.get_components_graph_cache_key() + + helpers.get_components_graph_cache_key.cache_clear() + mock_subprocess_run.return_value = mock_result2 + key2 = helpers.get_components_graph_cache_key() + + assert key1 != key2 + + +def test_cache_key_uses_git_ls_files( + mock_git_output: str, mock_subprocess_run: Mock +) -> None: + """Test that git ls-files command is called correctly.""" + mock_result = Mock() + mock_result.stdout = mock_git_output + mock_subprocess_run.return_value = mock_result + + helpers.get_components_graph_cache_key() + + # Verify git ls-files was called with correct arguments + mock_subprocess_run.assert_called_once() + call_args = mock_subprocess_run.call_args + assert call_args[0][0] == [ + "git", + "ls-files", + "-s", + "esphome/components/**/*.py", + ] + assert call_args[1]["capture_output"] is True + assert call_args[1]["text"] is True + assert call_args[1]["check"] is True + assert call_args[1]["close_fds"] is False + + +def test_cache_hit_returns_cached_graph( + tmp_path: Path, mock_git_output: str, mock_subprocess_run: Mock +) -> None: + """Test that cache hit returns cached data without rebuilding.""" + mock_graph = {"wifi": ["network"], "api": ["socket"]} + cache_key = "a" * 64 + cache_data = { + "_version": helpers.COMPONENTS_GRAPH_CACHE_VERSION, + "_cache_key": cache_key, + "graph": mock_graph, + } + + # Write cache file + cache_file = tmp_path / "components_graph.json" + cache_file.write_text(json.dumps(cache_data)) + + mock_result = Mock() + mock_result.stdout = mock_git_output + mock_subprocess_run.return_value = mock_result + + with ( + patch("helpers.get_components_graph_cache_key", return_value=cache_key), + patch("helpers.temp_folder", str(tmp_path)), + ): + result = helpers.create_components_graph() + assert result == mock_graph + + +def test_cache_miss_no_cache_file( + tmp_path: Path, mock_git_output: str, mock_subprocess_run: Mock +) -> None: + """Test that cache miss rebuilds graph when no cache file exists.""" + mock_result = Mock() + mock_result.stdout = mock_git_output + mock_subprocess_run.return_value = mock_result + + # Create minimal components directory structure + components_dir = tmp_path / "esphome" / "components" + components_dir.mkdir(parents=True) + + with ( + patch("helpers.root_path", str(tmp_path)), + patch("helpers.temp_folder", str(tmp_path / ".temp")), + patch("helpers.get_components_graph_cache_key", return_value="test_key"), + ): + result = helpers.create_components_graph() + # Should return empty graph for empty components directory + assert result == {} + + +def test_cache_miss_version_mismatch( + tmp_path: Path, mock_git_output: str, mock_subprocess_run: Mock +) -> None: + """Test that cache miss rebuilds graph when version doesn't match.""" + cache_data = { + "_version": 999, # Wrong version + "_cache_key": "test_key", + "graph": {"old": ["data"]}, + } + + cache_file = tmp_path / ".temp" / "components_graph.json" + cache_file.parent.mkdir(parents=True) + cache_file.write_text(json.dumps(cache_data)) + + mock_result = Mock() + mock_result.stdout = mock_git_output + mock_subprocess_run.return_value = mock_result + + # Create minimal components directory structure + components_dir = tmp_path / "esphome" / "components" + components_dir.mkdir(parents=True) + + with ( + patch("helpers.root_path", str(tmp_path)), + patch("helpers.temp_folder", str(tmp_path / ".temp")), + patch("helpers.get_components_graph_cache_key", return_value="test_key"), + ): + result = helpers.create_components_graph() + # Should rebuild and return empty graph, not use cached data + assert result == {} + + +def test_cache_miss_key_mismatch( + tmp_path: Path, mock_git_output: str, mock_subprocess_run: Mock +) -> None: + """Test that cache miss rebuilds graph when cache key doesn't match.""" + cache_data = { + "_version": helpers.COMPONENTS_GRAPH_CACHE_VERSION, + "_cache_key": "old_key", + "graph": {"old": ["data"]}, + } + + cache_file = tmp_path / ".temp" / "components_graph.json" + cache_file.parent.mkdir(parents=True) + cache_file.write_text(json.dumps(cache_data)) + + mock_result = Mock() + mock_result.stdout = mock_git_output + mock_subprocess_run.return_value = mock_result + + # Create minimal components directory structure + components_dir = tmp_path / "esphome" / "components" + components_dir.mkdir(parents=True) + + with ( + patch("helpers.root_path", str(tmp_path)), + patch("helpers.temp_folder", str(tmp_path / ".temp")), + patch("helpers.get_components_graph_cache_key", return_value="new_key"), + ): + result = helpers.create_components_graph() + # Should rebuild and return empty graph, not use cached data with old key + assert result == {} + + +def test_cache_miss_corrupted_json( + tmp_path: Path, mock_git_output: str, mock_subprocess_run: Mock +) -> None: + """Test that cache miss rebuilds graph when cache file has invalid JSON.""" + cache_file = tmp_path / ".temp" / "components_graph.json" + cache_file.parent.mkdir(parents=True) + cache_file.write_text("{invalid json") + + mock_result = Mock() + mock_result.stdout = mock_git_output + mock_subprocess_run.return_value = mock_result + + # Create minimal components directory structure + components_dir = tmp_path / "esphome" / "components" + components_dir.mkdir(parents=True) + + with ( + patch("helpers.root_path", str(tmp_path)), + patch("helpers.temp_folder", str(tmp_path / ".temp")), + patch("helpers.get_components_graph_cache_key", return_value="test_key"), + ): + result = helpers.create_components_graph() + # Should handle corruption gracefully and rebuild + assert result == {} From 13e3c03a6144ea215612640e43784080af5634f1 Mon Sep 17 00:00:00 2001 From: leejoow Date: Tue, 4 Nov 2025 07:30:53 +0100 Subject: [PATCH 382/526] [dallas_temp] add support for index (#11346) --- esphome/components/dallas_temp/dallas_temp.cpp | 2 +- esphome/components/one_wire/__init__.py | 7 +++++-- esphome/components/one_wire/one_wire.cpp | 12 +++++++++++- esphome/components/one_wire/one_wire.h | 7 ++++++- tests/components/dallas_temp/common.yaml | 3 +++ 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/esphome/components/dallas_temp/dallas_temp.cpp b/esphome/components/dallas_temp/dallas_temp.cpp index a518c96489..a3969e081e 100644 --- a/esphome/components/dallas_temp/dallas_temp.cpp +++ b/esphome/components/dallas_temp/dallas_temp.cpp @@ -70,7 +70,7 @@ bool DallasTemperatureSensor::read_scratch_pad_() { } void DallasTemperatureSensor::setup() { - if (!this->check_address_()) + if (!this->check_address_or_index_()) return; if (!this->read_scratch_pad_()) return; diff --git a/esphome/components/one_wire/__init__.py b/esphome/components/one_wire/__init__.py index 6d95b8fd33..e12cca3e27 100644 --- a/esphome/components/one_wire/__init__.py +++ b/esphome/components/one_wire/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ADDRESS +from esphome.const import CONF_ADDRESS, CONF_INDEX CODEOWNERS = ["@ssieb"] @@ -21,7 +21,8 @@ def one_wire_device_schema(): return cv.Schema( { cv.GenerateID(CONF_ONE_WIRE_ID): cv.use_id(OneWireBus), - cv.Optional(CONF_ADDRESS): cv.hex_uint64_t, + cv.Exclusive(CONF_ADDRESS, "index_or_address"): cv.hex_uint64_t, + cv.Exclusive(CONF_INDEX, "index_or_address"): cv.uint8_t, } ) @@ -37,3 +38,5 @@ async def register_one_wire_device(var, config): cg.add(var.set_one_wire_bus(parent)) if (address := config.get(CONF_ADDRESS)) is not None: cg.add(var.set_address(address)) + if (index := config.get(CONF_INDEX)) is not None: + cg.add(var.set_index(index)) diff --git a/esphome/components/one_wire/one_wire.cpp b/esphome/components/one_wire/one_wire.cpp index 96e6145f63..fd139d0ddc 100644 --- a/esphome/components/one_wire/one_wire.cpp +++ b/esphome/components/one_wire/one_wire.cpp @@ -18,10 +18,20 @@ bool OneWireDevice::send_command_(uint8_t cmd) { return true; } -bool OneWireDevice::check_address_() { +bool OneWireDevice::check_address_or_index_() { if (this->address_ != 0) return true; auto devices = this->bus_->get_devices(); + + if (this->index_ != INDEX_NOT_SET) { + if (this->index_ >= devices.size()) { + ESP_LOGE(TAG, "Index %d out of range, only %d devices found", this->index_, devices.size()); + return false; + } + this->address_ = devices[this->index_]; + return true; + } + if (devices.empty()) { ESP_LOGE(TAG, "No devices, can't auto-select address"); return false; diff --git a/esphome/components/one_wire/one_wire.h b/esphome/components/one_wire/one_wire.h index e83c6e81e8..f6a956a92c 100644 --- a/esphome/components/one_wire/one_wire.h +++ b/esphome/components/one_wire/one_wire.h @@ -17,6 +17,8 @@ class OneWireDevice { /// @param address of the device void set_address(uint64_t address) { this->address_ = address; } + void set_index(uint8_t index) { this->index_ = index; } + /// @brief store the pointer to the OneWireBus to use /// @param bus pointer to the OneWireBus object void set_one_wire_bus(OneWireBus *bus) { this->bus_ = bus; } @@ -25,13 +27,16 @@ class OneWireDevice { const std::string &get_address_name(); protected: + static constexpr uint8_t INDEX_NOT_SET = 255; + uint64_t address_{0}; + uint8_t index_{INDEX_NOT_SET}; OneWireBus *bus_{nullptr}; ///< pointer to OneWireBus instance std::string address_name_; /// @brief find an address if necessary /// should be called from setup - bool check_address_(); + bool check_address_or_index_(); /// @brief send command on the bus /// @param cmd command to send diff --git a/tests/components/dallas_temp/common.yaml b/tests/components/dallas_temp/common.yaml index 6d865d8e93..abd8e0cfa3 100644 --- a/tests/components/dallas_temp/common.yaml +++ b/tests/components/dallas_temp/common.yaml @@ -9,3 +9,6 @@ sensor: resolution: 9 - platform: dallas_temp name: Dallas Temperature 2 + - platform: dallas_temp + name: Dallas Temperature 3 + index: 2 From 84f7cacef95ae53bc1a993914453abffe35406e6 Mon Sep 17 00:00:00 2001 From: Chaser Huang Date: Tue, 4 Nov 2025 10:41:30 -0500 Subject: [PATCH 383/526] [sgp30] Fix reading from preexisting stored baseline even with `store_baseline:false` (#7922) Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/sgp30/sgp30.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 0e3aeff812..9e8d6b332c 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -78,7 +78,7 @@ void SGP30Component::setup() { uint32_t hash = fnv1_hash(App.get_compilation_time() + std::to_string(this->serial_number_)); this->pref_ = global_preferences->make_preference(hash, true); - if (this->pref_.load(&this->baselines_storage_)) { + if (this->store_baseline_ && this->pref_.load(&this->baselines_storage_)) { ESP_LOGI(TAG, "Loaded eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", this->baselines_storage_.eco2, baselines_storage_.tvoc); this->eco2_baseline_ = this->baselines_storage_.eco2; From 71fa88c9d41efe5a3fbf179e28b8a2aa2584e9a2 Mon Sep 17 00:00:00 2001 From: Cameron Steel Date: Wed, 5 Nov 2025 03:32:23 +1100 Subject: [PATCH 384/526] =?UTF-8?q?[max7219digit]=20support=20`flip=5Fx`?= =?UTF-8?q?=20when=20`rotate=5Fchip`=20is=2090=C2=B0=20or=20270=C2=B0=20(#?= =?UTF-8?q?6109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/max7219digit/max7219digit.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/esphome/components/max7219digit/max7219digit.cpp b/esphome/components/max7219digit/max7219digit.cpp index 6df3c4d7c8..cdceafad50 100644 --- a/esphome/components/max7219digit/max7219digit.cpp +++ b/esphome/components/max7219digit/max7219digit.cpp @@ -271,7 +271,11 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) { } } } else if (this->orientation_ == 1) { - b = pixels[col]; + if (this->flip_x_) { + b = pixels[7 - col]; + } else { + b = pixels[col]; + } } else if (this->orientation_ == 2) { for (uint8_t i = 0; i < 8; i++) { if (this->flip_x_) { @@ -282,7 +286,11 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) { } } else { for (uint8_t i = 0; i < 8; i++) { - b |= ((pixels[7 - col] >> i) & 1) << (7 - i); + if (this->flip_x_) { + b |= ((pixels[col] >> i) & 1) << (7 - i); + } else { + b |= ((pixels[7 - col] >> i) & 1) << (7 - i); + } } } // send this byte to display at selected chip From 968df6cb3fd625a87118e4d72ea2970641e9efb9 Mon Sep 17 00:00:00 2001 From: SeByDocKy Date: Tue, 4 Nov 2025 18:16:11 +0100 Subject: [PATCH 385/526] [gp8403] Add gp8413 (15 bits) DAC model (#7726) Co-authored-by: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- CODEOWNERS | 2 +- esphome/components/gp8403/__init__.py | 17 +++++--- esphome/components/gp8403/gp8403.cpp | 40 +++++++++++++++++-- esphome/components/gp8403/gp8403.h | 13 +++++- esphome/components/gp8403/output/__init__.py | 4 +- .../gp8403/output/gp8403_output.cpp | 10 +---- .../components/gp8403/output/gp8403_output.h | 4 +- tests/components/gp8403/common.yaml | 9 +++++ 8 files changed, 73 insertions(+), 26 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index b8a4df6a85..4d458eceb8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -181,7 +181,7 @@ esphome/components/gdk101/* @Szewcson esphome/components/gl_r01_i2c/* @pkejval esphome/components/globals/* @esphome/core esphome/components/gp2y1010au0f/* @zry98 -esphome/components/gp8403/* @jesserockz +esphome/components/gp8403/* @jesserockz @sebydocky esphome/components/gpio/* @esphome/core esphome/components/gpio/one_wire/* @ssieb esphome/components/gps/* @coogle @ximex diff --git a/esphome/components/gp8403/__init__.py b/esphome/components/gp8403/__init__.py index 96f1807688..83859a4030 100644 --- a/esphome/components/gp8403/__init__.py +++ b/esphome/components/gp8403/__init__.py @@ -1,19 +1,25 @@ import esphome.codegen as cg from esphome.components import i2c import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_VOLTAGE +from esphome.const import CONF_ID, CONF_MODEL, CONF_VOLTAGE -CODEOWNERS = ["@jesserockz"] +CODEOWNERS = ["@jesserockz", "@sebydocky"] DEPENDENCIES = ["i2c"] MULTI_CONF = True gp8403_ns = cg.esphome_ns.namespace("gp8403") -GP8403 = gp8403_ns.class_("GP8403", cg.Component, i2c.I2CDevice) +GP8403Component = gp8403_ns.class_("GP8403Component", cg.Component, i2c.I2CDevice) GP8403Voltage = gp8403_ns.enum("GP8403Voltage") +GP8403Model = gp8403_ns.enum("GP8403Model") CONF_GP8403_ID = "gp8403_id" +MODELS = { + "GP8403": GP8403Model.GP8403, + "GP8413": GP8403Model.GP8413, +} + VOLTAGES = { "5V": GP8403Voltage.GP8403_VOLTAGE_5V, "10V": GP8403Voltage.GP8403_VOLTAGE_10V, @@ -22,7 +28,8 @@ VOLTAGES = { CONFIG_SCHEMA = ( cv.Schema( { - cv.GenerateID(): cv.declare_id(GP8403), + cv.GenerateID(): cv.declare_id(GP8403Component), + cv.Optional(CONF_MODEL, default="GP8403"): cv.enum(MODELS, upper=True), cv.Required(CONF_VOLTAGE): cv.enum(VOLTAGES, upper=True), } ) @@ -35,5 +42,5 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await i2c.register_i2c_device(var, config) - + cg.add(var.set_model(config[CONF_MODEL])) cg.add(var.set_voltage(config[CONF_VOLTAGE])) diff --git a/esphome/components/gp8403/gp8403.cpp b/esphome/components/gp8403/gp8403.cpp index 5107e96dee..11c2f9a7c0 100644 --- a/esphome/components/gp8403/gp8403.cpp +++ b/esphome/components/gp8403/gp8403.cpp @@ -8,16 +8,48 @@ namespace gp8403 { static const char *const TAG = "gp8403"; static const uint8_t RANGE_REGISTER = 0x01; +static const uint8_t OUTPUT_REGISTER = 0x02; -void GP8403::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); } +const LogString *model_to_string(GP8403Model model) { + switch (model) { + case GP8403Model::GP8403: + return LOG_STR("GP8403"); + case GP8403Model::GP8413: + return LOG_STR("GP8413"); + } + return LOG_STR("Unknown"); +}; -void GP8403::dump_config() { +void GP8403Component::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); } + +void GP8403Component::dump_config() { ESP_LOGCONFIG(TAG, "GP8403:\n" - " Voltage: %dV", - this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10); + " Voltage: %dV\n" + " Model: %s", + this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10, LOG_STR_ARG(model_to_string(this->model_))); LOG_I2C_DEVICE(this); } +void GP8403Component::write_state(float state, uint8_t channel) { + uint16_t val = 0; + switch (this->model_) { + case GP8403Model::GP8403: + val = ((uint16_t) (4095 * state)) << 4; + break; + case GP8403Model::GP8413: + val = ((uint16_t) (32767 * state)) << 1; + break; + default: + ESP_LOGE(TAG, "Unknown model %s", LOG_STR_ARG(model_to_string(this->model_))); + return; + } + ESP_LOGV(TAG, "Calculated DAC value: %" PRIu16, val); + i2c::ErrorCode err = this->write_register(OUTPUT_REGISTER + (2 * channel), (uint8_t *) &val, 2); + if (err != i2c::ERROR_OK) { + ESP_LOGE(TAG, "Error writing to %s, code %d", LOG_STR_ARG(model_to_string(this->model_)), err); + } +} + } // namespace gp8403 } // namespace esphome diff --git a/esphome/components/gp8403/gp8403.h b/esphome/components/gp8403/gp8403.h index 9f493d39e3..6613187b20 100644 --- a/esphome/components/gp8403/gp8403.h +++ b/esphome/components/gp8403/gp8403.h @@ -11,15 +11,24 @@ enum GP8403Voltage { GP8403_VOLTAGE_10V = 0x11, }; -class GP8403 : public Component, public i2c::I2CDevice { +enum GP8403Model { + GP8403, + GP8413, +}; + +class GP8403Component : public Component, public i2c::I2CDevice { public: void setup() override; void dump_config() override; - + float get_setup_priority() const override { return setup_priority::DATA; } + void set_model(GP8403Model model) { this->model_ = model; } void set_voltage(gp8403::GP8403Voltage voltage) { this->voltage_ = voltage; } + void write_state(float state, uint8_t channel); + protected: GP8403Voltage voltage_; + GP8403Model model_{GP8403Model::GP8403}; }; } // namespace gp8403 diff --git a/esphome/components/gp8403/output/__init__.py b/esphome/components/gp8403/output/__init__.py index dc57833f4a..5245c405db 100644 --- a/esphome/components/gp8403/output/__init__.py +++ b/esphome/components/gp8403/output/__init__.py @@ -3,7 +3,7 @@ from esphome.components import i2c, output import esphome.config_validation as cv from esphome.const import CONF_CHANNEL, CONF_ID -from .. import CONF_GP8403_ID, GP8403, gp8403_ns +from .. import CONF_GP8403_ID, GP8403Component, gp8403_ns DEPENDENCIES = ["gp8403"] @@ -14,7 +14,7 @@ GP8403Output = gp8403_ns.class_( CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(GP8403Output), - cv.GenerateID(CONF_GP8403_ID): cv.use_id(GP8403), + cv.GenerateID(CONF_GP8403_ID): cv.use_id(GP8403Component), cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=1), } ).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/gp8403/output/gp8403_output.cpp b/esphome/components/gp8403/output/gp8403_output.cpp index edb6972184..dfdc2d6ccb 100644 --- a/esphome/components/gp8403/output/gp8403_output.cpp +++ b/esphome/components/gp8403/output/gp8403_output.cpp @@ -7,8 +7,6 @@ namespace gp8403 { static const char *const TAG = "gp8403.output"; -static const uint8_t OUTPUT_REGISTER = 0x02; - void GP8403Output::dump_config() { ESP_LOGCONFIG(TAG, "GP8403 Output:\n" @@ -16,13 +14,7 @@ void GP8403Output::dump_config() { this->channel_); } -void GP8403Output::write_state(float state) { - uint16_t value = ((uint16_t) (state * 4095)) << 4; - i2c::ErrorCode err = this->parent_->write_register(OUTPUT_REGISTER + (2 * this->channel_), (uint8_t *) &value, 2); - if (err != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Error writing to GP8403, code %d", err); - } -} +void GP8403Output::write_state(float state) { this->parent_->write_state(state, this->channel_); } } // namespace gp8403 } // namespace esphome diff --git a/esphome/components/gp8403/output/gp8403_output.h b/esphome/components/gp8403/output/gp8403_output.h index 71e5efb1cb..c0d6650500 100644 --- a/esphome/components/gp8403/output/gp8403_output.h +++ b/esphome/components/gp8403/output/gp8403_output.h @@ -8,13 +8,11 @@ namespace esphome { namespace gp8403 { -class GP8403Output : public Component, public output::FloatOutput, public Parented { +class GP8403Output : public Component, public output::FloatOutput, public Parented { public: void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA - 1; } - void set_channel(uint8_t channel) { this->channel_ = channel; } - void write_state(float state) override; protected: diff --git a/tests/components/gp8403/common.yaml b/tests/components/gp8403/common.yaml index b04fbc4fbc..1147316b02 100644 --- a/tests/components/gp8403/common.yaml +++ b/tests/components/gp8403/common.yaml @@ -4,6 +4,11 @@ gp8403: voltage: 5V - id: gp8403_10v i2c_id: i2c_bus + model: GP8403 + voltage: 10V + - id: gp8413 + i2c_id: i2c_bus + model: GP8413 voltage: 10V output: @@ -15,3 +20,7 @@ output: gp8403_id: gp8403_10v id: gp8403_output_1 channel: 1 + - platform: gp8403 + gp8403_id: gp8413 + id: gp8413_output_2 + channel: 1 From 191a88c2dc50dd44f8a9dc3bcb2fbc70bff4fb35 Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Tue, 4 Nov 2025 19:38:59 +0100 Subject: [PATCH 386/526] [gt911] Fix gt911 touchscreen with reset pin not initializing when loglevel is set to NONE (#11715) --- esphome/components/gt911/touchscreen/gt911_touchscreen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp index 4810867d4b..992a86cc21 100644 --- a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +++ b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp @@ -34,8 +34,8 @@ void GT911Touchscreen::setup() { this->interrupt_pin_->digital_write(false); } delay(2); - this->reset_pin_->digital_write(true); // wait 50ms after reset - this->set_timeout(50, [this] { this->setup_internal_(); }); + this->reset_pin_->digital_write(true); // wait at least T3+T4 ms as per the datasheet + this->set_timeout(5 + 50 + 1, [this] { this->setup_internal_(); }); return; } this->setup_internal_(); From aed7505f53c48bcda7206d0ac6f8db6cdfad433d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 4 Nov 2025 15:48:20 -0600 Subject: [PATCH 387/526] [automations] Reduce memory usage in if/while/repeat actions (32-36 bytes per instance) (#11650) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/core/automation.h | 1 + esphome/core/base_automation.h | 91 +++++++++++++++++++++++++++------- 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/esphome/core/automation.h b/esphome/core/automation.h index aace7889f0..c22b3ca0e3 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -220,6 +220,7 @@ template class Action { protected: friend ActionList; + template friend class ContinuationAction; virtual void play(Ts... x) = 0; void play_next_(Ts... x) { diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index 28af02a846..083bb3ae31 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -216,18 +216,46 @@ template class StatelessLambdaAction : public Action { void (*f_)(Ts...); }; +/// Simple continuation action that calls play_next_ on a parent action. +/// Used internally by IfAction, WhileAction, RepeatAction, etc. to chain actions. +/// Memory: 4-8 bytes (parent pointer) vs 40 bytes (LambdaAction with std::function). +template class ContinuationAction : public Action { + public: + explicit ContinuationAction(Action *parent) : parent_(parent) {} + + void play(Ts... x) override { this->parent_->play_next_(x...); } + + protected: + Action *parent_; +}; + +// Forward declaration for WhileLoopContinuation +template class WhileAction; + +/// Loop continuation for WhileAction that checks condition and repeats or continues. +/// Memory: 4-8 bytes (parent pointer) vs 40 bytes (LambdaAction with std::function). +template class WhileLoopContinuation : public Action { + public: + explicit WhileLoopContinuation(WhileAction *parent) : parent_(parent) {} + + void play(Ts... x) override; + + protected: + WhileAction *parent_; +}; + template class IfAction : public Action { public: explicit IfAction(Condition *condition) : condition_(condition) {} void add_then(const std::initializer_list *> &actions) { this->then_.add_actions(actions); - this->then_.add_action(new LambdaAction([this](Ts... x) { this->play_next_(x...); })); + this->then_.add_action(new ContinuationAction(this)); } void add_else(const std::initializer_list *> &actions) { this->else_.add_actions(actions); - this->else_.add_action(new LambdaAction([this](Ts... x) { this->play_next_(x...); })); + this->else_.add_action(new ContinuationAction(this)); } void play_complex(Ts... x) override { @@ -268,17 +296,11 @@ template class WhileAction : public Action { void add_then(const std::initializer_list *> &actions) { this->then_.add_actions(actions); - this->then_.add_action(new LambdaAction([this](Ts... x) { - if (this->num_running_ > 0 && this->condition_->check(x...)) { - // play again - this->then_.play(x...); - } else { - // condition false, play next - this->play_next_(x...); - } - })); + this->then_.add_action(new WhileLoopContinuation(this)); } + friend class WhileLoopContinuation; + void play_complex(Ts... x) override { this->num_running_++; // Initial condition check @@ -304,22 +326,43 @@ template class WhileAction : public Action { ActionList then_; }; +// Implementation of WhileLoopContinuation::play +template void WhileLoopContinuation::play(Ts... x) { + if (this->parent_->num_running_ > 0 && this->parent_->condition_->check(x...)) { + // play again + this->parent_->then_.play(x...); + } else { + // condition false, play next + this->parent_->play_next_(x...); + } +} + +// Forward declaration for RepeatLoopContinuation +template class RepeatAction; + +/// Loop continuation for RepeatAction that increments iteration and repeats or continues. +/// Memory: 4-8 bytes (parent pointer) vs 40 bytes (LambdaAction with std::function). +template class RepeatLoopContinuation : public Action { + public: + explicit RepeatLoopContinuation(RepeatAction *parent) : parent_(parent) {} + + void play(uint32_t iteration, Ts... x) override; + + protected: + RepeatAction *parent_; +}; + template class RepeatAction : public Action { public: TEMPLATABLE_VALUE(uint32_t, count) void add_then(const std::initializer_list *> &actions) { this->then_.add_actions(actions); - this->then_.add_action(new LambdaAction([this](uint32_t iteration, Ts... x) { - iteration++; - if (iteration >= this->count_.value(x...)) { - this->play_next_(x...); - } else { - this->then_.play(iteration, x...); - } - })); + this->then_.add_action(new RepeatLoopContinuation(this)); } + friend class RepeatLoopContinuation; + void play_complex(Ts... x) override { this->num_running_++; if (this->count_.value(x...) > 0) { @@ -338,6 +381,16 @@ template class RepeatAction : public Action { ActionList then_; }; +// Implementation of RepeatLoopContinuation::play +template void RepeatLoopContinuation::play(uint32_t iteration, Ts... x) { + iteration++; + if (iteration >= this->parent_->count_.value(x...)) { + this->parent_->play_next_(x...); + } else { + this->parent_->then_.play(iteration, x...); + } +} + /** Wait until a condition is true to continue execution. * * Uses queue-based storage to safely handle concurrent executions. From 531b27582a4422700f166c16b8e2b2bd39647eaa Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 4 Nov 2025 15:52:10 -0600 Subject: [PATCH 388/526] [network] Store use_address in RODATA to save RAM (#11707) --- esphome/components/api/api_server.cpp | 2 +- esphome/components/esphome/ota/ota_esphome.cpp | 2 +- esphome/components/ethernet/ethernet_component.cpp | 4 ++-- esphome/components/ethernet/ethernet_component.h | 10 +++++++--- esphome/components/network/util.cpp | 5 ++--- esphome/components/network/util.h | 2 +- esphome/components/openthread/openthread.cpp | 4 ++-- esphome/components/openthread/openthread.h | 10 +++++++--- esphome/components/web_server/web_server.cpp | 2 +- esphome/components/wifi/wifi_component.cpp | 4 ++-- esphome/components/wifi/wifi_component.h | 10 +++++++--- 11 files changed, 33 insertions(+), 22 deletions(-) diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index e618610a75..e5f0d9795e 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -224,7 +224,7 @@ void APIServer::dump_config() { " Address: %s:%u\n" " Listen backlog: %u\n" " Max connections: %u", - network::get_use_address().c_str(), this->port_, this->listen_backlog_, this->max_connections_); + network::get_use_address(), this->port_, this->listen_backlog_, this->max_connections_); #ifdef USE_API_NOISE ESP_LOGCONFIG(TAG, " Noise encryption: %s", YESNO(this->noise_ctx_->has_psk())); if (!this->noise_ctx_->has_psk()) { diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index b85d660272..eb6c61a69b 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -94,7 +94,7 @@ void ESPHomeOTAComponent::dump_config() { "Over-The-Air updates:\n" " Address: %s:%u\n" " Version: %d", - network::get_use_address().c_str(), this->port_, USE_OTA_VERSION); + network::get_use_address(), this->port_, USE_OTA_VERSION); #ifdef USE_OTA_PASSWORD if (!this->password_.empty()) { ESP_LOGCONFIG(TAG, " Password configured"); diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 24b6e8154b..893d0285be 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -691,9 +691,9 @@ void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ // set_use_address() is guaranteed to be called during component setup by Python code generation, // so use_address_ will always be valid when get_use_address() is called - no fallback needed. -const std::string &EthernetComponent::get_use_address() const { return this->use_address_; } +const char *EthernetComponent::get_use_address() const { return this->use_address_; } -void EthernetComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } +void EthernetComponent::set_use_address(const char *use_address) { this->use_address_ = use_address; } void EthernetComponent::get_eth_mac_address_raw(uint8_t *mac) { esp_err_t err; diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index d5dda3e3ae..31f9fa360a 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -88,8 +88,8 @@ class EthernetComponent : public Component { network::IPAddresses get_ip_addresses(); network::IPAddress get_dns_address(uint8_t num); - const std::string &get_use_address() const; - void set_use_address(const std::string &use_address); + const char *get_use_address() const; + void set_use_address(const char *use_address); void get_eth_mac_address_raw(uint8_t *mac); std::string get_eth_mac_address_pretty(); eth_duplex_t get_duplex_mode(); @@ -114,7 +114,6 @@ class EthernetComponent : public Component { /// @brief Set arbitratry PHY registers from config. void write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data); - std::string use_address_; #ifdef USE_ETHERNET_SPI uint8_t clk_pin_; uint8_t miso_pin_; @@ -158,6 +157,11 @@ class EthernetComponent : public Component { esp_eth_handle_t eth_handle_; esp_eth_phy_t *phy_{nullptr}; optional> fixed_mac_; + + private: + // Stores a pointer to a string literal (static storage duration). + // ONLY set from Python-generated code with string literals - never dynamic strings. + const char *use_address_{""}; }; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp index cb8f8569ad..5e741fd244 100644 --- a/esphome/components/network/util.cpp +++ b/esphome/components/network/util.cpp @@ -85,7 +85,7 @@ network::IPAddresses get_ip_addresses() { return {}; } -const std::string &get_use_address() { +const char *get_use_address() { // Global component pointers are guaranteed to be set by component constructors when USE_* is defined #ifdef USE_ETHERNET return ethernet::global_eth_component->get_use_address(); @@ -105,8 +105,7 @@ const std::string &get_use_address() { #if !defined(USE_ETHERNET) && !defined(USE_MODEM) && !defined(USE_WIFI) && !defined(USE_OPENTHREAD) // Fallback when no network component is defined (e.g., host platform) - static const std::string empty; - return empty; + return ""; #endif } diff --git a/esphome/components/network/util.h b/esphome/components/network/util.h index b4a92f8bee..3dc12232aa 100644 --- a/esphome/components/network/util.h +++ b/esphome/components/network/util.h @@ -12,7 +12,7 @@ bool is_connected(); /// Return whether the network is disabled (only wifi for now) bool is_disabled(); /// Get the active network hostname -const std::string &get_use_address(); +const char *get_use_address(); IPAddresses get_ip_addresses(); } // namespace network diff --git a/esphome/components/openthread/openthread.cpp b/esphome/components/openthread/openthread.cpp index db909e6b1f..d7fb1e1d42 100644 --- a/esphome/components/openthread/openthread.cpp +++ b/esphome/components/openthread/openthread.cpp @@ -254,9 +254,9 @@ void OpenThreadComponent::on_factory_reset(std::function callback) { // set_use_address() is guaranteed to be called during component setup by Python code generation, // so use_address_ will always be valid when get_use_address() is called - no fallback needed. -const std::string &OpenThreadComponent::get_use_address() const { return this->use_address_; } +const char *OpenThreadComponent::get_use_address() const { return this->use_address_; } -void OpenThreadComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } +void OpenThreadComponent::set_use_address(const char *use_address) { this->use_address_ = use_address; } } // namespace openthread } // namespace esphome diff --git a/esphome/components/openthread/openthread.h b/esphome/components/openthread/openthread.h index 19dbeb4628..3132e41696 100644 --- a/esphome/components/openthread/openthread.h +++ b/esphome/components/openthread/openthread.h @@ -33,15 +33,19 @@ class OpenThreadComponent : public Component { void on_factory_reset(std::function callback); void defer_factory_reset_external_callback(); - const std::string &get_use_address() const; - void set_use_address(const std::string &use_address); + const char *get_use_address() const; + void set_use_address(const char *use_address); protected: std::optional get_omr_address_(InstanceLock &lock); bool teardown_started_{false}; bool teardown_complete_{false}; std::function factory_reset_external_callback_; - std::string use_address_; + + private: + // Stores a pointer to a string literal (static storage duration). + // ONLY set from Python-generated code with string literals - never dynamic strings. + const char *use_address_{""}; }; extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 465356db80..acc0f33e61 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -324,7 +324,7 @@ void WebServer::dump_config() { ESP_LOGCONFIG(TAG, "Web Server:\n" " Address: %s:%u", - network::get_use_address().c_str(), this->base_->get_port()); + network::get_use_address(), this->base_->get_port()); } float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f; } diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index b278e5a386..51b5756f29 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -273,8 +273,8 @@ network::IPAddress WiFiComponent::get_dns_address(int num) { } // set_use_address() is guaranteed to be called during component setup by Python code generation, // so use_address_ will always be valid when get_use_address() is called - no fallback needed. -const std::string &WiFiComponent::get_use_address() const { return this->use_address_; } -void WiFiComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } +const char *WiFiComponent::get_use_address() const { return this->use_address_; } +void WiFiComponent::set_use_address(const char *use_address) { this->use_address_ = use_address; } #ifdef USE_WIFI_AP void WiFiComponent::setup_ap_config_() { diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 42f78dbfac..89b7b1fa41 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -283,8 +283,8 @@ class WiFiComponent : public Component { network::IPAddress get_dns_address(int num); network::IPAddresses get_ip_addresses(); - const std::string &get_use_address() const; - void set_use_address(const std::string &use_address); + const char *get_use_address() const; + void set_use_address(const char *use_address); const wifi_scan_vector_t &get_scan_result() const { return scan_result_; } @@ -393,7 +393,6 @@ class WiFiComponent : public Component { void wifi_scan_done_callback_(); #endif - std::string use_address_; FixedVector sta_; std::vector sta_priorities_; wifi_scan_vector_t scan_result_; @@ -445,6 +444,11 @@ class WiFiComponent : public Component { // Pointers at the end (naturally aligned) Trigger<> *connect_trigger_{new Trigger<>()}; Trigger<> *disconnect_trigger_{new Trigger<>()}; + + private: + // Stores a pointer to a string literal (static storage duration). + // ONLY set from Python-generated code with string literals - never dynamic strings. + const char *use_address_{""}; }; extern WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) From 885508775f6c1a556410c462b95f247c6b249d05 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 4 Nov 2025 15:55:37 -0600 Subject: [PATCH 389/526] [fan] Remove duplicate preset mode storage to save RAM (#11632) --- esphome/components/api/api_connection.cpp | 4 +- esphome/components/copy/fan/copy_fan.cpp | 6 +- esphome/components/fan/automation.h | 9 +- esphome/components/fan/fan.cpp | 90 ++++++++++++------- esphome/components/fan/fan.h | 32 +++++-- esphome/components/fan/fan_traits.h | 12 +++ .../components/hbridge/fan/hbridge_fan.cpp | 2 +- esphome/components/speed/fan/speed_fan.cpp | 2 +- .../components/template/fan/template_fan.cpp | 2 +- 9 files changed, 109 insertions(+), 50 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index f8490b6ef7..3f1a076007 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -410,8 +410,8 @@ uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *co } if (traits.supports_direction()) msg.direction = static_cast(fan->direction); - if (traits.supports_preset_modes()) - msg.set_preset_mode(StringRef(fan->preset_mode)); + if (traits.supports_preset_modes() && fan->has_preset_mode()) + msg.set_preset_mode(StringRef(fan->get_preset_mode())); return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, diff --git a/esphome/components/copy/fan/copy_fan.cpp b/esphome/components/copy/fan/copy_fan.cpp index 15a7f5e025..d35ece950b 100644 --- a/esphome/components/copy/fan/copy_fan.cpp +++ b/esphome/components/copy/fan/copy_fan.cpp @@ -12,7 +12,7 @@ void CopyFan::setup() { this->oscillating = source_->oscillating; this->speed = source_->speed; this->direction = source_->direction; - this->preset_mode = source_->preset_mode; + this->set_preset_mode_(source_->get_preset_mode()); this->publish_state(); }); @@ -20,7 +20,7 @@ void CopyFan::setup() { this->oscillating = source_->oscillating; this->speed = source_->speed; this->direction = source_->direction; - this->preset_mode = source_->preset_mode; + this->set_preset_mode_(source_->get_preset_mode()); this->publish_state(); } @@ -49,7 +49,7 @@ void CopyFan::control(const fan::FanCall &call) { call2.set_speed(*call.get_speed()); if (call.get_direction().has_value()) call2.set_direction(*call.get_direction()); - if (!call.get_preset_mode().empty()) + if (call.has_preset_mode()) call2.set_preset_mode(call.get_preset_mode()); call2.perform(); } diff --git a/esphome/components/fan/automation.h b/esphome/components/fan/automation.h index 90661c307c..ae0af1a9bd 100644 --- a/esphome/components/fan/automation.h +++ b/esphome/components/fan/automation.h @@ -212,18 +212,19 @@ class FanPresetSetTrigger : public Trigger { public: FanPresetSetTrigger(Fan *state) { state->add_on_state_callback([this, state]() { - auto preset_mode = state->preset_mode; + const auto *preset_mode = state->get_preset_mode(); auto should_trigger = preset_mode != this->last_preset_mode_; this->last_preset_mode_ = preset_mode; if (should_trigger) { - this->trigger(preset_mode); + // Trigger with empty string when nullptr to maintain backward compatibility + this->trigger(preset_mode != nullptr ? preset_mode : ""); } }); - this->last_preset_mode_ = state->preset_mode; + this->last_preset_mode_ = state->get_preset_mode(); } protected: - std::string last_preset_mode_; + const char *last_preset_mode_{nullptr}; }; } // namespace fan diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 5b4f437f99..959572e9d9 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -17,6 +17,27 @@ const LogString *fan_direction_to_string(FanDirection direction) { } } +FanCall &FanCall::set_preset_mode(const std::string &preset_mode) { return this->set_preset_mode(preset_mode.c_str()); } + +FanCall &FanCall::set_preset_mode(const char *preset_mode) { + if (preset_mode == nullptr || strlen(preset_mode) == 0) { + this->preset_mode_ = nullptr; + return *this; + } + + // Find and validate pointer from traits immediately + auto traits = this->parent_.get_traits(); + const char *validated_mode = traits.find_preset_mode(preset_mode); + if (validated_mode != nullptr) { + this->preset_mode_ = validated_mode; // Store pointer from traits + } else { + // Preset mode not found in traits - log warning and don't set + ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), preset_mode); + this->preset_mode_ = nullptr; + } + return *this; +} + void FanCall::perform() { ESP_LOGD(TAG, "'%s' - Setting:", this->parent_.get_name().c_str()); this->validate_(); @@ -32,8 +53,8 @@ void FanCall::perform() { if (this->direction_.has_value()) { ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(*this->direction_))); } - if (!this->preset_mode_.empty()) { - ESP_LOGD(TAG, " Preset Mode: %s", this->preset_mode_.c_str()); + if (this->has_preset_mode()) { + ESP_LOGD(TAG, " Preset Mode: %s", this->preset_mode_); } this->parent_.control(*this); } @@ -46,30 +67,15 @@ void FanCall::validate_() { // https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes // "Manually setting a speed must disable any set preset mode" - this->preset_mode_.clear(); - } - - if (!this->preset_mode_.empty()) { - const auto &preset_modes = traits.supported_preset_modes(); - bool found = false; - for (const auto &mode : preset_modes) { - if (strcmp(mode, this->preset_mode_.c_str()) == 0) { - found = true; - break; - } - } - if (!found) { - ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), this->preset_mode_.c_str()); - this->preset_mode_.clear(); - } + this->preset_mode_ = nullptr; } // when turning on... if (!this->parent_.state && this->binary_state_.has_value() && *this->binary_state_ // ..,and no preset mode will be active... - && this->preset_mode_.empty() && - this->parent_.preset_mode.empty() + && !this->has_preset_mode() && + this->parent_.get_preset_mode() == nullptr // ...and neither current nor new speed is available... && traits.supports_speed() && this->parent_.speed == 0 && !this->speed_.has_value()) { // ...set speed to 100% @@ -117,12 +123,13 @@ void FanRestoreState::apply(Fan &fan) { auto traits = fan.get_traits(); if (traits.supports_preset_modes()) { - // Use stored preset index to get preset name + // Use stored preset index to get preset name from traits const auto &preset_modes = traits.supported_preset_modes(); if (this->preset_mode < preset_modes.size()) { - fan.preset_mode = preset_modes[this->preset_mode]; + fan.set_preset_mode_(preset_modes[this->preset_mode]); } } + fan.publish_state(); } @@ -131,6 +138,29 @@ FanCall Fan::turn_off() { return this->make_call().set_state(false); } FanCall Fan::toggle() { return this->make_call().set_state(!this->state); } FanCall Fan::make_call() { return FanCall(*this); } +const char *Fan::find_preset_mode_(const char *preset_mode) { return this->get_traits().find_preset_mode(preset_mode); } + +bool Fan::set_preset_mode_(const char *preset_mode) { + if (preset_mode == nullptr) { + // Treat nullptr as clearing the preset mode + if (this->preset_mode_ == nullptr) { + return false; // No change + } + this->clear_preset_mode_(); + return true; + } + const char *validated = this->find_preset_mode_(preset_mode); + if (validated == nullptr || this->preset_mode_ == validated) { + return false; // Preset mode not supported or no change + } + this->preset_mode_ = validated; + return true; +} + +bool Fan::set_preset_mode_(const std::string &preset_mode) { return this->set_preset_mode_(preset_mode.c_str()); } + +void Fan::clear_preset_mode_() { this->preset_mode_ = nullptr; } + void Fan::add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } void Fan::publish_state() { auto traits = this->get_traits(); @@ -146,8 +176,9 @@ void Fan::publish_state() { if (traits.supports_direction()) { ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(this->direction))); } - if (traits.supports_preset_modes() && !this->preset_mode.empty()) { - ESP_LOGD(TAG, " Preset Mode: %s", this->preset_mode.c_str()); + const char *preset = this->get_preset_mode(); + if (preset != nullptr) { + ESP_LOGD(TAG, " Preset Mode: %s", preset); } this->state_callback_.call(); this->save_state_(); @@ -199,16 +230,15 @@ void Fan::save_state_() { state.speed = this->speed; state.direction = this->direction; - if (traits.supports_preset_modes() && !this->preset_mode.empty()) { + const char *preset = this->get_preset_mode(); + if (preset != nullptr) { const auto &preset_modes = traits.supported_preset_modes(); - // Store index of current preset mode - size_t i = 0; - for (const auto &mode : preset_modes) { - if (strcmp(mode, this->preset_mode.c_str()) == 0) { + // Find index of current preset mode (pointer comparison is safe since preset is from traits) + for (size_t i = 0; i < preset_modes.size(); i++) { + if (preset_modes[i] == preset) { state.preset_mode = i; break; } - i++; } } diff --git a/esphome/components/fan/fan.h b/esphome/components/fan/fan.h index 3739de29a2..e38a80dbbe 100644 --- a/esphome/components/fan/fan.h +++ b/esphome/components/fan/fan.h @@ -70,11 +70,10 @@ class FanCall { return *this; } optional get_direction() const { return this->direction_; } - FanCall &set_preset_mode(const std::string &preset_mode) { - this->preset_mode_ = preset_mode; - return *this; - } - std::string get_preset_mode() const { return this->preset_mode_; } + FanCall &set_preset_mode(const std::string &preset_mode); + FanCall &set_preset_mode(const char *preset_mode); + const char *get_preset_mode() const { return this->preset_mode_; } + bool has_preset_mode() const { return this->preset_mode_ != nullptr; } void perform(); @@ -86,7 +85,7 @@ class FanCall { optional oscillating_; optional speed_; optional direction_{}; - std::string preset_mode_{}; + const char *preset_mode_{nullptr}; // Pointer to string in traits (after validation) }; struct FanRestoreState { @@ -112,8 +111,6 @@ class Fan : public EntityBase { int speed{0}; /// The current direction of the fan FanDirection direction{FanDirection::FORWARD}; - // The current preset mode of the fan - std::string preset_mode{}; FanCall turn_on(); FanCall turn_off(); @@ -130,8 +127,15 @@ class Fan : public EntityBase { /// Set the restore mode of this fan. void set_restore_mode(FanRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } + /// Get the current preset mode (returns pointer to string stored in traits, or nullptr if not set) + const char *get_preset_mode() const { return this->preset_mode_; } + + /// Check if a preset mode is currently active + bool has_preset_mode() const { return this->preset_mode_ != nullptr; } + protected: friend FanCall; + friend struct FanRestoreState; virtual void control(const FanCall &call) = 0; @@ -140,9 +144,21 @@ class Fan : public EntityBase { void dump_traits_(const char *tag, const char *prefix); + /// Set the preset mode (finds and stores pointer from traits). Returns true if changed. + bool set_preset_mode_(const char *preset_mode); + /// Set the preset mode (finds and stores pointer from traits). Returns true if changed. + bool set_preset_mode_(const std::string &preset_mode); + /// Clear the preset mode + void clear_preset_mode_(); + /// Find and return the matching preset mode pointer from traits, or nullptr if not found. + const char *find_preset_mode_(const char *preset_mode); + CallbackManager state_callback_{}; ESPPreferenceObject rtc_; FanRestoreMode restore_mode_; + + private: + const char *preset_mode_{nullptr}; }; } // namespace fan diff --git a/esphome/components/fan/fan_traits.h b/esphome/components/fan/fan_traits.h index df345f9b04..24987fe984 100644 --- a/esphome/components/fan/fan_traits.h +++ b/esphome/components/fan/fan_traits.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -44,6 +45,17 @@ class FanTraits { /// Return if preset modes are supported bool supports_preset_modes() const { return !this->preset_modes_.empty(); } + /// Find and return the matching preset mode pointer from supported modes, or nullptr if not found. + const char *find_preset_mode(const char *preset_mode) const { + if (preset_mode == nullptr) + return nullptr; + for (const char *mode : this->preset_modes_) { + if (strcmp(mode, preset_mode) == 0) { + return mode; // Return pointer from traits + } + } + return nullptr; + } protected: bool oscillation_{false}; diff --git a/esphome/components/hbridge/fan/hbridge_fan.cpp b/esphome/components/hbridge/fan/hbridge_fan.cpp index 605a9d4ef3..488208b725 100644 --- a/esphome/components/hbridge/fan/hbridge_fan.cpp +++ b/esphome/components/hbridge/fan/hbridge_fan.cpp @@ -57,7 +57,7 @@ void HBridgeFan::control(const fan::FanCall &call) { this->oscillating = *call.get_oscillating(); if (call.get_direction().has_value()) this->direction = *call.get_direction(); - this->preset_mode = call.get_preset_mode(); + this->set_preset_mode_(call.get_preset_mode()); this->write_state_(); this->publish_state(); diff --git a/esphome/components/speed/fan/speed_fan.cpp b/esphome/components/speed/fan/speed_fan.cpp index 57bd795416..801593c2ac 100644 --- a/esphome/components/speed/fan/speed_fan.cpp +++ b/esphome/components/speed/fan/speed_fan.cpp @@ -29,7 +29,7 @@ void SpeedFan::control(const fan::FanCall &call) { this->oscillating = *call.get_oscillating(); if (call.get_direction().has_value()) this->direction = *call.get_direction(); - this->preset_mode = call.get_preset_mode(); + this->set_preset_mode_(call.get_preset_mode()); this->write_state_(); this->publish_state(); diff --git a/esphome/components/template/fan/template_fan.cpp b/esphome/components/template/fan/template_fan.cpp index 5f4a2ae8f7..eba4c673b5 100644 --- a/esphome/components/template/fan/template_fan.cpp +++ b/esphome/components/template/fan/template_fan.cpp @@ -29,7 +29,7 @@ void TemplateFan::control(const fan::FanCall &call) { this->oscillating = *call.get_oscillating(); if (call.get_direction().has_value() && this->has_direction_) this->direction = *call.get_direction(); - this->preset_mode = call.get_preset_mode(); + this->set_preset_mode_(call.get_preset_mode()); this->publish_state(); } From c5e5609e926d4ebb9144638aeaef638b78c17105 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 5 Nov 2025 08:00:12 +1000 Subject: [PATCH 390/526] [lvgl] Fix case sensitivity in flex layout (#11717) --- esphome/components/lvgl/layout.py | 39 +++++++++++++++++++------ tests/components/lvgl/lvgl-package.yaml | 6 ++-- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/esphome/components/lvgl/layout.py b/esphome/components/lvgl/layout.py index 0aed525e16..a6aa816fda 100644 --- a/esphome/components/lvgl/layout.py +++ b/esphome/components/lvgl/layout.py @@ -1,4 +1,5 @@ import re +import textwrap import esphome.config_validation as cv from esphome.const import CONF_HEIGHT, CONF_TYPE, CONF_WIDTH @@ -122,7 +123,7 @@ class FlexLayout(Layout): def get_layout_schemas(self, config: dict) -> tuple: layout = config.get(CONF_LAYOUT) - if not isinstance(layout, dict) or layout.get(CONF_TYPE) != TYPE_FLEX: + if not isinstance(layout, dict) or layout.get(CONF_TYPE).lower() != TYPE_FLEX: return None, {} child_schema = FLEX_OBJ_SCHEMA if grow := layout.get(CONF_FLEX_GROW): @@ -161,6 +162,8 @@ class DirectionalLayout(FlexLayout): return self.direction def get_layout_schemas(self, config: dict) -> tuple: + if not isinstance(config.get(CONF_LAYOUT), str): + return None, {} if config.get(CONF_LAYOUT, "").lower() != self.direction: return None, {} return cv.one_of(self.direction, lower=True), flex_hv_schema(self.direction) @@ -206,7 +209,7 @@ class GridLayout(Layout): # Not a valid grid layout string return None, {} - if not isinstance(layout, dict) or layout.get(CONF_TYPE) != TYPE_GRID: + if not isinstance(layout, dict) or layout.get(CONF_TYPE).lower() != TYPE_GRID: return None, {} return ( { @@ -259,7 +262,7 @@ class GridLayout(Layout): ) # should be guaranteed to be a dict at this point assert isinstance(layout, dict) - assert layout.get(CONF_TYPE) == TYPE_GRID + assert layout.get(CONF_TYPE).lower() == TYPE_GRID rows = len(layout[CONF_GRID_ROWS]) columns = len(layout[CONF_GRID_COLUMNS]) used_cells = [[None] * columns for _ in range(rows)] @@ -335,6 +338,17 @@ def append_layout_schema(schema, config: dict): if CONF_LAYOUT not in config: # If no layout is specified, return the schema as is return schema.extend({cv.Optional(CONF_WIDGETS): any_widget_schema()}) + layout = config[CONF_LAYOUT] + # Sanity check the layout to avoid redundant checks in each type + if not isinstance(layout, str) and not isinstance(layout, dict): + raise cv.Invalid( + "The 'layout' option must be a string or a dictionary", [CONF_LAYOUT] + ) + if isinstance(layout, dict) and not isinstance(layout.get(CONF_TYPE), str): + raise cv.Invalid( + "Invalid layout type; must be a string ('flex' or 'grid')", + [CONF_LAYOUT, CONF_TYPE], + ) for layout_class in LAYOUT_CLASSES: layout_schema, child_schema = layout_class.get_layout_schemas(config) @@ -348,10 +362,17 @@ def append_layout_schema(schema, config: dict): layout_schema.add_extra(layout_class.validate) return layout_schema.extend(schema) - # If no layout class matched, return a default schema - return cv.Schema( - { - cv.Optional(CONF_LAYOUT): cv.one_of(*LAYOUT_CHOICES, lower=True), - cv.Optional(CONF_WIDGETS): any_widget_schema(), - } + if isinstance(layout, dict): + raise cv.Invalid( + "Invalid layout type; must be 'flex' or 'grid'", [CONF_LAYOUT, CONF_TYPE] + ) + raise cv.Invalid( + textwrap.dedent( + """ + Invalid 'layout' value + layout choices are 'horizontal', 'vertical', 'x', + or a dictionary with a 'type' key + """ + ), + [CONF_LAYOUT], ) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index ca669c16e4..8ac9a60e2d 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -191,7 +191,7 @@ lvgl: args: ['lv_event_code_name_for(event->code).c_str()'] skip: true layout: - type: flex + type: Flex pad_row: 4 pad_column: 4px flex_align_main: center @@ -863,7 +863,7 @@ lvgl: width: 100% pad_all: 8 layout: - type: grid + type: GRid grid_row_align: end grid_rows: [25px, fr(1), content] grid_columns: [40, fr(1), fr(1)] @@ -1014,7 +1014,7 @@ lvgl: r_mod: -20 opa: 0% - id: page3 - layout: horizontal + layout: Horizontal widgets: - keyboard: id: lv_keyboard From c7ae42461331d1fdeb73c9a042fabc1ab6c954a6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 4 Nov 2025 16:14:54 -0600 Subject: [PATCH 391/526] [display] Optimize display writers with function pointers for stateless lambdas (#11629) --- esphome/components/display/display.h | 114 +++++++++++++++++- .../components/lcd_gpio/gpio_lcd_display.h | 9 +- .../components/lcd_pcf8574/pcf8574_display.h | 9 +- esphome/components/max7219/max7219.h | 5 +- .../components/max7219digit/max7219digit.h | 4 +- esphome/components/nextion/nextion.h | 5 +- .../pvvx_mithermometer/display/pvvx_display.h | 5 +- esphome/components/st7920/st7920.h | 4 +- esphome/components/tm1621/tm1621.h | 5 +- esphome/components/tm1637/tm1637.h | 5 +- esphome/components/tm1638/tm1638.h | 5 +- tests/components/image/test.host.yaml | 1 + tests/components/sdl/common.yaml | 3 + 13 files changed, 152 insertions(+), 22 deletions(-) diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index 14205da853..97b0a4e8d7 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -176,7 +176,117 @@ class Display; class DisplayPage; class DisplayOnPageChangeTrigger; -using display_writer_t = std::function; +/** Optimized display writer that uses function pointers for stateless lambdas. + * + * Similar to TemplatableValue but specialized for display writer callbacks. + * Saves ~8 bytes per stateless lambda on 32-bit platforms (16 bytes std::function → ~8 bytes discriminator+pointer). + * + * Supports both: + * - Stateless lambdas (from YAML) → function pointer (4 bytes) + * - Stateful lambdas/std::function (from C++ code) → std::function* (heap allocated) + * + * @tparam T The display type (e.g., Display, Nextion, GPIOLCDDisplay) + */ +template class DisplayWriter { + public: + DisplayWriter() : type_(NONE) {} + + // For stateless lambdas (convertible to function pointer): use function pointer (4 bytes) + template + DisplayWriter(F f) requires std::invocable && std::convertible_to + : type_(STATELESS_LAMBDA) { + this->stateless_f_ = f; // Implicit conversion to function pointer + } + + // For stateful lambdas and std::function (not convertible to function pointer): use std::function* (heap allocated) + // This handles backwards compatibility with external components + template + DisplayWriter(F f) requires std::invocable &&(!std::convertible_to) : type_(LAMBDA) { + this->f_ = new std::function(std::move(f)); + } + + // Copy constructor + DisplayWriter(const DisplayWriter &other) : type_(other.type_) { + if (type_ == LAMBDA) { + this->f_ = new std::function(*other.f_); + } else if (type_ == STATELESS_LAMBDA) { + this->stateless_f_ = other.stateless_f_; + } + } + + // Move constructor + DisplayWriter(DisplayWriter &&other) noexcept : type_(other.type_) { + if (type_ == LAMBDA) { + this->f_ = other.f_; + other.f_ = nullptr; + } else if (type_ == STATELESS_LAMBDA) { + this->stateless_f_ = other.stateless_f_; + } + other.type_ = NONE; + } + + // Assignment operators + DisplayWriter &operator=(const DisplayWriter &other) { + if (this != &other) { + this->~DisplayWriter(); + new (this) DisplayWriter(other); + } + return *this; + } + + DisplayWriter &operator=(DisplayWriter &&other) noexcept { + if (this != &other) { + this->~DisplayWriter(); + new (this) DisplayWriter(std::move(other)); + } + return *this; + } + + ~DisplayWriter() { + if (type_ == LAMBDA) { + delete this->f_; + } + // STATELESS_LAMBDA/NONE: no cleanup needed (function pointer or empty) + } + + bool has_value() const { return this->type_ != NONE; } + + void call(T &display) const { + switch (this->type_) { + case STATELESS_LAMBDA: + this->stateless_f_(display); // Direct function pointer call + break; + case LAMBDA: + (*this->f_)(display); // std::function call + break; + case NONE: + default: + break; + } + } + + // Operator() for convenience + void operator()(T &display) const { this->call(display); } + + // Operator* for backwards compatibility with (*writer_)(*this) pattern + DisplayWriter &operator*() { return *this; } + const DisplayWriter &operator*() const { return *this; } + + protected: + enum : uint8_t { + NONE, + LAMBDA, + STATELESS_LAMBDA, + } type_; + + union { + std::function *f_; + void (*stateless_f_)(T &); + }; +}; + +// Type alias for Display writer - uses optimized DisplayWriter instead of std::function +using display_writer_t = DisplayWriter; #define LOG_DISPLAY(prefix, type, obj) \ if ((obj) != nullptr) { \ @@ -678,7 +788,7 @@ class Display : public PollingComponent { void sort_triangle_points_by_y_(int *x1, int *y1, int *x2, int *y2, int *x3, int *y3); DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES}; - optional writer_{}; + display_writer_t writer_{}; DisplayPage *page_{nullptr}; DisplayPage *previous_page_{nullptr}; std::vector on_page_change_triggers_; diff --git a/esphome/components/lcd_gpio/gpio_lcd_display.h b/esphome/components/lcd_gpio/gpio_lcd_display.h index aba254a90a..81e4dc51a0 100644 --- a/esphome/components/lcd_gpio/gpio_lcd_display.h +++ b/esphome/components/lcd_gpio/gpio_lcd_display.h @@ -2,13 +2,18 @@ #include "esphome/core/hal.h" #include "esphome/components/lcd_base/lcd_display.h" +#include "esphome/components/display/display.h" namespace esphome { namespace lcd_gpio { +class GPIOLCDDisplay; + +using gpio_lcd_writer_t = display::DisplayWriter; + class GPIOLCDDisplay : public lcd_base::LCDDisplay { public: - void set_writer(std::function &&writer) { this->writer_ = std::move(writer); } + void set_writer(gpio_lcd_writer_t &&writer) { this->writer_ = std::move(writer); } void setup() override; void set_data_pins(GPIOPin *d0, GPIOPin *d1, GPIOPin *d2, GPIOPin *d3) { this->data_pins_[0] = d0; @@ -43,7 +48,7 @@ class GPIOLCDDisplay : public lcd_base::LCDDisplay { GPIOPin *rw_pin_{nullptr}; GPIOPin *enable_pin_{nullptr}; GPIOPin *data_pins_[8]{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; - std::function writer_; + gpio_lcd_writer_t writer_; }; } // namespace lcd_gpio diff --git a/esphome/components/lcd_pcf8574/pcf8574_display.h b/esphome/components/lcd_pcf8574/pcf8574_display.h index 4db3afb9b0..672b609036 100644 --- a/esphome/components/lcd_pcf8574/pcf8574_display.h +++ b/esphome/components/lcd_pcf8574/pcf8574_display.h @@ -3,13 +3,18 @@ #include "esphome/core/component.h" #include "esphome/components/lcd_base/lcd_display.h" #include "esphome/components/i2c/i2c.h" +#include "esphome/components/display/display.h" namespace esphome { namespace lcd_pcf8574 { +class PCF8574LCDDisplay; + +using pcf8574_lcd_writer_t = display::DisplayWriter; + class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice { public: - void set_writer(std::function &&writer) { this->writer_ = std::move(writer); } + void set_writer(pcf8574_lcd_writer_t &&writer) { this->writer_ = std::move(writer); } void setup() override; void dump_config() override; void backlight(); @@ -24,7 +29,7 @@ class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice { // Stores the current state of the backlight. uint8_t backlight_value_; - std::function writer_; + pcf8574_lcd_writer_t writer_; }; } // namespace lcd_pcf8574 diff --git a/esphome/components/max7219/max7219.h b/esphome/components/max7219/max7219.h index 270edf3282..58d871d54c 100644 --- a/esphome/components/max7219/max7219.h +++ b/esphome/components/max7219/max7219.h @@ -4,13 +4,14 @@ #include "esphome/core/time.h" #include "esphome/components/spi/spi.h" +#include "esphome/components/display/display.h" namespace esphome { namespace max7219 { class MAX7219Component; -using max7219_writer_t = std::function; +using max7219_writer_t = display::DisplayWriter; class MAX7219Component : public PollingComponent, public spi::SPIDevice writer_{}; + max7219_writer_t writer_{}; }; } // namespace max7219 diff --git a/esphome/components/max7219digit/max7219digit.h b/esphome/components/max7219digit/max7219digit.h index ead8033803..af419b9b38 100644 --- a/esphome/components/max7219digit/max7219digit.h +++ b/esphome/components/max7219digit/max7219digit.h @@ -23,7 +23,7 @@ enum ScrollMode { class MAX7219Component; -using max7219_writer_t = std::function; +using max7219_writer_t = display::DisplayWriter; class MAX7219Component : public display::DisplayBuffer, public spi::SPIDevice writer_local_{}; + max7219_writer_t writer_local_{}; }; } // namespace max7219digit diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index c078ab9d56..61068b52fc 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -9,6 +9,7 @@ #include "esphome/components/uart/uart.h" #include "nextion_base.h" #include "nextion_component.h" +#include "esphome/components/display/display.h" #include "esphome/components/display/display_color_utils.h" #ifdef USE_NEXTION_TFT_UPLOAD @@ -31,7 +32,7 @@ namespace nextion { class Nextion; class NextionComponentBase; -using nextion_writer_t = std::function; +using nextion_writer_t = display::DisplayWriter; static const std::string COMMAND_DELIMITER{static_cast(255), static_cast(255), static_cast(255)}; @@ -1471,7 +1472,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe CallbackManager touch_callback_{}; CallbackManager buffer_overflow_callback_{}; - optional writer_; + nextion_writer_t writer_; optional brightness_; #ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO diff --git a/esphome/components/pvvx_mithermometer/display/pvvx_display.h b/esphome/components/pvvx_mithermometer/display/pvvx_display.h index c7fc523420..8637506bae 100644 --- a/esphome/components/pvvx_mithermometer/display/pvvx_display.h +++ b/esphome/components/pvvx_mithermometer/display/pvvx_display.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/components/ble_client/ble_client.h" +#include "esphome/components/display/display.h" #include @@ -29,7 +30,7 @@ enum UNIT { UNIT_DEG_E, ///< show "°E" }; -using pvvx_writer_t = std::function; +using pvvx_writer_t = display::DisplayWriter; class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent { public: @@ -126,7 +127,7 @@ class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent { esp32_ble_tracker::ESPBTUUID char_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw("00001f1f-0000-1000-8000-00805f9b34fb"); - optional writer_{}; + pvvx_writer_t writer_{}; }; } // namespace pvvx_mithermometer diff --git a/esphome/components/st7920/st7920.h b/esphome/components/st7920/st7920.h index c9fdad454d..c48fe8cc1c 100644 --- a/esphome/components/st7920/st7920.h +++ b/esphome/components/st7920/st7920.h @@ -9,7 +9,7 @@ namespace st7920 { class ST7920; -using st7920_writer_t = std::function; +using st7920_writer_t = display::DisplayWriter; class ST7920 : public display::DisplayBuffer, public spi::SPIDevice writer_local_{}; + st7920_writer_t writer_local_{}; }; } // namespace st7920 diff --git a/esphome/components/tm1621/tm1621.h b/esphome/components/tm1621/tm1621.h index b9f330e96e..fe923417a6 100644 --- a/esphome/components/tm1621/tm1621.h +++ b/esphome/components/tm1621/tm1621.h @@ -3,13 +3,14 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/hal.h" +#include "esphome/components/display/display.h" namespace esphome { namespace tm1621 { class TM1621Display; -using tm1621_writer_t = std::function; +using tm1621_writer_t = display::DisplayWriter; class TM1621Display : public PollingComponent { public: @@ -59,7 +60,7 @@ class TM1621Display : public PollingComponent { GPIOPin *cs_pin_; GPIOPin *read_pin_; GPIOPin *write_pin_; - optional writer_{}; + tm1621_writer_t writer_{}; char row_[2][12]; uint8_t state_; uint8_t device_; diff --git a/esphome/components/tm1637/tm1637.h b/esphome/components/tm1637/tm1637.h index d44680c623..b9e96119e9 100644 --- a/esphome/components/tm1637/tm1637.h +++ b/esphome/components/tm1637/tm1637.h @@ -4,6 +4,7 @@ #include "esphome/core/defines.h" #include "esphome/core/hal.h" #include "esphome/core/time.h" +#include "esphome/components/display/display.h" #include @@ -19,7 +20,7 @@ class TM1637Display; class TM1637Key; #endif -using tm1637_writer_t = std::function; +using tm1637_writer_t = display::DisplayWriter; class TM1637Display : public PollingComponent { public: @@ -78,7 +79,7 @@ class TM1637Display : public PollingComponent { uint8_t length_; bool inverted_; bool on_{true}; - optional writer_{}; + tm1637_writer_t writer_{}; uint8_t buffer_[6] = {0}; #ifdef USE_BINARY_SENSOR std::vector tm1637_keys_{}; diff --git a/esphome/components/tm1638/tm1638.h b/esphome/components/tm1638/tm1638.h index add72cfdf3..f6b2922ecf 100644 --- a/esphome/components/tm1638/tm1638.h +++ b/esphome/components/tm1638/tm1638.h @@ -5,6 +5,7 @@ #include "esphome/core/defines.h" #include "esphome/core/hal.h" #include "esphome/core/time.h" +#include "esphome/components/display/display.h" #include @@ -18,7 +19,7 @@ class KeyListener { class TM1638Component; -using tm1638_writer_t = std::function; +using tm1638_writer_t = display::DisplayWriter; class TM1638Component : public PollingComponent { public: @@ -70,7 +71,7 @@ class TM1638Component : public PollingComponent { GPIOPin *stb_pin_; GPIOPin *dio_pin_; uint8_t *buffer_ = new uint8_t[8]; - optional writer_{}; + tm1638_writer_t writer_{}; std::vector listeners_{}; }; diff --git a/tests/components/image/test.host.yaml b/tests/components/image/test.host.yaml index 0411195e2a..aa45497088 100644 --- a/tests/components/image/test.host.yaml +++ b/tests/components/image/test.host.yaml @@ -1,5 +1,6 @@ display: - platform: sdl + id: image_display auto_clear_enabled: false dimensions: width: 480 diff --git a/tests/components/sdl/common.yaml b/tests/components/sdl/common.yaml index 52991d595c..66f93915b6 100644 --- a/tests/components/sdl/common.yaml +++ b/tests/components/sdl/common.yaml @@ -13,11 +13,14 @@ display: binary_sensor: - platform: sdl + sdl_id: sdl_display id: key_up key: SDLK_UP - platform: sdl + sdl_id: sdl_display id: key_down key: SDLK_DOWN - platform: sdl + sdl_id: sdl_display id: key_enter key: SDLK_RETURN From 6f7e54c3f3ba979eee8f4200a8b298a7dfd61b68 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 4 Nov 2025 16:33:01 -0600 Subject: [PATCH 392/526] [select] Refactor to index-based operations for immediate and future RAM savings (#11623) --- esphome/components/api/api_connection.cpp | 2 +- .../components/copy/select/copy_select.cpp | 8 +- esphome/components/copy/select/copy_select.h | 2 +- .../display_menu_base/menu_item.cpp | 2 +- esphome/components/ld2410/ld2410.cpp | 14 +- esphome/components/ld2410/ld2410.h | 4 +- .../ld2410/select/baud_rate_select.cpp | 6 +- .../ld2410/select/baud_rate_select.h | 2 +- .../select/distance_resolution_select.cpp | 6 +- .../select/distance_resolution_select.h | 2 +- .../select/light_out_control_select.cpp | 4 +- .../ld2410/select/light_out_control_select.h | 2 +- esphome/components/ld2412/ld2412.cpp | 14 +- esphome/components/ld2412/ld2412.h | 4 +- .../ld2412/select/baud_rate_select.cpp | 6 +- .../ld2412/select/baud_rate_select.h | 2 +- .../select/distance_resolution_select.cpp | 6 +- .../select/distance_resolution_select.h | 2 +- .../select/light_out_control_select.cpp | 4 +- .../ld2412/select/light_out_control_select.h | 2 +- esphome/components/ld2420/ld2420.cpp | 6 +- esphome/components/ld2420/ld2420.h | 2 +- .../ld2420/select/operating_mode_select.cpp | 6 +- .../ld2420/select/operating_mode_select.h | 2 +- esphome/components/ld2450/ld2450.cpp | 12 +- esphome/components/ld2450/ld2450.h | 4 +- .../ld2450/select/baud_rate_select.cpp | 6 +- .../ld2450/select/baud_rate_select.h | 2 +- .../ld2450/select/zone_type_select.cpp | 6 +- .../ld2450/select/zone_type_select.h | 2 +- .../logger/select/logger_level_select.cpp | 13 +- .../logger/select/logger_level_select.h | 2 +- esphome/components/mqtt/mqtt_select.cpp | 5 +- .../seeed_mr24hpc1/seeed_mr24hpc1.cpp | 10 +- .../seeed_mr60fda2/seeed_mr60fda2.cpp | 6 +- esphome/components/select/select.cpp | 49 ++++-- esphome/components/select/select.h | 49 +++++- esphome/components/select/select_call.cpp | 159 +++++++++--------- esphome/components/select/select_call.h | 6 +- .../template/select/template_select.cpp | 14 +- .../template/select/template_select.h | 2 +- .../components/tuya/select/tuya_select.cpp | 3 +- esphome/components/web_server/web_server.cpp | 10 +- esphome/components/web_server/web_server.h | 2 +- 44 files changed, 268 insertions(+), 204 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 3f1a076007..5ab8a6eb05 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -877,7 +877,7 @@ uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection bool is_single) { auto *select = static_cast(entity); SelectStateResponse resp; - resp.set_state(StringRef(select->state)); + resp.set_state(StringRef(select->current_option())); resp.missing_state = !select->has_state(); return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } diff --git a/esphome/components/copy/select/copy_select.cpp b/esphome/components/copy/select/copy_select.cpp index bdcbd0b42c..e45338e785 100644 --- a/esphome/components/copy/select/copy_select.cpp +++ b/esphome/components/copy/select/copy_select.cpp @@ -7,19 +7,19 @@ namespace copy { static const char *const TAG = "copy.select"; void CopySelect::setup() { - source_->add_on_state_callback([this](const std::string &value, size_t index) { this->publish_state(value); }); + source_->add_on_state_callback([this](const std::string &value, size_t index) { this->publish_state(index); }); traits.set_options(source_->traits.get_options()); if (source_->has_state()) - this->publish_state(source_->state); + this->publish_state(source_->active_index().value()); } void CopySelect::dump_config() { LOG_SELECT("", "Copy Select", this); } -void CopySelect::control(const std::string &value) { +void CopySelect::control(size_t index) { auto call = source_->make_call(); - call.set_option(value); + call.set_index(index); call.perform(); } diff --git a/esphome/components/copy/select/copy_select.h b/esphome/components/copy/select/copy_select.h index fb0aee86f6..bd74a93e82 100644 --- a/esphome/components/copy/select/copy_select.h +++ b/esphome/components/copy/select/copy_select.h @@ -13,7 +13,7 @@ class CopySelect : public select::Select, public Component { void dump_config() override; protected: - void control(const std::string &value) override; + void control(size_t index) override; select::Select *source_; }; diff --git a/esphome/components/display_menu_base/menu_item.cpp b/esphome/components/display_menu_base/menu_item.cpp index 2c7f34c493..8224adf3fe 100644 --- a/esphome/components/display_menu_base/menu_item.cpp +++ b/esphome/components/display_menu_base/menu_item.cpp @@ -42,7 +42,7 @@ std::string MenuItemSelect::get_value_text() const { result = this->value_getter_.value()(this); } else { if (this->select_var_ != nullptr) { - result = this->select_var_->state; + result = this->select_var_->current_option(); } } diff --git a/esphome/components/ld2410/ld2410.cpp b/esphome/components/ld2410/ld2410.cpp index 5c3af54ad8..608882565f 100644 --- a/esphome/components/ld2410/ld2410.cpp +++ b/esphome/components/ld2410/ld2410.cpp @@ -121,9 +121,9 @@ constexpr Uint8ToString OUT_PIN_LEVELS_BY_UINT[] = { }; // Helper functions for lookups -template uint8_t find_uint8(const StringToUint8 (&arr)[N], const std::string &str) { +template uint8_t find_uint8(const StringToUint8 (&arr)[N], const char *str) { for (const auto &entry : arr) { - if (str == entry.str) + if (strcmp(str, entry.str) == 0) return entry.value; } return 0xFF; // Not found @@ -441,7 +441,7 @@ bool LD2410Component::handle_ack_data_() { ESP_LOGV(TAG, "Baud rate change"); #ifdef USE_SELECT if (this->baud_rate_select_ != nullptr) { - ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->state.c_str()); + ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->current_option()); } #endif break; @@ -626,14 +626,14 @@ void LD2410Component::set_bluetooth(bool enable) { this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); } -void LD2410Component::set_distance_resolution(const std::string &state) { +void LD2410Component::set_distance_resolution(const char *state) { this->set_config_mode_(true); const uint8_t cmd_value[2] = {find_uint8(DISTANCE_RESOLUTIONS_BY_STR, state), 0x00}; this->send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, sizeof(cmd_value)); this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); } -void LD2410Component::set_baud_rate(const std::string &state) { +void LD2410Component::set_baud_rate(const char *state) { this->set_config_mode_(true); const uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00}; this->send_command_(CMD_SET_BAUD_RATE, cmd_value, sizeof(cmd_value)); @@ -759,10 +759,10 @@ void LD2410Component::set_light_out_control() { #endif #ifdef USE_SELECT if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) { - this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->state); + this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->current_option()); } if (this->out_pin_level_select_ != nullptr && this->out_pin_level_select_->has_state()) { - this->out_pin_level_ = find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->state); + this->out_pin_level_ = find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option()); } #endif this->set_config_mode_(true); diff --git a/esphome/components/ld2410/ld2410.h b/esphome/components/ld2410/ld2410.h index 54fe1ce14d..52cf76b5b6 100644 --- a/esphome/components/ld2410/ld2410.h +++ b/esphome/components/ld2410/ld2410.h @@ -98,8 +98,8 @@ class LD2410Component : public Component, public uart::UARTDevice { void read_all_info(); void restart_and_read_all_info(); void set_bluetooth(bool enable); - void set_distance_resolution(const std::string &state); - void set_baud_rate(const std::string &state); + void set_distance_resolution(const char *state); + void set_baud_rate(const char *state); void factory_reset(); protected: diff --git a/esphome/components/ld2410/select/baud_rate_select.cpp b/esphome/components/ld2410/select/baud_rate_select.cpp index f4e0b90e2e..6da7c1d5f5 100644 --- a/esphome/components/ld2410/select/baud_rate_select.cpp +++ b/esphome/components/ld2410/select/baud_rate_select.cpp @@ -3,9 +3,9 @@ namespace esphome { namespace ld2410 { -void BaudRateSelect::control(const std::string &value) { - this->publish_state(value); - this->parent_->set_baud_rate(state); +void BaudRateSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_baud_rate(this->option_at(index)); } } // namespace ld2410 diff --git a/esphome/components/ld2410/select/baud_rate_select.h b/esphome/components/ld2410/select/baud_rate_select.h index 3827b6a48a..9385c8cf7e 100644 --- a/esphome/components/ld2410/select/baud_rate_select.h +++ b/esphome/components/ld2410/select/baud_rate_select.h @@ -11,7 +11,7 @@ class BaudRateSelect : public select::Select, public Parented { BaudRateSelect() = default; protected: - void control(const std::string &value) override; + void control(size_t index) override; }; } // namespace ld2410 diff --git a/esphome/components/ld2410/select/distance_resolution_select.cpp b/esphome/components/ld2410/select/distance_resolution_select.cpp index eef34bda63..4fc4c5af02 100644 --- a/esphome/components/ld2410/select/distance_resolution_select.cpp +++ b/esphome/components/ld2410/select/distance_resolution_select.cpp @@ -3,9 +3,9 @@ namespace esphome { namespace ld2410 { -void DistanceResolutionSelect::control(const std::string &value) { - this->publish_state(value); - this->parent_->set_distance_resolution(state); +void DistanceResolutionSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_distance_resolution(this->option_at(index)); } } // namespace ld2410 diff --git a/esphome/components/ld2410/select/distance_resolution_select.h b/esphome/components/ld2410/select/distance_resolution_select.h index d6affb1020..1a04f843a6 100644 --- a/esphome/components/ld2410/select/distance_resolution_select.h +++ b/esphome/components/ld2410/select/distance_resolution_select.h @@ -11,7 +11,7 @@ class DistanceResolutionSelect : public select::Select, public Parentedpublish_state(value); +void LightOutControlSelect::control(size_t index) { + this->publish_state(index); this->parent_->set_light_out_control(); } diff --git a/esphome/components/ld2410/select/light_out_control_select.h b/esphome/components/ld2410/select/light_out_control_select.h index 5d72e1774e..e8cd8f1d6a 100644 --- a/esphome/components/ld2410/select/light_out_control_select.h +++ b/esphome/components/ld2410/select/light_out_control_select.h @@ -11,7 +11,7 @@ class LightOutControlSelect : public select::Select, public Parented uint8_t find_uint8(const StringToUint8 (&arr)[N], const std::string &str) { +template uint8_t find_uint8(const StringToUint8 (&arr)[N], const char *str) { for (const auto &entry : arr) { - if (str == entry.str) { + if (strcmp(str, entry.str) == 0) { return entry.value; } } @@ -485,7 +485,7 @@ bool LD2412Component::handle_ack_data_() { ESP_LOGV(TAG, "Baud rate change"); #ifdef USE_SELECT if (this->baud_rate_select_ != nullptr) { - ESP_LOGW(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->state.c_str()); + ESP_LOGW(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->current_option()); } #endif break; @@ -699,14 +699,14 @@ void LD2412Component::set_bluetooth(bool enable) { this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); } -void LD2412Component::set_distance_resolution(const std::string &state) { +void LD2412Component::set_distance_resolution(const char *state) { this->set_config_mode_(true); const uint8_t cmd_value[6] = {find_uint8(DISTANCE_RESOLUTIONS_BY_STR, state), 0x00, 0x00, 0x00, 0x00, 0x00}; this->send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, sizeof(cmd_value)); this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); } -void LD2412Component::set_baud_rate(const std::string &state) { +void LD2412Component::set_baud_rate(const char *state) { this->set_config_mode_(true); const uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00}; this->send_command_(CMD_SET_BAUD_RATE, cmd_value, sizeof(cmd_value)); @@ -783,7 +783,7 @@ void LD2412Component::set_basic_config() { 1, TOTAL_GATES, DEFAULT_PRESENCE_TIMEOUT, 0, #endif #ifdef USE_SELECT - find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->state), + find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option()), #else 0x01, // Default value if not using select #endif @@ -837,7 +837,7 @@ void LD2412Component::set_light_out_control() { #endif #ifdef USE_SELECT if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) { - this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->state); + this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->current_option()); } #endif uint8_t value[2] = {this->light_function_, this->light_threshold_}; diff --git a/esphome/components/ld2412/ld2412.h b/esphome/components/ld2412/ld2412.h index 41f96ab301..2bed34bdd8 100644 --- a/esphome/components/ld2412/ld2412.h +++ b/esphome/components/ld2412/ld2412.h @@ -99,8 +99,8 @@ class LD2412Component : public Component, public uart::UARTDevice { void read_all_info(); void restart_and_read_all_info(); void set_bluetooth(bool enable); - void set_distance_resolution(const std::string &state); - void set_baud_rate(const std::string &state); + void set_distance_resolution(const char *state); + void set_baud_rate(const char *state); void factory_reset(); void start_dynamic_background_correction(); diff --git a/esphome/components/ld2412/select/baud_rate_select.cpp b/esphome/components/ld2412/select/baud_rate_select.cpp index 2291a81896..7bc4683853 100644 --- a/esphome/components/ld2412/select/baud_rate_select.cpp +++ b/esphome/components/ld2412/select/baud_rate_select.cpp @@ -3,9 +3,9 @@ namespace esphome { namespace ld2412 { -void BaudRateSelect::control(const std::string &value) { - this->publish_state(value); - this->parent_->set_baud_rate(state); +void BaudRateSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_baud_rate(this->option_at(index)); } } // namespace ld2412 diff --git a/esphome/components/ld2412/select/baud_rate_select.h b/esphome/components/ld2412/select/baud_rate_select.h index 2ae33551fb..ffe0329341 100644 --- a/esphome/components/ld2412/select/baud_rate_select.h +++ b/esphome/components/ld2412/select/baud_rate_select.h @@ -11,7 +11,7 @@ class BaudRateSelect : public select::Select, public Parented { BaudRateSelect() = default; protected: - void control(const std::string &value) override; + void control(size_t index) override; }; } // namespace ld2412 diff --git a/esphome/components/ld2412/select/distance_resolution_select.cpp b/esphome/components/ld2412/select/distance_resolution_select.cpp index a282215fbd..5a6f46a071 100644 --- a/esphome/components/ld2412/select/distance_resolution_select.cpp +++ b/esphome/components/ld2412/select/distance_resolution_select.cpp @@ -3,9 +3,9 @@ namespace esphome { namespace ld2412 { -void DistanceResolutionSelect::control(const std::string &value) { - this->publish_state(value); - this->parent_->set_distance_resolution(state); +void DistanceResolutionSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_distance_resolution(this->option_at(index)); } } // namespace ld2412 diff --git a/esphome/components/ld2412/select/distance_resolution_select.h b/esphome/components/ld2412/select/distance_resolution_select.h index 0658f5d1a7..842f63b7b1 100644 --- a/esphome/components/ld2412/select/distance_resolution_select.h +++ b/esphome/components/ld2412/select/distance_resolution_select.h @@ -11,7 +11,7 @@ class DistanceResolutionSelect : public select::Select, public Parentedpublish_state(value); +void LightOutControlSelect::control(size_t index) { + this->publish_state(index); this->parent_->set_light_out_control(); } diff --git a/esphome/components/ld2412/select/light_out_control_select.h b/esphome/components/ld2412/select/light_out_control_select.h index a71bab1e14..7a50970d0d 100644 --- a/esphome/components/ld2412/select/light_out_control_select.h +++ b/esphome/components/ld2412/select/light_out_control_select.h @@ -11,7 +11,7 @@ class LightOutControlSelect : public select::Select, public Parentedtotal_sample_number_counter); } -void LD2420Component::set_operating_mode(const std::string &state) { +void LD2420Component::set_operating_mode(const char *state) { // If unsupported firmware ignore mode select if (ld2420::get_firmware_int(firmware_ver_) >= CALIBRATE_VERSION_MIN) { this->current_operating_mode = find_uint8(OP_MODE_BY_STR, state); diff --git a/esphome/components/ld2420/ld2420.h b/esphome/components/ld2420/ld2420.h index 812c408cfd..128baab604 100644 --- a/esphome/components/ld2420/ld2420.h +++ b/esphome/components/ld2420/ld2420.h @@ -107,7 +107,7 @@ class LD2420Component : public Component, public uart::UARTDevice { int send_cmd_from_array(CmdFrameT cmd_frame); void report_gate_data(); void handle_cmd_error(uint8_t error); - void set_operating_mode(const std::string &state); + void set_operating_mode(const char *state); void auto_calibrate_sensitivity(); void update_radar_data(uint16_t const *gate_energy, uint8_t sample_number); uint8_t set_config_mode(bool enable); diff --git a/esphome/components/ld2420/select/operating_mode_select.cpp b/esphome/components/ld2420/select/operating_mode_select.cpp index 2d576e7cc6..5bf80b33c9 100644 --- a/esphome/components/ld2420/select/operating_mode_select.cpp +++ b/esphome/components/ld2420/select/operating_mode_select.cpp @@ -7,9 +7,9 @@ namespace ld2420 { static const char *const TAG = "ld2420.select"; -void LD2420Select::control(const std::string &value) { - this->publish_state(value); - this->parent_->set_operating_mode(value); +void LD2420Select::control(size_t index) { + this->publish_state(index); + this->parent_->set_operating_mode(this->option_at(index)); } } // namespace ld2420 diff --git a/esphome/components/ld2420/select/operating_mode_select.h b/esphome/components/ld2420/select/operating_mode_select.h index 317b2af8c0..f59eb33432 100644 --- a/esphome/components/ld2420/select/operating_mode_select.h +++ b/esphome/components/ld2420/select/operating_mode_select.h @@ -11,7 +11,7 @@ class LD2420Select : public Component, public select::Select, public Parentedset_config_mode_(false); #ifdef USE_SELECT const auto baud_rate = std::to_string(this->parent_->get_baud_rate()); - if (this->baud_rate_select_ != nullptr && this->baud_rate_select_->state != baud_rate) { + if (this->baud_rate_select_ != nullptr && strcmp(this->baud_rate_select_->current_option(), baud_rate.c_str()) != 0) { this->baud_rate_select_->publish_state(baud_rate); } this->publish_zone_type(); @@ -635,7 +635,7 @@ bool LD2450Component::handle_ack_data_() { ESP_LOGV(TAG, "Baud rate change"); #ifdef USE_SELECT if (this->baud_rate_select_ != nullptr) { - ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->state.c_str()); + ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->current_option()); } #endif break; @@ -716,7 +716,7 @@ bool LD2450Component::handle_ack_data_() { this->publish_zone_type(); #ifdef USE_SELECT if (this->zone_type_select_ != nullptr) { - ESP_LOGV(TAG, "Change zone type to: %s", this->zone_type_select_->state.c_str()); + ESP_LOGV(TAG, "Change zone type to: %s", this->zone_type_select_->current_option()); } #endif if (this->buffer_data_[10] == 0x00) { @@ -790,7 +790,7 @@ void LD2450Component::set_bluetooth(bool enable) { } // Set Baud rate -void LD2450Component::set_baud_rate(const std::string &state) { +void LD2450Component::set_baud_rate(const char *state) { this->set_config_mode_(true); const uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00}; this->send_command_(CMD_SET_BAUD_RATE, cmd_value, sizeof(cmd_value)); @@ -798,8 +798,8 @@ void LD2450Component::set_baud_rate(const std::string &state) { } // Set Zone Type - one of: Disabled, Detection, Filter -void LD2450Component::set_zone_type(const std::string &state) { - ESP_LOGV(TAG, "Set zone type: %s", state.c_str()); +void LD2450Component::set_zone_type(const char *state) { + ESP_LOGV(TAG, "Set zone type: %s", state); uint8_t zone_type = find_uint8(ZONE_TYPE_BY_STR, state); this->zone_type_ = zone_type; this->send_set_zone_command_(); diff --git a/esphome/components/ld2450/ld2450.h b/esphome/components/ld2450/ld2450.h index 9faa189019..44b63be444 100644 --- a/esphome/components/ld2450/ld2450.h +++ b/esphome/components/ld2450/ld2450.h @@ -115,8 +115,8 @@ class LD2450Component : public Component, public uart::UARTDevice { void restart_and_read_all_info(); void set_bluetooth(bool enable); void set_multi_target(bool enable); - void set_baud_rate(const std::string &state); - void set_zone_type(const std::string &state); + void set_baud_rate(const char *state); + void set_zone_type(const char *state); void publish_zone_type(); void factory_reset(); #ifdef USE_TEXT_SENSOR diff --git a/esphome/components/ld2450/select/baud_rate_select.cpp b/esphome/components/ld2450/select/baud_rate_select.cpp index 06439aaa75..754972214e 100644 --- a/esphome/components/ld2450/select/baud_rate_select.cpp +++ b/esphome/components/ld2450/select/baud_rate_select.cpp @@ -3,9 +3,9 @@ namespace esphome { namespace ld2450 { -void BaudRateSelect::control(const std::string &value) { - this->publish_state(value); - this->parent_->set_baud_rate(state); +void BaudRateSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_baud_rate(this->option_at(index)); } } // namespace ld2450 diff --git a/esphome/components/ld2450/select/baud_rate_select.h b/esphome/components/ld2450/select/baud_rate_select.h index 04fe65b4fd..22810d5f13 100644 --- a/esphome/components/ld2450/select/baud_rate_select.h +++ b/esphome/components/ld2450/select/baud_rate_select.h @@ -11,7 +11,7 @@ class BaudRateSelect : public select::Select, public Parented { BaudRateSelect() = default; protected: - void control(const std::string &value) override; + void control(size_t index) override; }; } // namespace ld2450 diff --git a/esphome/components/ld2450/select/zone_type_select.cpp b/esphome/components/ld2450/select/zone_type_select.cpp index a9f6155142..1111428c7c 100644 --- a/esphome/components/ld2450/select/zone_type_select.cpp +++ b/esphome/components/ld2450/select/zone_type_select.cpp @@ -3,9 +3,9 @@ namespace esphome { namespace ld2450 { -void ZoneTypeSelect::control(const std::string &value) { - this->publish_state(value); - this->parent_->set_zone_type(state); +void ZoneTypeSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_zone_type(this->option_at(index)); } } // namespace ld2450 diff --git a/esphome/components/ld2450/select/zone_type_select.h b/esphome/components/ld2450/select/zone_type_select.h index 8aafeb6beb..fc95ec1021 100644 --- a/esphome/components/ld2450/select/zone_type_select.h +++ b/esphome/components/ld2450/select/zone_type_select.h @@ -11,7 +11,7 @@ class ZoneTypeSelect : public select::Select, public Parented { ZoneTypeSelect() = default; protected: - void control(const std::string &value) override; + void control(size_t index) override; }; } // namespace ld2450 diff --git a/esphome/components/logger/select/logger_level_select.cpp b/esphome/components/logger/select/logger_level_select.cpp index 6d60a3ae47..e2ec28a390 100644 --- a/esphome/components/logger/select/logger_level_select.cpp +++ b/esphome/components/logger/select/logger_level_select.cpp @@ -3,10 +3,10 @@ namespace esphome::logger { void LoggerLevelSelect::publish_state(int level) { - const auto &option = this->at(level_to_index(level)); - if (!option) + auto index = level_to_index(level); + if (!this->has_index(index)) return; - Select::publish_state(option.value()); + Select::publish_state(index); } void LoggerLevelSelect::setup() { @@ -14,11 +14,6 @@ void LoggerLevelSelect::setup() { this->publish_state(this->parent_->get_log_level()); } -void LoggerLevelSelect::control(const std::string &value) { - const auto index = this->index_of(value); - if (!index) - return; - this->parent_->set_log_level(index_to_level(index.value())); -} +void LoggerLevelSelect::control(size_t index) { this->parent_->set_log_level(index_to_level(index)); } } // namespace esphome::logger diff --git a/esphome/components/logger/select/logger_level_select.h b/esphome/components/logger/select/logger_level_select.h index 0631eca45d..950edd29ac 100644 --- a/esphome/components/logger/select/logger_level_select.h +++ b/esphome/components/logger/select/logger_level_select.h @@ -9,7 +9,7 @@ class LoggerLevelSelect : public Component, public select::Select, public Parent public: void publish_state(int level); void setup() override; - void control(const std::string &value) override; + void control(size_t index) override; protected: // Convert log level to option index (skip CONFIG at level 4) diff --git a/esphome/components/mqtt/mqtt_select.cpp b/esphome/components/mqtt/mqtt_select.cpp index b851348306..e1660b07ea 100644 --- a/esphome/components/mqtt/mqtt_select.cpp +++ b/esphome/components/mqtt/mqtt_select.cpp @@ -21,7 +21,8 @@ void MQTTSelectComponent::setup() { call.set_option(state); call.perform(); }); - this->select_->add_on_state_callback([this](const std::string &state, size_t index) { this->publish_state(state); }); + this->select_->add_on_state_callback( + [this](const std::string &state, size_t index) { this->publish_state(this->select_->option_at(index)); }); } void MQTTSelectComponent::dump_config() { @@ -44,7 +45,7 @@ void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon } bool MQTTSelectComponent::send_initial_state() { if (this->select_->has_state()) { - return this->publish_state(this->select_->state); + return this->publish_state(this->select_->current_option()); } else { return true; } diff --git a/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp b/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp index 76523ce5c0..4c0416d727 100644 --- a/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +++ b/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp @@ -435,12 +435,12 @@ void MR24HPC1Component::r24_frame_parse_open_underlying_information_(uint8_t *da } else if ((this->existence_boundary_select_ != nullptr) && ((data[FRAME_COMMAND_WORD_INDEX] == 0x0a) || (data[FRAME_COMMAND_WORD_INDEX] == 0x8a))) { if (this->existence_boundary_select_->has_index(data[FRAME_DATA_INDEX] - 1)) { - this->existence_boundary_select_->publish_state(S_BOUNDARY_STR[data[FRAME_DATA_INDEX] - 1]); + this->existence_boundary_select_->publish_state(data[FRAME_DATA_INDEX] - 1); } } else if ((this->motion_boundary_select_ != nullptr) && ((data[FRAME_COMMAND_WORD_INDEX] == 0x0b) || (data[FRAME_COMMAND_WORD_INDEX] == 0x8b))) { if (this->motion_boundary_select_->has_index(data[FRAME_DATA_INDEX] - 1)) { - this->motion_boundary_select_->publish_state(S_BOUNDARY_STR[data[FRAME_DATA_INDEX] - 1]); + this->motion_boundary_select_->publish_state(data[FRAME_DATA_INDEX] - 1); } } else if ((this->motion_trigger_number_ != nullptr) && ((data[FRAME_COMMAND_WORD_INDEX] == 0x0c) || (data[FRAME_COMMAND_WORD_INDEX] == 0x8c))) { @@ -515,7 +515,7 @@ void MR24HPC1Component::r24_frame_parse_work_status_(uint8_t *data) { ESP_LOGD(TAG, "Reply: get radar init status 0x%02X", data[FRAME_DATA_INDEX]); } else if (data[FRAME_COMMAND_WORD_INDEX] == 0x07) { if ((this->scene_mode_select_ != nullptr) && (this->scene_mode_select_->has_index(data[FRAME_DATA_INDEX]))) { - this->scene_mode_select_->publish_state(S_SCENE_STR[data[FRAME_DATA_INDEX]]); + this->scene_mode_select_->publish_state(data[FRAME_DATA_INDEX]); } else { ESP_LOGD(TAG, "Select has index offset %d Error", data[FRAME_DATA_INDEX]); } @@ -538,7 +538,7 @@ void MR24HPC1Component::r24_frame_parse_work_status_(uint8_t *data) { ESP_LOGD(TAG, "Reply: get radar init status 0x%02X", data[FRAME_DATA_INDEX]); } else if (data[FRAME_COMMAND_WORD_INDEX] == 0x87) { if ((this->scene_mode_select_ != nullptr) && (this->scene_mode_select_->has_index(data[FRAME_DATA_INDEX]))) { - this->scene_mode_select_->publish_state(S_SCENE_STR[data[FRAME_DATA_INDEX]]); + this->scene_mode_select_->publish_state(data[FRAME_DATA_INDEX]); } else { ESP_LOGD(TAG, "Select has index offset %d Error", data[FRAME_DATA_INDEX]); } @@ -581,7 +581,7 @@ void MR24HPC1Component::r24_frame_parse_human_information_(uint8_t *data) { ((data[FRAME_COMMAND_WORD_INDEX] == 0x0A) || (data[FRAME_COMMAND_WORD_INDEX] == 0x8A))) { // none:0x00 1s:0x01 30s:0x02 1min:0x03 2min:0x04 5min:0x05 10min:0x06 30min:0x07 1hour:0x08 if (data[FRAME_DATA_INDEX] < 9) { - this->unman_time_select_->publish_state(S_UNMANNED_TIME_STR[data[FRAME_DATA_INDEX]]); + this->unman_time_select_->publish_state(data[FRAME_DATA_INDEX]); } } else if ((this->keep_away_text_sensor_ != nullptr) && ((data[FRAME_COMMAND_WORD_INDEX] == 0x0B) || (data[FRAME_COMMAND_WORD_INDEX] == 0x8B))) { diff --git a/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp index dea7976578..7f8bd6a43c 100644 --- a/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +++ b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp @@ -292,7 +292,7 @@ void MR60FDA2Component::process_frame_() { install_height_float = bit_cast(current_install_height_int); uint32_t select_index = find_nearest_index(install_height_float, INSTALL_HEIGHT, 7); - this->install_height_select_->publish_state(this->install_height_select_->at(select_index).value()); + this->install_height_select_->publish_state(select_index); } if (this->height_threshold_select_ != nullptr) { @@ -301,7 +301,7 @@ void MR60FDA2Component::process_frame_() { height_threshold_float = bit_cast(current_height_threshold_int); size_t select_index = find_nearest_index(height_threshold_float, HEIGHT_THRESHOLD, 7); - this->height_threshold_select_->publish_state(this->height_threshold_select_->at(select_index).value()); + this->height_threshold_select_->publish_state(select_index); } if (this->sensitivity_select_ != nullptr) { @@ -309,7 +309,7 @@ void MR60FDA2Component::process_frame_() { encode_uint32(current_data_buf_[11], current_data_buf_[10], current_data_buf_[9], current_data_buf_[8]); uint32_t select_index = find_nearest_index(current_sensitivity, SENSITIVITY, 3); - this->sensitivity_select_->publish_state(this->sensitivity_select_->at(select_index).value()); + this->sensitivity_select_->publish_state(select_index); } ESP_LOGD(TAG, "Mounting height: %.2f, Height threshold: %.2f, Sensitivity: %" PRIu32, install_height_float, diff --git a/esphome/components/select/select.cpp b/esphome/components/select/select.cpp index 5e30be3c13..6bb01ba6e2 100644 --- a/esphome/components/select/select.cpp +++ b/esphome/components/select/select.cpp @@ -7,24 +7,43 @@ namespace select { static const char *const TAG = "select"; -void Select::publish_state(const std::string &state) { +void Select::publish_state(const std::string &state) { this->publish_state(state.c_str()); } + +void Select::publish_state(const char *state) { auto index = this->index_of(state); - const auto *name = this->get_name().c_str(); if (index.has_value()) { - this->set_has_state(true); - this->state = state; - ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", name, state.c_str(), index.value()); - this->state_callback_.call(state, index.value()); + this->publish_state(index.value()); } else { - ESP_LOGE(TAG, "'%s': invalid state for publish_state(): %s", name, state.c_str()); + ESP_LOGE(TAG, "'%s': Invalid option %s", this->get_name().c_str(), state); } } +void Select::publish_state(size_t index) { + if (!this->has_index(index)) { + ESP_LOGE(TAG, "'%s': Invalid index %zu", this->get_name().c_str(), index); + return; + } + const char *option = this->option_at(index); + this->set_has_state(true); + this->active_index_ = index; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + this->state = option; // Update deprecated member for backward compatibility +#pragma GCC diagnostic pop + ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", this->get_name().c_str(), option, index); + // Callback signature requires std::string, create temporary for compatibility + this->state_callback_.call(std::string(option), index); +} + +const char *Select::current_option() const { return this->has_state() ? this->option_at(this->active_index_) : ""; } + void Select::add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } -bool Select::has_option(const std::string &option) const { return this->index_of(option).has_value(); } +bool Select::has_option(const std::string &option) const { return this->index_of(option.c_str()).has_value(); } + +bool Select::has_option(const char *option) const { return this->index_of(option).has_value(); } bool Select::has_index(size_t index) const { return index < this->size(); } @@ -33,10 +52,12 @@ size_t Select::size() const { return options.size(); } -optional Select::index_of(const std::string &option) const { +optional Select::index_of(const std::string &option) const { return this->index_of(option.c_str()); } + +optional Select::index_of(const char *option) const { const auto &options = traits.get_options(); for (size_t i = 0; i < options.size(); i++) { - if (strcmp(options[i], option.c_str()) == 0) { + if (strcmp(options[i], option) == 0) { return i; } } @@ -45,19 +66,17 @@ optional Select::index_of(const std::string &option) const { optional Select::active_index() const { if (this->has_state()) { - return this->index_of(this->state); - } else { - return {}; + return this->active_index_; } + return {}; } optional Select::at(size_t index) const { if (this->has_index(index)) { const auto &options = traits.get_options(); return std::string(options.at(index)); - } else { - return {}; } + return {}; } const char *Select::option_at(size_t index) const { return traits.get_options().at(index); } diff --git a/esphome/components/select/select.h b/esphome/components/select/select.h index eabb39898b..f859594cd1 100644 --- a/esphome/components/select/select.h +++ b/esphome/components/select/select.h @@ -30,16 +30,31 @@ namespace select { */ class Select : public EntityBase { public: - std::string state; SelectTraits traits; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + /// @deprecated Use current_option() instead. This member will be removed in ESPHome 2026.5.0. + __attribute__((deprecated("Use current_option() instead of .state. Will be removed in 2026.5.0"))) + std::string state{}; + + Select() = default; + ~Select() = default; +#pragma GCC diagnostic pop + void publish_state(const std::string &state); + void publish_state(const char *state); + void publish_state(size_t index); + + /// Return the currently selected option (as const char* from flash). + const char *current_option() const; /// Instantiate a SelectCall object to modify this select component's state. SelectCall make_call() { return SelectCall(this); } /// Return whether this select component contains the provided option. bool has_option(const std::string &option) const; + bool has_option(const char *option) const; /// Return whether this select component contains the provided index offset. bool has_index(size_t index) const; @@ -49,6 +64,7 @@ class Select : public EntityBase { /// Find the (optional) index offset of the provided option value. optional index_of(const std::string &option) const; + optional index_of(const char *option) const; /// Return the (optional) index offset of the currently active option. optional active_index() const; @@ -64,13 +80,36 @@ class Select : public EntityBase { protected: friend class SelectCall; - /** Set the value of the select, this is a virtual method that each select integration must implement. + size_t active_index_{0}; + + /** Set the value of the select by index, this is an optional virtual method. * - * This method is called by the SelectCall. + * IMPORTANT: At least ONE of the two control() methods must be overridden by derived classes. + * Overriding this index-based version is PREFERRED as it avoids string conversions. * - * @param value The value as validated by the SelectCall. + * This method is called by the SelectCall when the index is already known. + * Default implementation converts to string and calls control(const std::string&). + * + * @param index The index as validated by the SelectCall. */ - virtual void control(const std::string &value) = 0; + virtual void control(size_t index) { this->control(this->option_at(index)); } + + /** Set the value of the select, this is a virtual method that each select integration can implement. + * + * IMPORTANT: At least ONE of the two control() methods must be overridden by derived classes. + * Overriding control(size_t) is PREFERRED as it avoids string conversions. + * + * This method is called by control(size_t) when not overridden, or directly by external code. + * Default implementation converts to index and calls control(size_t). + * + * @param value The value as validated by the caller. + */ + virtual void control(const std::string &value) { + auto index = this->index_of(value); + if (index.has_value()) { + this->control(index.value()); + } + } CallbackManager state_callback_; }; diff --git a/esphome/components/select/select_call.cpp b/esphome/components/select/select_call.cpp index dd398b4052..aa7559e24e 100644 --- a/esphome/components/select/select_call.cpp +++ b/esphome/components/select/select_call.cpp @@ -7,19 +7,21 @@ namespace select { static const char *const TAG = "select"; -SelectCall &SelectCall::set_option(const std::string &option) { - return with_operation(SELECT_OP_SET).with_option(option); +SelectCall &SelectCall::set_option(const std::string &option) { return this->with_option(option); } + +SelectCall &SelectCall::set_option(const char *option) { return this->with_option(option); } + +SelectCall &SelectCall::set_index(size_t index) { return this->with_index(index); } + +SelectCall &SelectCall::select_next(bool cycle) { return this->with_operation(SELECT_OP_NEXT).with_cycle(cycle); } + +SelectCall &SelectCall::select_previous(bool cycle) { + return this->with_operation(SELECT_OP_PREVIOUS).with_cycle(cycle); } -SelectCall &SelectCall::set_index(size_t index) { return with_operation(SELECT_OP_SET_INDEX).with_index(index); } +SelectCall &SelectCall::select_first() { return this->with_operation(SELECT_OP_FIRST); } -SelectCall &SelectCall::select_next(bool cycle) { return with_operation(SELECT_OP_NEXT).with_cycle(cycle); } - -SelectCall &SelectCall::select_previous(bool cycle) { return with_operation(SELECT_OP_PREVIOUS).with_cycle(cycle); } - -SelectCall &SelectCall::select_first() { return with_operation(SELECT_OP_FIRST); } - -SelectCall &SelectCall::select_last() { return with_operation(SELECT_OP_LAST); } +SelectCall &SelectCall::select_last() { return this->with_operation(SELECT_OP_LAST); } SelectCall &SelectCall::with_operation(SelectOperation operation) { this->operation_ = operation; @@ -31,89 +33,96 @@ SelectCall &SelectCall::with_cycle(bool cycle) { return *this; } -SelectCall &SelectCall::with_option(const std::string &option) { - this->option_ = option; +SelectCall &SelectCall::with_option(const std::string &option) { return this->with_option(option.c_str()); } + +SelectCall &SelectCall::with_option(const char *option) { + this->operation_ = SELECT_OP_SET; + // Find the option index - this validates the option exists + this->index_ = this->parent_->index_of(option); return *this; } SelectCall &SelectCall::with_index(size_t index) { - this->index_ = index; + this->operation_ = SELECT_OP_SET; + if (index >= this->parent_->size()) { + ESP_LOGW(TAG, "'%s' - Index value %zu out of bounds", this->parent_->get_name().c_str(), index); + this->index_ = {}; // Store nullopt for invalid index + } else { + this->index_ = index; + } return *this; } +optional SelectCall::calculate_target_index_(const char *name) { + const auto &options = this->parent_->traits.get_options(); + if (options.empty()) { + ESP_LOGW(TAG, "'%s' - Select has no options", name); + return {}; + } + + if (this->operation_ == SELECT_OP_FIRST) { + return 0; + } + + if (this->operation_ == SELECT_OP_LAST) { + return options.size() - 1; + } + + if (this->operation_ == SELECT_OP_SET) { + ESP_LOGD(TAG, "'%s' - Setting", name); + if (!this->index_.has_value()) { + ESP_LOGW(TAG, "'%s' - No option set", name); + return {}; + } + return this->index_.value(); + } + + // SELECT_OP_NEXT or SELECT_OP_PREVIOUS + ESP_LOGD(TAG, "'%s' - Selecting %s, with%s cycling", name, + this->operation_ == SELECT_OP_NEXT ? LOG_STR_LITERAL("next") : LOG_STR_LITERAL("previous"), + this->cycle_ ? LOG_STR_LITERAL("") : LOG_STR_LITERAL("out")); + + const auto size = options.size(); + if (!this->parent_->has_state()) { + return this->operation_ == SELECT_OP_NEXT ? 0 : size - 1; + } + + // Use cached active_index_ instead of index_of() lookup + const auto active_index = this->parent_->active_index_; + if (this->cycle_) { + return (size + active_index + (this->operation_ == SELECT_OP_NEXT ? +1 : -1)) % size; + } + + if (this->operation_ == SELECT_OP_PREVIOUS && active_index > 0) { + return active_index - 1; + } + + if (this->operation_ == SELECT_OP_NEXT && active_index < size - 1) { + return active_index + 1; + } + + return {}; // Can't navigate further without cycling +} + void SelectCall::perform() { auto *parent = this->parent_; const auto *name = parent->get_name().c_str(); - const auto &traits = parent->traits; - const auto &options = traits.get_options(); if (this->operation_ == SELECT_OP_NONE) { ESP_LOGW(TAG, "'%s' - SelectCall performed without selecting an operation", name); return; } - if (options.empty()) { - ESP_LOGW(TAG, "'%s' - Cannot perform SelectCall, select has no options", name); + + // Calculate target index (with_index() and with_option() already validate bounds/existence) + auto target_index = this->calculate_target_index_(name); + if (!target_index.has_value()) { return; } - std::string target_value; - - if (this->operation_ == SELECT_OP_SET) { - ESP_LOGD(TAG, "'%s' - Setting", name); - if (!this->option_.has_value()) { - ESP_LOGW(TAG, "'%s' - No option value set for SelectCall", name); - return; - } - target_value = this->option_.value(); - } else if (this->operation_ == SELECT_OP_SET_INDEX) { - if (!this->index_.has_value()) { - ESP_LOGW(TAG, "'%s' - No index value set for SelectCall", name); - return; - } - if (this->index_.value() >= options.size()) { - ESP_LOGW(TAG, "'%s' - Index value %zu out of bounds", name, this->index_.value()); - return; - } - target_value = options[this->index_.value()]; - } else if (this->operation_ == SELECT_OP_FIRST) { - target_value = options.front(); - } else if (this->operation_ == SELECT_OP_LAST) { - target_value = options.back(); - } else if (this->operation_ == SELECT_OP_NEXT || this->operation_ == SELECT_OP_PREVIOUS) { - auto cycle = this->cycle_; - ESP_LOGD(TAG, "'%s' - Selecting %s, with%s cycling", name, this->operation_ == SELECT_OP_NEXT ? "next" : "previous", - cycle ? "" : "out"); - if (!parent->has_state()) { - target_value = this->operation_ == SELECT_OP_NEXT ? options.front() : options.back(); - } else { - auto index = parent->index_of(parent->state); - if (index.has_value()) { - auto size = options.size(); - if (cycle) { - auto use_index = (size + index.value() + (this->operation_ == SELECT_OP_NEXT ? +1 : -1)) % size; - target_value = options[use_index]; - } else { - if (this->operation_ == SELECT_OP_PREVIOUS && index.value() > 0) { - target_value = options[index.value() - 1]; - } else if (this->operation_ == SELECT_OP_NEXT && index.value() < options.size() - 1) { - target_value = options[index.value() + 1]; - } else { - return; - } - } - } else { - target_value = this->operation_ == SELECT_OP_NEXT ? options.front() : options.back(); - } - } - } - - if (!parent->has_option(target_value)) { - ESP_LOGW(TAG, "'%s' - Option %s is not a valid option", name, target_value.c_str()); - return; - } - - ESP_LOGD(TAG, "'%s' - Set selected option to: %s", name, target_value.c_str()); - parent->control(target_value); + auto idx = target_index.value(); + // All operations use indices, call control() by index to avoid string conversion + ESP_LOGD(TAG, "'%s' - Set selected option to: %s", name, parent->option_at(idx)); + parent->control(idx); } } // namespace select diff --git a/esphome/components/select/select_call.h b/esphome/components/select/select_call.h index efc9a982ec..eae7d3de1d 100644 --- a/esphome/components/select/select_call.h +++ b/esphome/components/select/select_call.h @@ -10,7 +10,6 @@ class Select; enum SelectOperation { SELECT_OP_NONE, SELECT_OP_SET, - SELECT_OP_SET_INDEX, SELECT_OP_NEXT, SELECT_OP_PREVIOUS, SELECT_OP_FIRST, @@ -23,6 +22,7 @@ class SelectCall { void perform(); SelectCall &set_option(const std::string &option); + SelectCall &set_option(const char *option); SelectCall &set_index(size_t index); SelectCall &select_next(bool cycle); @@ -33,11 +33,13 @@ class SelectCall { SelectCall &with_operation(SelectOperation operation); SelectCall &with_cycle(bool cycle); SelectCall &with_option(const std::string &option); + SelectCall &with_option(const char *option); SelectCall &with_index(size_t index); protected: + __attribute__((always_inline)) inline optional calculate_target_index_(const char *name); + Select *const parent_; - optional option_; optional index_; SelectOperation operation_{SELECT_OP_NONE}; bool cycle_; diff --git a/esphome/components/template/select/template_select.cpp b/esphome/components/template/select/template_select.cpp index 3ea34c3c7c..112f24e919 100644 --- a/esphome/components/template/select/template_select.cpp +++ b/esphome/components/template/select/template_select.cpp @@ -24,7 +24,7 @@ void TemplateSelect::setup() { ESP_LOGD(TAG, "State from initial: %s", this->option_at(index)); } - this->publish_state(this->at(index).value()); + this->publish_state(index); } void TemplateSelect::update() { @@ -41,16 +41,14 @@ void TemplateSelect::update() { } } -void TemplateSelect::control(const std::string &value) { - this->set_trigger_->trigger(value); +void TemplateSelect::control(size_t index) { + this->set_trigger_->trigger(std::string(this->option_at(index))); if (this->optimistic_) - this->publish_state(value); + this->publish_state(index); - if (this->restore_value_) { - auto index = this->index_of(value); - this->pref_.save(&index.value()); - } + if (this->restore_value_) + this->pref_.save(&index); } void TemplateSelect::dump_config() { diff --git a/esphome/components/template/select/template_select.h b/esphome/components/template/select/template_select.h index 1c33153872..2dad059ade 100644 --- a/esphome/components/template/select/template_select.h +++ b/esphome/components/template/select/template_select.h @@ -24,7 +24,7 @@ class TemplateSelect : public select::Select, public PollingComponent { void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; } protected: - void control(const std::string &value) override; + void control(size_t index) override; bool optimistic_ = false; size_t initial_option_index_{0}; bool restore_value_ = false; diff --git a/esphome/components/tuya/select/tuya_select.cpp b/esphome/components/tuya/select/tuya_select.cpp index 7c1cd09d06..07e3ce44ee 100644 --- a/esphome/components/tuya/select/tuya_select.cpp +++ b/esphome/components/tuya/select/tuya_select.cpp @@ -17,8 +17,7 @@ void TuyaSelect::setup() { return; } size_t mapping_idx = std::distance(mappings.cbegin(), it); - auto value = this->at(mapping_idx); - this->publish_state(value.value()); + this->publish_state(mapping_idx); }); } diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index acc0f33e61..f1d1a75875 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1188,7 +1188,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM if (request->method() == HTTP_GET && match.method_empty()) { auto detail = get_request_detail(request); - std::string data = this->select_json(obj, obj->state, detail); + std::string data = this->select_json(obj, obj->has_state() ? obj->current_option() : "", detail); request->send(200, "application/json", data.c_str()); return; } @@ -1208,12 +1208,14 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM request->send(404); } std::string WebServer::select_state_json_generator(WebServer *web_server, void *source) { - return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_STATE); + auto *obj = (select::Select *) (source); + return web_server->select_json(obj, obj->has_state() ? obj->current_option() : "", DETAIL_STATE); } std::string WebServer::select_all_json_generator(WebServer *web_server, void *source) { - return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_ALL); + auto *obj = (select::Select *) (source); + return web_server->select_json(obj, obj->has_state() ? obj->current_option() : "", DETAIL_ALL); } -std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) { +std::string WebServer::select_json(select::Select *obj, const char *value, JsonDetail start_config) { json::JsonBuilder builder; JsonObject root = builder.root(); diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index fb790483dc..328140cfae 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -410,7 +410,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { static std::string select_state_json_generator(WebServer *web_server, void *source); static std::string select_all_json_generator(WebServer *web_server, void *source); /// Dump the select state with its value as a JSON string. - std::string select_json(select::Select *obj, const std::string &value, JsonDetail start_config); + std::string select_json(select::Select *obj, const char *value, JsonDetail start_config); #endif #ifdef USE_CLIMATE From 64f8963566092f382f2b8c1556a201d0f48252be Mon Sep 17 00:00:00 2001 From: Gnuspice Date: Wed, 5 Nov 2025 12:46:06 +1300 Subject: [PATCH 393/526] [const] Move `CONF_ENABLED` to const.py (#11719) --- esphome/components/const/__init__.py | 1 + esphome/components/modbus_controller/__init__.py | 2 +- esphome/components/modbus_controller/const.py | 1 - esphome/components/wireguard/binary_sensor.py | 3 +-- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/esphome/components/const/__init__.py b/esphome/components/const/__init__.py index 2b88bb43a8..0c22b2d27e 100644 --- a/esphome/components/const/__init__.py +++ b/esphome/components/const/__init__.py @@ -8,6 +8,7 @@ BYTE_ORDER_BIG = "big_endian" CONF_COLOR_DEPTH = "color_depth" CONF_DRAW_ROUNDING = "draw_rounding" +CONF_ENABLED = "enabled" CONF_ON_RECEIVE = "on_receive" CONF_ON_STATE_CHANGE = "on_state_change" CONF_REQUEST_HEADERS = "request_headers" diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 28f3326c47..1c23783ce3 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -3,6 +3,7 @@ import binascii from esphome import automation import esphome.codegen as cg from esphome.components import modbus +from esphome.components.const import CONF_ENABLED import esphome.config_validation as cv from esphome.const import ( CONF_ADDRESS, @@ -20,7 +21,6 @@ from .const import ( CONF_BYTE_OFFSET, CONF_COMMAND_THROTTLE, CONF_CUSTOM_COMMAND, - CONF_ENABLED, CONF_FORCE_NEW_RANGE, CONF_MAX_CMD_RETRIES, CONF_MODBUS_CONTROLLER_ID, diff --git a/esphome/components/modbus_controller/const.py b/esphome/components/modbus_controller/const.py index ee0b5fc633..c689d84576 100644 --- a/esphome/components/modbus_controller/const.py +++ b/esphome/components/modbus_controller/const.py @@ -2,7 +2,6 @@ CONF_ALLOW_DUPLICATE_COMMANDS = "allow_duplicate_commands" CONF_BITMASK = "bitmask" CONF_BYTE_OFFSET = "byte_offset" CONF_COMMAND_THROTTLE = "command_throttle" -CONF_ENABLED = "enabled" CONF_OFFLINE_SKIP_UPDATES = "offline_skip_updates" CONF_CUSTOM_COMMAND = "custom_command" CONF_FORCE_NEW_RANGE = "force_new_range" diff --git a/esphome/components/wireguard/binary_sensor.py b/esphome/components/wireguard/binary_sensor.py index 02c4862e8d..2ba59d4c39 100644 --- a/esphome/components/wireguard/binary_sensor.py +++ b/esphome/components/wireguard/binary_sensor.py @@ -1,5 +1,6 @@ import esphome.codegen as cg from esphome.components import binary_sensor +from esphome.components.const import CONF_ENABLED import esphome.config_validation as cv from esphome.const import ( CONF_STATUS, @@ -9,8 +10,6 @@ from esphome.const import ( from . import CONF_WIREGUARD_ID, Wireguard -CONF_ENABLED = "enabled" - DEPENDENCIES = ["wireguard"] CONFIG_SCHEMA = { From 1446e7174ac96a7158d9d51fb21020e965d72873 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 4 Nov 2025 19:23:24 -0600 Subject: [PATCH 394/526] [core] Reduce action framework argument copies by 83% (#11704) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/ags10/ags10.h | 4 +- esphome/components/aic3204/automation.h | 2 +- .../alarm_control_panel/automation.h | 14 ++--- esphome/components/animation/animation.h | 6 +- esphome/components/api/api_server.h | 2 +- .../components/api/homeassistant_service.h | 2 +- esphome/components/at581x/automation.h | 4 +- esphome/components/audio_adc/automation.h | 2 +- esphome/components/audio_dac/automation.h | 6 +- esphome/components/binary_sensor/automation.h | 6 +- esphome/components/bl0906/bl0906.h | 2 +- esphome/components/ble_client/automation.h | 18 +++--- esphome/components/button/automation.h | 2 +- esphome/components/canbus/canbus.h | 2 +- esphome/components/climate/automation.h | 2 +- esphome/components/cm1106/cm1106.h | 2 +- esphome/components/cover/automation.h | 16 ++--- esphome/components/cs5460a/cs5460a.h | 2 +- esphome/components/datetime/date_entity.h | 2 +- esphome/components/datetime/datetime_entity.h | 2 +- esphome/components/datetime/time_entity.h | 2 +- .../deep_sleep/deep_sleep_component.h | 6 +- esphome/components/dfplayer/dfplayer.h | 16 ++--- .../components/dfrobot_sen0395/automation.h | 4 +- esphome/components/display/display.h | 8 +-- .../components/display_menu_base/automation.h | 18 +++--- esphome/components/ds1307/ds1307.h | 4 +- .../components/duty_time/duty_time_sensor.h | 8 +-- esphome/components/esp32_ble/ble.h | 6 +- .../esp32_ble_server/ble_server_automations.h | 6 +- .../components/esp32_ble_tracker/automation.h | 4 +- esphome/components/esp8266_pwm/esp8266_pwm.h | 2 +- esphome/components/esp_ldo/esp_ldo.h | 2 +- esphome/components/espnow/automation.h | 10 ++-- esphome/components/event/automation.h | 2 +- esphome/components/ezo/automation.h | 10 ++-- esphome/components/ezo_pmp/ezo_pmp.h | 24 ++++---- esphome/components/fan/automation.h | 12 ++-- .../fingerprint_grow/fingerprint_grow.h | 12 ++-- .../components/globals/globals_component.h | 2 +- .../grove_tb6612fng/grove_tb6612fng.h | 12 ++-- esphome/components/haier/automation.h | 26 ++++---- esphome/components/hbridge/fan/hbridge_fan.h | 2 +- .../components/http_request/http_request.h | 6 +- .../components/http_request/ota/automation.h | 2 +- esphome/components/htu21d/htu21d.h | 4 +- .../integration/integration_sensor.h | 2 +- .../components/key_collector/key_collector.h | 4 +- esphome/components/ld2410/automation.h | 2 +- esphome/components/ledc/ledc_output.h | 2 +- .../components/libretiny_pwm/libretiny_pwm.h | 2 +- esphome/components/light/automation.h | 12 ++-- esphome/components/lightwaverf/lightwaverf.h | 2 +- esphome/components/lock/automation.h | 8 +-- esphome/components/lvgl/lvgl_esphome.h | 6 +- esphome/components/max17043/automation.h | 2 +- esphome/components/max6956/automation.h | 4 +- esphome/components/max7219digit/automation.h | 8 +-- esphome/components/media_player/automation.h | 20 ++++--- esphome/components/mhz19/mhz19.h | 6 +- .../components/micro_wake_word/automation.h | 12 ++-- esphome/components/microphone/automation.h | 12 ++-- esphome/components/midea/ac_automations.h | 16 ++--- esphome/components/mixer/speaker/automation.h | 2 +- esphome/components/mqtt/mqtt_client.h | 10 ++-- esphome/components/nau7802/nau7802.h | 6 +- esphome/components/nextion/automation.h | 8 +-- esphome/components/number/automation.h | 6 +- .../components/online_image/online_image.h | 4 +- esphome/components/output/automation.h | 10 ++-- esphome/components/pcf85063/pcf85063.h | 4 +- esphome/components/pcf8563/pcf8563.h | 4 +- esphome/components/pid/pid_climate.h | 6 +- .../pipsolar/output/pipsolar_output.h | 2 +- esphome/components/pmwcs3/pmwcs3.h | 6 +- esphome/components/pn532/pn532.h | 2 +- esphome/components/pn7150/automation.h | 22 +++---- esphome/components/pn7160/automation.h | 22 +++---- esphome/components/pulse_counter/automation.h | 2 +- esphome/components/pulse_meter/automation.h | 2 +- esphome/components/pzemac/pzemac.h | 2 +- esphome/components/pzemdc/pzemdc.h | 2 +- esphome/components/remote_base/remote_base.h | 2 +- .../remote_transmitter/automation.h | 2 +- esphome/components/rf_bridge/rf_bridge.h | 16 ++--- .../rotary_encoder/rotary_encoder.h | 2 +- esphome/components/rp2040_pwm/rp2040_pwm.h | 2 +- esphome/components/rtttl/rtttl.h | 6 +- esphome/components/scd30/automation.h | 2 +- esphome/components/scd4x/automation.h | 4 +- esphome/components/script/script.h | 10 ++-- esphome/components/select/automation.h | 6 +- esphome/components/sen5x/automation.h | 2 +- esphome/components/senseair/senseair.h | 10 ++-- esphome/components/sensor/automation.h | 4 +- esphome/components/servo/servo.h | 4 +- esphome/components/sim800l/sim800l.h | 10 ++-- esphome/components/sound_level/sound_level.h | 4 +- esphome/components/speaker/automation.h | 16 ++--- .../speaker/media_player/automation.h | 2 +- esphome/components/sprinkler/automation.h | 30 +++++----- esphome/components/sps30/automation.h | 2 +- esphome/components/stepper/stepper.h | 10 ++-- esphome/components/sun/sun.h | 2 +- esphome/components/switch/automation.h | 12 ++-- esphome/components/sx126x/automation.h | 12 ++-- esphome/components/sx127x/automation.h | 12 ++-- esphome/components/template/lock/automation.h | 2 +- .../components/template/valve/automation.h | 2 +- esphome/components/text/automation.h | 2 +- esphome/components/text_sensor/automation.h | 4 +- esphome/components/time/real_time_clock.h | 2 +- esphome/components/tm1651/tm1651.h | 10 ++-- esphome/components/uart/automation.h | 2 +- esphome/components/udp/automation.h | 2 +- esphome/components/ufire_ec/ufire_ec.h | 4 +- esphome/components/ufire_ise/ufire_ise.h | 6 +- esphome/components/update/automation.h | 4 +- esphome/components/valve/automation.h | 14 ++--- .../voice_assistant/voice_assistant.h | 10 ++-- esphome/components/wifi/wifi_component.h | 10 ++-- esphome/components/wireguard/wireguard.h | 8 +-- esphome/core/automation.h | 14 ++--- esphome/core/base_automation.h | 60 +++++++++---------- .../loop_test_component/loop_test_component.h | 4 +- 125 files changed, 448 insertions(+), 446 deletions(-) diff --git a/esphome/components/ags10/ags10.h b/esphome/components/ags10/ags10.h index e0975f14bc..9e034b20cb 100644 --- a/esphome/components/ags10/ags10.h +++ b/esphome/components/ags10/ags10.h @@ -105,7 +105,7 @@ template class AGS10NewI2cAddressAction : public Action, public: TEMPLATABLE_VALUE(uint8_t, new_address) - void play(Ts... x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); } + void play(const Ts &...x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); } }; enum AGS10SetZeroPointActionMode { @@ -122,7 +122,7 @@ template class AGS10SetZeroPointAction : public Action, p TEMPLATABLE_VALUE(uint16_t, value) TEMPLATABLE_VALUE(AGS10SetZeroPointActionMode, mode) - void play(Ts... x) override { + void play(const Ts &...x) override { switch (this->mode_.value(x...)) { case FACTORY_DEFAULT: this->parent_->set_zero_point_with_factory_defaults(); diff --git a/esphome/components/aic3204/automation.h b/esphome/components/aic3204/automation.h index 416a88fa12..851ff930f8 100644 --- a/esphome/components/aic3204/automation.h +++ b/esphome/components/aic3204/automation.h @@ -13,7 +13,7 @@ template class SetAutoMuteAction : public Action { TEMPLATABLE_VALUE(uint8_t, auto_mute_mode) - void play(Ts... x) override { this->aic3204_->set_auto_mute_mode(this->auto_mute_mode_.value(x...)); } + void play(const Ts &...x) override { this->aic3204_->set_auto_mute_mode(this->auto_mute_mode_.value(x...)); } protected: AIC3204 *aic3204_; diff --git a/esphome/components/alarm_control_panel/automation.h b/esphome/components/alarm_control_panel/automation.h index 2177fb710f..db2ef78158 100644 --- a/esphome/components/alarm_control_panel/automation.h +++ b/esphome/components/alarm_control_panel/automation.h @@ -89,7 +89,7 @@ template class ArmAwayAction : public Action { TEMPLATABLE_VALUE(std::string, code) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->alarm_control_panel_->make_call(); auto code = this->code_.optional_value(x...); if (code.has_value()) { @@ -109,7 +109,7 @@ template class ArmHomeAction : public Action { TEMPLATABLE_VALUE(std::string, code) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->alarm_control_panel_->make_call(); auto code = this->code_.optional_value(x...); if (code.has_value()) { @@ -129,7 +129,7 @@ template class ArmNightAction : public Action { TEMPLATABLE_VALUE(std::string, code) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->alarm_control_panel_->make_call(); auto code = this->code_.optional_value(x...); if (code.has_value()) { @@ -149,7 +149,7 @@ template class DisarmAction : public Action { TEMPLATABLE_VALUE(std::string, code) - void play(Ts... x) override { this->alarm_control_panel_->disarm(this->code_.optional_value(x...)); } + void play(const Ts &...x) override { this->alarm_control_panel_->disarm(this->code_.optional_value(x...)); } protected: AlarmControlPanel *alarm_control_panel_; @@ -159,7 +159,7 @@ template class PendingAction : public Action { public: explicit PendingAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {} - void play(Ts... x) override { this->alarm_control_panel_->make_call().pending().perform(); } + void play(const Ts &...x) override { this->alarm_control_panel_->make_call().pending().perform(); } protected: AlarmControlPanel *alarm_control_panel_; @@ -169,7 +169,7 @@ template class TriggeredAction : public Action { public: explicit TriggeredAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {} - void play(Ts... x) override { this->alarm_control_panel_->make_call().triggered().perform(); } + void play(const Ts &...x) override { this->alarm_control_panel_->make_call().triggered().perform(); } protected: AlarmControlPanel *alarm_control_panel_; @@ -178,7 +178,7 @@ template class TriggeredAction : public Action { template class AlarmControlPanelCondition : public Condition { public: AlarmControlPanelCondition(AlarmControlPanel *parent) : parent_(parent) {} - bool check(Ts... x) override { + bool check(const Ts &...x) override { return this->parent_->is_state_armed(this->parent_->get_state()) || this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_TRIGGERED; } diff --git a/esphome/components/animation/animation.h b/esphome/components/animation/animation.h index c44e0060af..b33254df30 100644 --- a/esphome/components/animation/animation.h +++ b/esphome/components/animation/animation.h @@ -39,7 +39,7 @@ class Animation : public image::Image { template class AnimationNextFrameAction : public Action { public: AnimationNextFrameAction(Animation *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->next_frame(); } + void play(const Ts &...x) override { this->parent_->next_frame(); } protected: Animation *parent_; @@ -48,7 +48,7 @@ template class AnimationNextFrameAction : public Action { template class AnimationPrevFrameAction : public Action { public: AnimationPrevFrameAction(Animation *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->prev_frame(); } + void play(const Ts &...x) override { this->parent_->prev_frame(); } protected: Animation *parent_; @@ -58,7 +58,7 @@ template class AnimationSetFrameAction : public Action { public: AnimationSetFrameAction(Animation *parent) : parent_(parent) {} TEMPLATABLE_VALUE(uint16_t, frame) - void play(Ts... x) override { this->parent_->set_frame(this->frame_.value(x...)); } + void play(const Ts &...x) override { this->parent_->set_frame(this->frame_.value(x...)); } protected: Animation *parent_; diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index d29181250e..f1f44a266d 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -237,7 +237,7 @@ extern APIServer *global_api_server; // NOLINT(cppcoreguidelines-avoid-non-cons template class APIConnectedCondition : public Condition { public: - bool check(Ts... x) override { return global_api_server->is_connected(); } + bool check(const Ts &...x) override { return global_api_server->is_connected(); } }; } // namespace esphome::api diff --git a/esphome/components/api/homeassistant_service.h b/esphome/components/api/homeassistant_service.h index 4343fcd0bb..d00e9e6257 100644 --- a/esphome/components/api/homeassistant_service.h +++ b/esphome/components/api/homeassistant_service.h @@ -133,7 +133,7 @@ template class HomeAssistantServiceCallAction : public Action *get_error_trigger() const { return this->error_trigger_; } #endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES - void play(Ts... x) override { + void play(const Ts &...x) override { HomeassistantActionRequest resp; std::string service_value = this->service_.value(x...); resp.set_service(StringRef(service_value)); diff --git a/esphome/components/at581x/automation.h b/esphome/components/at581x/automation.h index 4863a87565..b1611a6758 100644 --- a/esphome/components/at581x/automation.h +++ b/esphome/components/at581x/automation.h @@ -10,7 +10,7 @@ namespace at581x { template class AT581XResetAction : public Action, public Parented { public: - void play(Ts... x) { this->parent_->reset_hardware_frontend(); } + void play(const Ts &...x) { this->parent_->reset_hardware_frontend(); } }; template class AT581XSettingsAction : public Action, public Parented { @@ -25,7 +25,7 @@ template class AT581XSettingsAction : public Action, publ TEMPLATABLE_VALUE(int, trigger_keep) TEMPLATABLE_VALUE(int, stage_gain) - void play(Ts... x) { + void play(const Ts &...x) { if (this->frequency_.has_value()) { int v = this->frequency_.value(x...); this->parent_->set_frequency(v); diff --git a/esphome/components/audio_adc/automation.h b/esphome/components/audio_adc/automation.h index 1b0bc2a6ad..0c42468479 100644 --- a/esphome/components/audio_adc/automation.h +++ b/esphome/components/audio_adc/automation.h @@ -13,7 +13,7 @@ template class SetMicGainAction : public Action { TEMPLATABLE_VALUE(float, mic_gain) - void play(Ts... x) override { this->audio_adc_->set_mic_gain(this->mic_gain_.value(x...)); } + void play(const Ts &...x) override { this->audio_adc_->set_mic_gain(this->mic_gain_.value(x...)); } protected: AudioAdc *audio_adc_; diff --git a/esphome/components/audio_dac/automation.h b/esphome/components/audio_dac/automation.h index b6cf2acaf4..3eb3441f3d 100644 --- a/esphome/components/audio_dac/automation.h +++ b/esphome/components/audio_dac/automation.h @@ -11,7 +11,7 @@ template class MuteOffAction : public Action { public: explicit MuteOffAction(AudioDac *audio_dac) : audio_dac_(audio_dac) {} - void play(Ts... x) override { this->audio_dac_->set_mute_off(); } + void play(const Ts &...x) override { this->audio_dac_->set_mute_off(); } protected: AudioDac *audio_dac_; @@ -21,7 +21,7 @@ template class MuteOnAction : public Action { public: explicit MuteOnAction(AudioDac *audio_dac) : audio_dac_(audio_dac) {} - void play(Ts... x) override { this->audio_dac_->set_mute_on(); } + void play(const Ts &...x) override { this->audio_dac_->set_mute_on(); } protected: AudioDac *audio_dac_; @@ -33,7 +33,7 @@ template class SetVolumeAction : public Action { TEMPLATABLE_VALUE(float, volume) - void play(Ts... x) override { this->audio_dac_->set_volume(this->volume_.value(x...)); } + void play(const Ts &...x) override { this->audio_dac_->set_volume(this->volume_.value(x...)); } protected: AudioDac *audio_dac_; diff --git a/esphome/components/binary_sensor/automation.h b/esphome/components/binary_sensor/automation.h index 0bc7b9acb3..f6971a2fc4 100644 --- a/esphome/components/binary_sensor/automation.h +++ b/esphome/components/binary_sensor/automation.h @@ -141,7 +141,7 @@ class StateChangeTrigger : public Trigger, optional > { template class BinarySensorCondition : public Condition { public: BinarySensorCondition(BinarySensor *parent, bool state) : parent_(parent), state_(state) {} - bool check(Ts... x) override { return this->parent_->state == this->state_; } + bool check(const Ts &...x) override { return this->parent_->state == this->state_; } protected: BinarySensor *parent_; @@ -153,7 +153,7 @@ template class BinarySensorPublishAction : public Action explicit BinarySensorPublishAction(BinarySensor *sensor) : sensor_(sensor) {} TEMPLATABLE_VALUE(bool, state) - void play(Ts... x) override { + void play(const Ts &...x) override { auto val = this->state_.value(x...); this->sensor_->publish_state(val); } @@ -166,7 +166,7 @@ template class BinarySensorInvalidateAction : public Actionsensor_->invalidate_state(); } + void play(const Ts &...x) override { this->sensor_->invalidate_state(); } protected: BinarySensor *sensor_; diff --git a/esphome/components/bl0906/bl0906.h b/esphome/components/bl0906/bl0906.h index 5a9ad0f028..493b645c89 100644 --- a/esphome/components/bl0906/bl0906.h +++ b/esphome/components/bl0906/bl0906.h @@ -89,7 +89,7 @@ class BL0906 : public PollingComponent, public uart::UARTDevice { template class ResetEnergyAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->enqueue_action_(&BL0906::reset_energy_); } + void play(const Ts &...x) override { this->parent_->enqueue_action_(&BL0906::reset_energy_); } }; } // namespace bl0906 diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index 55f1cb2f46..ce534501f3 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -123,9 +123,9 @@ template class BLEClientWriteAction : public Action, publ this->has_simple_value_ = true; } - void play(Ts... x) override {} + void play(const Ts &...x) override {} - void play_complex(Ts... x) override { + void play_complex(const Ts &...x) override { this->num_running_++; this->var_ = std::make_tuple(x...); auto value = this->has_simple_value_ ? this->value_.simple : this->value_.template_func(x...); @@ -229,7 +229,7 @@ template class BLEClientPasskeyReplyAction : public Actionvalue_.simple; @@ -266,7 +266,7 @@ template class BLEClientNumericComparisonReplyAction : public Ac public: BLEClientNumericComparisonReplyAction(BLEClient *ble_client) { parent_ = ble_client; } - void play(Ts... x) override { + void play(const Ts &...x) override { esp_bd_addr_t remote_bda; memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t)); if (has_simple_value_) { @@ -299,7 +299,7 @@ template class BLEClientRemoveBondAction : public Action public: BLEClientRemoveBondAction(BLEClient *ble_client) { parent_ = ble_client; } - void play(Ts... x) override { + void play(const Ts &...x) override { esp_bd_addr_t remote_bda; memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t)); esp_ble_remove_bond_device(remote_bda); @@ -334,9 +334,9 @@ template class BLEClientConnectAction : public Action, pu } // not used since we override play_complex_ - void play(Ts... x) override {} + void play(const Ts &...x) override {} - void play_complex(Ts... x) override { + void play_complex(const Ts &...x) override { // it makes no sense to have multiple instances of this running at the same time. // this would occur only if the same automation was re-triggered while still // running. So just cancel the second chain if this is detected. @@ -379,9 +379,9 @@ template class BLEClientDisconnectAction : public Action, } // not used since we override play_complex_ - void play(Ts... x) override {} + void play(const Ts &...x) override {} - void play_complex(Ts... x) override { + void play_complex(const Ts &...x) override { this->num_running_++; if (this->node_state == espbt::ClientState::IDLE) { this->play_next_(x...); diff --git a/esphome/components/button/automation.h b/esphome/components/button/automation.h index a5fb9f35b7..3b792eb5d7 100644 --- a/esphome/components/button/automation.h +++ b/esphome/components/button/automation.h @@ -11,7 +11,7 @@ template class PressAction : public Action { public: explicit PressAction(Button *button) : button_(button) {} - void play(Ts... x) override { this->button_->press(); } + void play(const Ts &...x) override { this->button_->press(); } protected: Button *button_; diff --git a/esphome/components/canbus/canbus.h b/esphome/components/canbus/canbus.h index 51d7c0830a..029eb278c0 100644 --- a/esphome/components/canbus/canbus.h +++ b/esphome/components/canbus/canbus.h @@ -129,7 +129,7 @@ template class CanbusSendAction : public Action, public P this->remote_transmission_request_ = remote_transmission_request; } - void play(Ts... x) override { + void play(const Ts &...x) override { auto can_id = this->can_id_.has_value() ? *this->can_id_ : this->parent_->can_id_; auto use_extended_id = this->use_extended_id_.has_value() ? *this->use_extended_id_ : this->parent_->use_extended_id_; diff --git a/esphome/components/climate/automation.h b/esphome/components/climate/automation.h index a4d13ade58..36cc8f4f21 100644 --- a/esphome/components/climate/automation.h +++ b/esphome/components/climate/automation.h @@ -22,7 +22,7 @@ template class ControlAction : public Action { TEMPLATABLE_VALUE(std::string, custom_preset) TEMPLATABLE_VALUE(ClimateSwingMode, swing_mode) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->climate_->make_call(); call.set_mode(this->mode_.optional_value(x...)); call.set_target_temperature(this->target_temperature_.optional_value(x...)); diff --git a/esphome/components/cm1106/cm1106.h b/esphome/components/cm1106/cm1106.h index 3b78e17cf4..ad089bbe7d 100644 --- a/esphome/components/cm1106/cm1106.h +++ b/esphome/components/cm1106/cm1106.h @@ -30,7 +30,7 @@ template class CM1106CalibrateZeroAction : public Action public: CM1106CalibrateZeroAction(CM1106Component *cm1106) : cm1106_(cm1106) {} - void play(Ts... x) override { this->cm1106_->calibrate_zero(400); } + void play(const Ts &...x) override { this->cm1106_->calibrate_zero(400); } protected: CM1106Component *cm1106_; diff --git a/esphome/components/cover/automation.h b/esphome/components/cover/automation.h index 6406ba52cb..752e0398c1 100644 --- a/esphome/components/cover/automation.h +++ b/esphome/components/cover/automation.h @@ -11,7 +11,7 @@ template class OpenAction : public Action { public: explicit OpenAction(Cover *cover) : cover_(cover) {} - void play(Ts... x) override { this->cover_->make_call().set_command_open().perform(); } + void play(const Ts &...x) override { this->cover_->make_call().set_command_open().perform(); } protected: Cover *cover_; @@ -21,7 +21,7 @@ template class CloseAction : public Action { public: explicit CloseAction(Cover *cover) : cover_(cover) {} - void play(Ts... x) override { this->cover_->make_call().set_command_close().perform(); } + void play(const Ts &...x) override { this->cover_->make_call().set_command_close().perform(); } protected: Cover *cover_; @@ -31,7 +31,7 @@ template class StopAction : public Action { public: explicit StopAction(Cover *cover) : cover_(cover) {} - void play(Ts... x) override { this->cover_->make_call().set_command_stop().perform(); } + void play(const Ts &...x) override { this->cover_->make_call().set_command_stop().perform(); } protected: Cover *cover_; @@ -41,7 +41,7 @@ template class ToggleAction : public Action { public: explicit ToggleAction(Cover *cover) : cover_(cover) {} - void play(Ts... x) override { this->cover_->make_call().set_command_toggle().perform(); } + void play(const Ts &...x) override { this->cover_->make_call().set_command_toggle().perform(); } protected: Cover *cover_; @@ -55,7 +55,7 @@ template class ControlAction : public Action { TEMPLATABLE_VALUE(float, position) TEMPLATABLE_VALUE(float, tilt) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->cover_->make_call(); if (this->stop_.has_value()) call.set_stop(this->stop_.value(x...)); @@ -77,7 +77,7 @@ template class CoverPublishAction : public Action { TEMPLATABLE_VALUE(float, tilt) TEMPLATABLE_VALUE(CoverOperation, current_operation) - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->position_.has_value()) this->cover_->position = this->position_.value(x...); if (this->tilt_.has_value()) @@ -94,7 +94,7 @@ template class CoverPublishAction : public Action { template class CoverIsOpenCondition : public Condition { public: CoverIsOpenCondition(Cover *cover) : cover_(cover) {} - bool check(Ts... x) override { return this->cover_->is_fully_open(); } + bool check(const Ts &...x) override { return this->cover_->is_fully_open(); } protected: Cover *cover_; @@ -103,7 +103,7 @@ template class CoverIsOpenCondition : public Condition { template class CoverIsClosedCondition : public Condition { public: CoverIsClosedCondition(Cover *cover) : cover_(cover) {} - bool check(Ts... x) override { return this->cover_->is_fully_closed(); } + bool check(const Ts &...x) override { return this->cover_->is_fully_closed(); } protected: Cover *cover_; diff --git a/esphome/components/cs5460a/cs5460a.h b/esphome/components/cs5460a/cs5460a.h index 15ae04f3c6..11b13f5851 100644 --- a/esphome/components/cs5460a/cs5460a.h +++ b/esphome/components/cs5460a/cs5460a.h @@ -114,7 +114,7 @@ template class CS5460ARestartAction : public Action { public: CS5460ARestartAction(CS5460AComponent *cs5460a) : cs5460a_(cs5460a) {} - void play(Ts... x) override { cs5460a_->restart(); } + void play(const Ts &...x) override { cs5460a_->restart(); } protected: CS5460AComponent *cs5460a_; diff --git a/esphome/components/datetime/date_entity.h b/esphome/components/datetime/date_entity.h index fcbb46cf17..ba2edb127a 100644 --- a/esphome/components/datetime/date_entity.h +++ b/esphome/components/datetime/date_entity.h @@ -101,7 +101,7 @@ template class DateSetAction : public Action, public Pare public: TEMPLATABLE_VALUE(ESPTime, date) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->parent_->make_call(); if (this->date_.has_value()) { diff --git a/esphome/components/datetime/datetime_entity.h b/esphome/components/datetime/datetime_entity.h index 275eedfd3b..43bff5a181 100644 --- a/esphome/components/datetime/datetime_entity.h +++ b/esphome/components/datetime/datetime_entity.h @@ -124,7 +124,7 @@ template class DateTimeSetAction : public Action, public public: TEMPLATABLE_VALUE(ESPTime, datetime) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->parent_->make_call(); if (this->datetime_.has_value()) { diff --git a/esphome/components/datetime/time_entity.h b/esphome/components/datetime/time_entity.h index e79b8c225d..c5cbeb52da 100644 --- a/esphome/components/datetime/time_entity.h +++ b/esphome/components/datetime/time_entity.h @@ -103,7 +103,7 @@ template class TimeSetAction : public Action, public Pare public: TEMPLATABLE_VALUE(ESPTime, time) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->parent_->make_call(); if (this->time_.has_value()) { diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index 38744163c7..80381e767c 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -148,7 +148,7 @@ template class EnterDeepSleepAction : public Action { void set_time(time::RealTimeClock *time) { this->time_ = time; } #endif - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->sleep_duration_.has_value()) { this->deep_sleep_->set_sleep_duration(this->sleep_duration_.value(x...)); } @@ -207,12 +207,12 @@ template class EnterDeepSleepAction : public Action { template class PreventDeepSleepAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->prevent_deep_sleep(); } + void play(const Ts &...x) override { this->parent_->prevent_deep_sleep(); } }; template class AllowDeepSleepAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->allow_deep_sleep(); } + void play(const Ts &...x) override { this->parent_->allow_deep_sleep(); } }; } // namespace deep_sleep diff --git a/esphome/components/dfplayer/dfplayer.h b/esphome/components/dfplayer/dfplayer.h index d2ec0a2310..03d2230ca6 100644 --- a/esphome/components/dfplayer/dfplayer.h +++ b/esphome/components/dfplayer/dfplayer.h @@ -77,7 +77,7 @@ class DFPlayer : public uart::UARTDevice, public Component { class ACTION_CLASS : /* NOLINT */ \ public Action, \ public Parented { \ - void play(Ts... x) override { this->parent_->ACTION_METHOD(); } \ + void play(const Ts &...x) override { this->parent_->ACTION_METHOD(); } \ }; DFPLAYER_SIMPLE_ACTION(NextAction, next) @@ -87,7 +87,7 @@ template class PlayMp3Action : public Action, public Pare public: TEMPLATABLE_VALUE(uint16_t, file) - void play(Ts... x) override { + void play(const Ts &...x) override { auto file = this->file_.value(x...); this->parent_->play_mp3(file); } @@ -98,7 +98,7 @@ template class PlayFileAction : public Action, public Par TEMPLATABLE_VALUE(uint16_t, file) TEMPLATABLE_VALUE(bool, loop) - void play(Ts... x) override { + void play(const Ts &...x) override { auto file = this->file_.value(x...); auto loop = this->loop_.value(x...); if (loop) { @@ -115,7 +115,7 @@ template class PlayFolderAction : public Action, public P TEMPLATABLE_VALUE(uint16_t, file) TEMPLATABLE_VALUE(bool, loop) - void play(Ts... x) override { + void play(const Ts &...x) override { auto folder = this->folder_.value(x...); auto file = this->file_.value(x...); auto loop = this->loop_.value(x...); @@ -131,7 +131,7 @@ template class SetDeviceAction : public Action, public Pa public: TEMPLATABLE_VALUE(Device, device) - void play(Ts... x) override { + void play(const Ts &...x) override { auto device = this->device_.value(x...); this->parent_->set_device(device); } @@ -141,7 +141,7 @@ template class SetVolumeAction : public Action, public Pa public: TEMPLATABLE_VALUE(uint8_t, volume) - void play(Ts... x) override { + void play(const Ts &...x) override { auto volume = this->volume_.value(x...); this->parent_->set_volume(volume); } @@ -151,7 +151,7 @@ template class SetEqAction : public Action, public Parent public: TEMPLATABLE_VALUE(EqPreset, eq) - void play(Ts... x) override { + void play(const Ts &...x) override { auto eq = this->eq_.value(x...); this->parent_->set_eq(eq); } @@ -168,7 +168,7 @@ DFPLAYER_SIMPLE_ACTION(VolumeDownAction, volume_down) template class DFPlayerIsPlayingCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_playing(); } + bool check(const Ts &...x) override { return this->parent_->is_playing(); } }; class DFPlayerFinishedPlaybackTrigger : public Trigger<> { diff --git a/esphome/components/dfrobot_sen0395/automation.h b/esphome/components/dfrobot_sen0395/automation.h index 3f69e482b7..422555d6eb 100644 --- a/esphome/components/dfrobot_sen0395/automation.h +++ b/esphome/components/dfrobot_sen0395/automation.h @@ -11,7 +11,7 @@ namespace dfrobot_sen0395 { template class DfrobotSen0395ResetAction : public Action, public Parented { public: - void play(Ts... x) { this->parent_->enqueue(make_unique()); } + void play(const Ts &...x) { this->parent_->enqueue(make_unique()); } }; template @@ -33,7 +33,7 @@ class DfrobotSen0395SettingsAction : public Action, public Parentedparent_->enqueue(make_unique(0)); if (this->factory_reset_.has_value() && this->factory_reset_.value(x...) == true) { this->parent_->enqueue(make_unique()); diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index 97b0a4e8d7..47d40915aa 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -819,7 +819,7 @@ template class DisplayPageShowAction : public Action { public: TEMPLATABLE_VALUE(DisplayPage *, page) - void play(Ts... x) override { + void play(const Ts &...x) override { auto *page = this->page_.value(x...); if (page != nullptr) { page->show(); @@ -831,7 +831,7 @@ template class DisplayPageShowNextAction : public Action public: DisplayPageShowNextAction(Display *buffer) : buffer_(buffer) {} - void play(Ts... x) override { this->buffer_->show_next_page(); } + void play(const Ts &...x) override { this->buffer_->show_next_page(); } Display *buffer_; }; @@ -840,7 +840,7 @@ template class DisplayPageShowPrevAction : public Action public: DisplayPageShowPrevAction(Display *buffer) : buffer_(buffer) {} - void play(Ts... x) override { this->buffer_->show_prev_page(); } + void play(const Ts &...x) override { this->buffer_->show_prev_page(); } Display *buffer_; }; @@ -850,7 +850,7 @@ template class DisplayIsDisplayingPageCondition : public Conditi DisplayIsDisplayingPageCondition(Display *parent) : parent_(parent) {} void set_page(DisplayPage *page) { this->page_ = page; } - bool check(Ts... x) override { return this->parent_->get_active_page() == this->page_; } + bool check(const Ts &...x) override { return this->parent_->get_active_page() == this->page_; } protected: Display *parent_; diff --git a/esphome/components/display_menu_base/automation.h b/esphome/components/display_menu_base/automation.h index d5394a1e0c..9c64794cef 100644 --- a/esphome/components/display_menu_base/automation.h +++ b/esphome/components/display_menu_base/automation.h @@ -10,7 +10,7 @@ template class UpAction : public Action { public: explicit UpAction(DisplayMenuComponent *menu) : menu_(menu) {} - void play(Ts... x) override { this->menu_->up(); } + void play(const Ts &...x) override { this->menu_->up(); } protected: DisplayMenuComponent *menu_; @@ -20,7 +20,7 @@ template class DownAction : public Action { public: explicit DownAction(DisplayMenuComponent *menu) : menu_(menu) {} - void play(Ts... x) override { this->menu_->down(); } + void play(const Ts &...x) override { this->menu_->down(); } protected: DisplayMenuComponent *menu_; @@ -30,7 +30,7 @@ template class LeftAction : public Action { public: explicit LeftAction(DisplayMenuComponent *menu) : menu_(menu) {} - void play(Ts... x) override { this->menu_->left(); } + void play(const Ts &...x) override { this->menu_->left(); } protected: DisplayMenuComponent *menu_; @@ -40,7 +40,7 @@ template class RightAction : public Action { public: explicit RightAction(DisplayMenuComponent *menu) : menu_(menu) {} - void play(Ts... x) override { this->menu_->right(); } + void play(const Ts &...x) override { this->menu_->right(); } protected: DisplayMenuComponent *menu_; @@ -50,7 +50,7 @@ template class EnterAction : public Action { public: explicit EnterAction(DisplayMenuComponent *menu) : menu_(menu) {} - void play(Ts... x) override { this->menu_->enter(); } + void play(const Ts &...x) override { this->menu_->enter(); } protected: DisplayMenuComponent *menu_; @@ -60,7 +60,7 @@ template class ShowAction : public Action { public: explicit ShowAction(DisplayMenuComponent *menu) : menu_(menu) {} - void play(Ts... x) override { this->menu_->show(); } + void play(const Ts &...x) override { this->menu_->show(); } protected: DisplayMenuComponent *menu_; @@ -70,7 +70,7 @@ template class HideAction : public Action { public: explicit HideAction(DisplayMenuComponent *menu) : menu_(menu) {} - void play(Ts... x) override { this->menu_->hide(); } + void play(const Ts &...x) override { this->menu_->hide(); } protected: DisplayMenuComponent *menu_; @@ -80,7 +80,7 @@ template class ShowMainAction : public Action { public: explicit ShowMainAction(DisplayMenuComponent *menu) : menu_(menu) {} - void play(Ts... x) override { this->menu_->show_main(); } + void play(const Ts &...x) override { this->menu_->show_main(); } protected: DisplayMenuComponent *menu_; @@ -88,7 +88,7 @@ template class ShowMainAction : public Action { template class IsActiveCondition : public Condition { public: explicit IsActiveCondition(DisplayMenuComponent *menu) : menu_(menu) {} - bool check(Ts... x) override { return this->menu_->is_active(); } + bool check(const Ts &...x) override { return this->menu_->is_active(); } protected: DisplayMenuComponent *menu_; diff --git a/esphome/components/ds1307/ds1307.h b/esphome/components/ds1307/ds1307.h index 2e9ac2275c..f7f06253b7 100644 --- a/esphome/components/ds1307/ds1307.h +++ b/esphome/components/ds1307/ds1307.h @@ -59,12 +59,12 @@ class DS1307Component : public time::RealTimeClock, public i2c::I2CDevice { template class WriteAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->write_time(); } + void play(const Ts &...x) override { this->parent_->write_time(); } }; template class ReadAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->read_time(); } + void play(const Ts &...x) override { this->parent_->read_time(); } }; } // namespace ds1307 } // namespace esphome diff --git a/esphome/components/duty_time/duty_time_sensor.h b/esphome/components/duty_time/duty_time_sensor.h index 18280f8e21..d9fb2a6d60 100644 --- a/esphome/components/duty_time/duty_time_sensor.h +++ b/esphome/components/duty_time/duty_time_sensor.h @@ -51,15 +51,15 @@ class DutyTimeSensor : public sensor::Sensor, public PollingComponent { template class BaseAction : public Action, public Parented {}; template class StartAction : public BaseAction { - void play(Ts... x) override { this->parent_->start(); } + void play(const Ts &...x) override { this->parent_->start(); } }; template class StopAction : public BaseAction { - void play(Ts... x) override { this->parent_->stop(); } + void play(const Ts &...x) override { this->parent_->stop(); } }; template class ResetAction : public BaseAction { - void play(Ts... x) override { this->parent_->reset(); } + void play(const Ts &...x) override { this->parent_->reset(); } }; template class RunningCondition : public Condition, public Parented { @@ -67,7 +67,7 @@ template class RunningCondition : public Condition, publi explicit RunningCondition(DutyTimeSensor *parent, bool state) : Parented(parent), state_(state) {} protected: - bool check(Ts... x) override { return this->parent_->is_running() == this->state_; } + bool check(const Ts &...x) override { return this->parent_->is_running() == this->state_; } bool state_; }; diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index c3e1ec2ce6..2fb60bb822 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -214,17 +214,17 @@ extern ESP32BLE *global_ble; template class BLEEnabledCondition : public Condition { public: - bool check(Ts... x) override { return global_ble->is_active(); } + bool check(const Ts &...x) override { return global_ble->is_active(); } }; template class BLEEnableAction : public Action { public: - void play(Ts... x) override { global_ble->enable(); } + void play(const Ts &...x) override { global_ble->enable(); } }; template class BLEDisableAction : public Action { public: - void play(Ts... x) override { global_ble->disable(); } + void play(const Ts &...x) override { global_ble->disable(); } }; } // namespace esphome::esp32_ble diff --git a/esphome/components/esp32_ble_server/ble_server_automations.h b/esphome/components/esp32_ble_server/ble_server_automations.h index 543b1153fc..fe18600280 100644 --- a/esphome/components/esp32_ble_server/ble_server_automations.h +++ b/esphome/components/esp32_ble_server/ble_server_automations.h @@ -71,7 +71,7 @@ template class BLECharacteristicSetValueAction : public Action, buffer) void set_buffer(ByteBuffer buffer) { this->set_buffer(buffer.get_data()); } - void play(Ts... x) override { + void play(const Ts &...x) override { // If the listener is already set, do nothing if (BLECharacteristicSetValueActionManager::get_instance()->has_listener(this->parent_)) return; @@ -96,7 +96,7 @@ template class BLECharacteristicSetValueAction : public Action class BLECharacteristicNotifyAction : public Action { public: BLECharacteristicNotifyAction(BLECharacteristic *characteristic) : parent_(characteristic) {} - void play(Ts... x) override { + void play(const Ts &...x) override { #ifdef USE_ESP32_BLE_SERVER_SET_VALUE_ACTION // Call the pre-notify event BLECharacteristicSetValueActionManager::get_instance()->emit_pre_notify(this->parent_); @@ -116,7 +116,7 @@ template class BLEDescriptorSetValueAction : public Action, buffer) void set_buffer(ByteBuffer buffer) { this->set_buffer(buffer.get_data()); } - void play(Ts... x) override { this->parent_->set_value(this->buffer_.value(x...)); } + void play(const Ts &...x) override { this->parent_->set_value(this->buffer_.value(x...)); } protected: BLEDescriptor *parent_; diff --git a/esphome/components/esp32_ble_tracker/automation.h b/esphome/components/esp32_ble_tracker/automation.h index 784f2eaaa2..054cbaa7df 100644 --- a/esphome/components/esp32_ble_tracker/automation.h +++ b/esphome/components/esp32_ble_tracker/automation.h @@ -96,7 +96,7 @@ template class ESP32BLEStartScanAction : public Action { public: ESP32BLEStartScanAction(ESP32BLETracker *parent) : parent_(parent) {} TEMPLATABLE_VALUE(bool, continuous) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->set_scan_continuous(this->continuous_.value(x...)); this->parent_->start_scan(); } @@ -107,7 +107,7 @@ template class ESP32BLEStartScanAction : public Action { template class ESP32BLEStopScanAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->stop_scan(); } + void play(const Ts &...x) override { this->parent_->stop_scan(); } }; } // namespace esphome::esp32_ble_tracker diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.h b/esphome/components/esp8266_pwm/esp8266_pwm.h index 79530aacd4..4b021fc462 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.h +++ b/esphome/components/esp8266_pwm/esp8266_pwm.h @@ -40,7 +40,7 @@ template class SetFrequencyAction : public Action { SetFrequencyAction(ESP8266PWM *parent) : parent_(parent) {} TEMPLATABLE_VALUE(float, frequency); - void play(Ts... x) { + void play(const Ts &...x) { float freq = this->frequency_.value(x...); this->parent_->update_frequency(freq); } diff --git a/esphome/components/esp_ldo/esp_ldo.h b/esphome/components/esp_ldo/esp_ldo.h index bafa32db6b..9edd303e16 100644 --- a/esphome/components/esp_ldo/esp_ldo.h +++ b/esphome/components/esp_ldo/esp_ldo.h @@ -34,7 +34,7 @@ template class AdjustAction : public Action { TEMPLATABLE_VALUE(float, voltage) - void play(Ts... x) override { this->ldo_->adjust_voltage(this->voltage_.value(x...)); } + void play(const Ts &...x) override { this->ldo_->adjust_voltage(this->voltage_.value(x...)); } protected: EspLdo *ldo_; diff --git a/esphome/components/espnow/automation.h b/esphome/components/espnow/automation.h index 5415b088fd..0b26681400 100644 --- a/esphome/components/espnow/automation.h +++ b/esphome/components/espnow/automation.h @@ -36,7 +36,7 @@ template class SendAction : public Action, public Parente void set_wait_for_sent(bool wait_for_sent) { this->flags_.wait_for_sent = wait_for_sent; } void set_continue_on_error(bool continue_on_error) { this->flags_.continue_on_error = continue_on_error; } - void play_complex(Ts... x) override { + void play_complex(const Ts &...x) override { this->num_running_++; send_callback_t send_callback = [this, x...](esp_err_t status) { if (status == ESP_OK) { @@ -67,7 +67,7 @@ template class SendAction : public Action, public Parente } } - void play(Ts... x) override { /* ignore - see play_complex */ + void play(const Ts &...x) override { /* ignore - see play_complex */ } void stop() override { @@ -90,7 +90,7 @@ template class AddPeerAction : public Action, public Pare TEMPLATABLE_VALUE(peer_address_t, address); public: - void play(Ts... x) override { + void play(const Ts &...x) override { peer_address_t address = this->address_.value(x...); this->parent_->add_peer(address.data()); } @@ -100,7 +100,7 @@ template class DeletePeerAction : public Action, public P TEMPLATABLE_VALUE(peer_address_t, address); public: - void play(Ts... x) override { + void play(const Ts &...x) override { peer_address_t address = this->address_.value(x...); this->parent_->del_peer(address.data()); } @@ -109,7 +109,7 @@ template class DeletePeerAction : public Action, public P template class SetChannelAction : public Action, public Parented { public: TEMPLATABLE_VALUE(uint8_t, channel) - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->parent_->is_wifi_enabled()) { return; } diff --git a/esphome/components/event/automation.h b/esphome/components/event/automation.h index 9ebcb654a0..5bdba18687 100644 --- a/esphome/components/event/automation.h +++ b/esphome/components/event/automation.h @@ -11,7 +11,7 @@ template class TriggerEventAction : public Action, public public: TEMPLATABLE_VALUE(std::string, event_type) - void play(Ts... x) override { this->parent_->trigger(this->event_type_.value(x...)); } + void play(const Ts &...x) override { this->parent_->trigger(this->event_type_.value(x...)); } }; class EventTrigger : public Trigger { diff --git a/esphome/components/ezo/automation.h b/esphome/components/ezo/automation.h index 19427b9159..a4a6fa3014 100644 --- a/esphome/components/ezo/automation.h +++ b/esphome/components/ezo/automation.h @@ -17,35 +17,35 @@ class LedTrigger : public Trigger { class CustomTrigger : public Trigger { public: explicit CustomTrigger(EZOSensor *ezo) { - ezo->add_custom_callback([this](std::string value) { this->trigger(std::move(value)); }); + ezo->add_custom_callback([this](const std::string &value) { this->trigger(value); }); } }; class TTrigger : public Trigger { public: explicit TTrigger(EZOSensor *ezo) { - ezo->add_t_callback([this](std::string value) { this->trigger(std::move(value)); }); + ezo->add_t_callback([this](const std::string &value) { this->trigger(value); }); } }; class CalibrationTrigger : public Trigger { public: explicit CalibrationTrigger(EZOSensor *ezo) { - ezo->add_calibration_callback([this](std::string value) { this->trigger(std::move(value)); }); + ezo->add_calibration_callback([this](const std::string &value) { this->trigger(value); }); } }; class SlopeTrigger : public Trigger { public: explicit SlopeTrigger(EZOSensor *ezo) { - ezo->add_slope_callback([this](std::string value) { this->trigger(std::move(value)); }); + ezo->add_slope_callback([this](const std::string &value) { this->trigger(value); }); } }; class DeviceInformationTrigger : public Trigger { public: explicit DeviceInformationTrigger(EZOSensor *ezo) { - ezo->add_device_infomation_callback([this](std::string value) { this->trigger(std::move(value)); }); + ezo->add_device_infomation_callback([this](const std::string &value) { this->trigger(value); }); } }; diff --git a/esphome/components/ezo_pmp/ezo_pmp.h b/esphome/components/ezo_pmp/ezo_pmp.h index 671e124810..d4917e7f4b 100644 --- a/esphome/components/ezo_pmp/ezo_pmp.h +++ b/esphome/components/ezo_pmp/ezo_pmp.h @@ -119,7 +119,7 @@ template class EzoPMPFindAction : public Action { public: EzoPMPFindAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {} - void play(Ts... x) override { this->ezopmp_->find(); } + void play(const Ts &...x) override { this->ezopmp_->find(); } protected: EzoPMP *ezopmp_; @@ -129,7 +129,7 @@ template class EzoPMPDoseContinuouslyAction : public Actionezopmp_->dose_continuously(); } + void play(const Ts &...x) override { this->ezopmp_->dose_continuously(); } protected: EzoPMP *ezopmp_; @@ -139,7 +139,7 @@ template class EzoPMPDoseVolumeAction : public Action { public: EzoPMPDoseVolumeAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {} - void play(Ts... x) override { this->ezopmp_->dose_volume(this->volume_.value(x...)); } + void play(const Ts &...x) override { this->ezopmp_->dose_volume(this->volume_.value(x...)); } TEMPLATABLE_VALUE(double, volume) protected: @@ -150,7 +150,7 @@ template class EzoPMPDoseVolumeOverTimeAction : public Actionezopmp_->dose_volume_over_time(this->volume_.value(x...), this->duration_.value(x...)); } TEMPLATABLE_VALUE(double, volume) @@ -164,7 +164,7 @@ template class EzoPMPDoseWithConstantFlowRateAction : public Act public: EzoPMPDoseWithConstantFlowRateAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {} - void play(Ts... x) override { + void play(const Ts &...x) override { this->ezopmp_->dose_with_constant_flow_rate(this->volume_.value(x...), this->duration_.value(x...)); } TEMPLATABLE_VALUE(double, volume) @@ -178,7 +178,7 @@ template class EzoPMPSetCalibrationVolumeAction : public Action< public: EzoPMPSetCalibrationVolumeAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {} - void play(Ts... x) override { this->ezopmp_->set_calibration_volume(this->volume_.value(x...)); } + void play(const Ts &...x) override { this->ezopmp_->set_calibration_volume(this->volume_.value(x...)); } TEMPLATABLE_VALUE(double, volume) protected: @@ -189,7 +189,7 @@ template class EzoPMPClearTotalVolumeDispensedAction : public Ac public: EzoPMPClearTotalVolumeDispensedAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {} - void play(Ts... x) override { this->ezopmp_->clear_total_volume_dosed(); } + void play(const Ts &...x) override { this->ezopmp_->clear_total_volume_dosed(); } protected: EzoPMP *ezopmp_; @@ -199,7 +199,7 @@ template class EzoPMPClearCalibrationAction : public Actionezopmp_->clear_calibration(); } + void play(const Ts &...x) override { this->ezopmp_->clear_calibration(); } protected: EzoPMP *ezopmp_; @@ -209,7 +209,7 @@ template class EzoPMPPauseDosingAction : public Action { public: EzoPMPPauseDosingAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {} - void play(Ts... x) override { this->ezopmp_->pause_dosing(); } + void play(const Ts &...x) override { this->ezopmp_->pause_dosing(); } protected: EzoPMP *ezopmp_; @@ -219,7 +219,7 @@ template class EzoPMPStopDosingAction : public Action { public: EzoPMPStopDosingAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {} - void play(Ts... x) override { this->ezopmp_->stop_dosing(); } + void play(const Ts &...x) override { this->ezopmp_->stop_dosing(); } protected: EzoPMP *ezopmp_; @@ -229,7 +229,7 @@ template class EzoPMPChangeI2CAddressAction : public Actionezopmp_->change_i2c_address(this->address_.value(x...)); } + void play(const Ts &...x) override { this->ezopmp_->change_i2c_address(this->address_.value(x...)); } TEMPLATABLE_VALUE(int, address) protected: @@ -240,7 +240,7 @@ template class EzoPMPArbitraryCommandAction : public Actionezopmp_->exec_arbitrary_command(this->command_.value(x...)); } + void play(const Ts &...x) override { this->ezopmp_->exec_arbitrary_command(this->command_.value(x...)); } TEMPLATABLE_VALUE(std::string, command) protected: diff --git a/esphome/components/fan/automation.h b/esphome/components/fan/automation.h index ae0af1a9bd..ce1db6fc64 100644 --- a/esphome/components/fan/automation.h +++ b/esphome/components/fan/automation.h @@ -15,7 +15,7 @@ template class TurnOnAction : public Action { TEMPLATABLE_VALUE(int, speed) TEMPLATABLE_VALUE(FanDirection, direction) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->state_->turn_on(); if (this->oscillating_.has_value()) { call.set_oscillating(this->oscillating_.value(x...)); @@ -36,7 +36,7 @@ template class TurnOffAction : public Action { public: explicit TurnOffAction(Fan *state) : state_(state) {} - void play(Ts... x) override { this->state_->turn_off().perform(); } + void play(const Ts &...x) override { this->state_->turn_off().perform(); } Fan *state_; }; @@ -45,7 +45,7 @@ template class ToggleAction : public Action { public: explicit ToggleAction(Fan *state) : state_(state) {} - void play(Ts... x) override { this->state_->toggle().perform(); } + void play(const Ts &...x) override { this->state_->toggle().perform(); } Fan *state_; }; @@ -56,7 +56,7 @@ template class CycleSpeedAction : public Action { TEMPLATABLE_VALUE(bool, no_off_cycle) - void play(Ts... x) override { + void play(const Ts &...x) override { // check to see if fan supports speeds and is on if (this->state_->get_traits().supported_speed_count()) { if (this->state_->state) { @@ -97,7 +97,7 @@ template class CycleSpeedAction : public Action { template class FanIsOnCondition : public Condition { public: explicit FanIsOnCondition(Fan *state) : state_(state) {} - bool check(Ts... x) override { return this->state_->state; } + bool check(const Ts &...x) override { return this->state_->state; } protected: Fan *state_; @@ -105,7 +105,7 @@ template class FanIsOnCondition : public Condition { template class FanIsOffCondition : public Condition { public: explicit FanIsOffCondition(Fan *state) : state_(state) {} - bool check(Ts... x) override { return !this->state_->state; } + bool check(const Ts &...x) override { return !this->state_->state; } protected: Fan *state_; diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.h b/esphome/components/fingerprint_grow/fingerprint_grow.h index 590c709c22..370b26f56a 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.h +++ b/esphome/components/fingerprint_grow/fingerprint_grow.h @@ -273,7 +273,7 @@ template class EnrollmentAction : public Action, public P TEMPLATABLE_VALUE(uint16_t, finger_id) TEMPLATABLE_VALUE(uint8_t, num_scans) - void play(Ts... x) override { + void play(const Ts &...x) override { auto finger_id = this->finger_id_.value(x...); auto num_scans = this->num_scans_.value(x...); if (num_scans) { @@ -287,14 +287,14 @@ template class EnrollmentAction : public Action, public P template class CancelEnrollmentAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->finish_enrollment(1); } + void play(const Ts &...x) override { this->parent_->finish_enrollment(1); } }; template class DeleteAction : public Action, public Parented { public: TEMPLATABLE_VALUE(uint16_t, finger_id) - void play(Ts... x) override { + void play(const Ts &...x) override { auto finger_id = this->finger_id_.value(x...); this->parent_->delete_fingerprint(finger_id); } @@ -302,14 +302,14 @@ template class DeleteAction : public Action, public Paren template class DeleteAllAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->delete_all_fingerprints(); } + void play(const Ts &...x) override { this->parent_->delete_all_fingerprints(); } }; template class LEDControlAction : public Action, public Parented { public: TEMPLATABLE_VALUE(bool, state) - void play(Ts... x) override { + void play(const Ts &...x) override { auto state = this->state_.value(x...); this->parent_->led_control(state); } @@ -322,7 +322,7 @@ template class AuraLEDControlAction : public Action, publ TEMPLATABLE_VALUE(uint8_t, color) TEMPLATABLE_VALUE(uint8_t, count) - void play(Ts... x) override { + void play(const Ts &...x) override { auto state = this->state_.value(x...); auto speed = this->speed_.value(x...); auto color = this->color_.value(x...); diff --git a/esphome/components/globals/globals_component.h b/esphome/components/globals/globals_component.h index 4c6a12aa72..1d2a08937e 100644 --- a/esphome/components/globals/globals_component.h +++ b/esphome/components/globals/globals_component.h @@ -134,7 +134,7 @@ template class GlobalVarSetAction : public Actionparent_->value() = this->value_.value(x...); } + void play(const Ts &...x) override { this->parent_->value() = this->value_.value(x...); } protected: C *parent_; diff --git a/esphome/components/grove_tb6612fng/grove_tb6612fng.h b/esphome/components/grove_tb6612fng/grove_tb6612fng.h index 68281117e7..a36cb85cff 100644 --- a/esphome/components/grove_tb6612fng/grove_tb6612fng.h +++ b/esphome/components/grove_tb6612fng/grove_tb6612fng.h @@ -168,7 +168,7 @@ class GROVETB6612FNGMotorRunAction : public Action, public Parentedchannel_.value(x...); auto speed = this->speed_.value(x...); this->parent_->dc_motor_run(channel, speed); @@ -180,7 +180,7 @@ class GROVETB6612FNGMotorBrakeAction : public Action, public Parentedparent_->dc_motor_brake(this->channel_.value(x...)); } + void play(const Ts &...x) override { this->parent_->dc_motor_brake(this->channel_.value(x...)); } }; template @@ -188,19 +188,19 @@ class GROVETB6612FNGMotorStopAction : public Action, public Parentedparent_->dc_motor_stop(this->channel_.value(x...)); } + void play(const Ts &...x) override { this->parent_->dc_motor_stop(this->channel_.value(x...)); } }; template class GROVETB6612FNGMotorStandbyAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->standby(); } + void play(const Ts &...x) override { this->parent_->standby(); } }; template class GROVETB6612FNGMotorNoStandbyAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->not_standby(); } + void play(const Ts &...x) override { this->parent_->not_standby(); } }; template @@ -208,7 +208,7 @@ class GROVETB6612FNGMotorChangeAddressAction : public Action, public Pare public: TEMPLATABLE_VALUE(uint8_t, address) - void play(Ts... x) override { this->parent_->set_i2c_addr(this->address_.value(x...)); } + void play(const Ts &...x) override { this->parent_->set_i2c_addr(this->address_.value(x...)); } }; } // namespace grove_tb6612fng diff --git a/esphome/components/haier/automation.h b/esphome/components/haier/automation.h index 55df7ecc1d..c1ce7c01ea 100644 --- a/esphome/components/haier/automation.h +++ b/esphome/components/haier/automation.h @@ -10,7 +10,7 @@ namespace haier { template class DisplayOnAction : public Action { public: DisplayOnAction(HaierClimateBase *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->set_display_state(true); } + void play(const Ts &...x) { this->parent_->set_display_state(true); } protected: HaierClimateBase *parent_; @@ -19,7 +19,7 @@ template class DisplayOnAction : public Action { template class DisplayOffAction : public Action { public: DisplayOffAction(HaierClimateBase *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->set_display_state(false); } + void play(const Ts &...x) { this->parent_->set_display_state(false); } protected: HaierClimateBase *parent_; @@ -28,7 +28,7 @@ template class DisplayOffAction : public Action { template class BeeperOnAction : public Action { public: BeeperOnAction(HonClimate *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->set_beeper_state(true); } + void play(const Ts &...x) { this->parent_->set_beeper_state(true); } protected: HonClimate *parent_; @@ -37,7 +37,7 @@ template class BeeperOnAction : public Action { template class BeeperOffAction : public Action { public: BeeperOffAction(HonClimate *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->set_beeper_state(false); } + void play(const Ts &...x) { this->parent_->set_beeper_state(false); } protected: HonClimate *parent_; @@ -47,7 +47,7 @@ template class VerticalAirflowAction : public Action { public: VerticalAirflowAction(HonClimate *parent) : parent_(parent) {} TEMPLATABLE_VALUE(hon_protocol::VerticalSwingMode, direction) - void play(Ts... x) { this->parent_->set_vertical_airflow(this->direction_.value(x...)); } + void play(const Ts &...x) { this->parent_->set_vertical_airflow(this->direction_.value(x...)); } protected: HonClimate *parent_; @@ -57,7 +57,7 @@ template class HorizontalAirflowAction : public Action { public: HorizontalAirflowAction(HonClimate *parent) : parent_(parent) {} TEMPLATABLE_VALUE(hon_protocol::HorizontalSwingMode, direction) - void play(Ts... x) { this->parent_->set_horizontal_airflow(this->direction_.value(x...)); } + void play(const Ts &...x) { this->parent_->set_horizontal_airflow(this->direction_.value(x...)); } protected: HonClimate *parent_; @@ -66,7 +66,7 @@ template class HorizontalAirflowAction : public Action { template class HealthOnAction : public Action { public: HealthOnAction(HaierClimateBase *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->set_health_mode(true); } + void play(const Ts &...x) { this->parent_->set_health_mode(true); } protected: HaierClimateBase *parent_; @@ -75,7 +75,7 @@ template class HealthOnAction : public Action { template class HealthOffAction : public Action { public: HealthOffAction(HaierClimateBase *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->set_health_mode(false); } + void play(const Ts &...x) { this->parent_->set_health_mode(false); } protected: HaierClimateBase *parent_; @@ -84,7 +84,7 @@ template class HealthOffAction : public Action { template class StartSelfCleaningAction : public Action { public: StartSelfCleaningAction(HonClimate *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->start_self_cleaning(); } + void play(const Ts &...x) { this->parent_->start_self_cleaning(); } protected: HonClimate *parent_; @@ -93,7 +93,7 @@ template class StartSelfCleaningAction : public Action { template class StartSteriCleaningAction : public Action { public: StartSteriCleaningAction(HonClimate *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->start_steri_cleaning(); } + void play(const Ts &...x) { this->parent_->start_steri_cleaning(); } protected: HonClimate *parent_; @@ -102,7 +102,7 @@ template class StartSteriCleaningAction : public Action { template class PowerOnAction : public Action { public: PowerOnAction(HaierClimateBase *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->send_power_on_command(); } + void play(const Ts &...x) { this->parent_->send_power_on_command(); } protected: HaierClimateBase *parent_; @@ -111,7 +111,7 @@ template class PowerOnAction : public Action { template class PowerOffAction : public Action { public: PowerOffAction(HaierClimateBase *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->send_power_off_command(); } + void play(const Ts &...x) { this->parent_->send_power_off_command(); } protected: HaierClimateBase *parent_; @@ -120,7 +120,7 @@ template class PowerOffAction : public Action { template class PowerToggleAction : public Action { public: PowerToggleAction(HaierClimateBase *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->toggle_power(); } + void play(const Ts &...x) { this->parent_->toggle_power(); } protected: HaierClimateBase *parent_; diff --git a/esphome/components/hbridge/fan/hbridge_fan.h b/esphome/components/hbridge/fan/hbridge_fan.h index 143c7c1853..ec1e8ada0e 100644 --- a/esphome/components/hbridge/fan/hbridge_fan.h +++ b/esphome/components/hbridge/fan/hbridge_fan.h @@ -49,7 +49,7 @@ template class BrakeAction : public Action { public: explicit BrakeAction(HBridgeFan *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->brake(); } + void play(const Ts &...x) override { this->parent_->brake(); } HBridgeFan *parent_; }; diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 482cd2da44..8a82a44d7d 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -113,8 +113,8 @@ class HttpContainer : public Parented { class HttpRequestResponseTrigger : public Trigger, std::string &> { public: - void process(std::shared_ptr container, std::string &response_body) { - this->trigger(std::move(container), response_body); + void process(const std::shared_ptr &container, std::string &response_body) { + this->trigger(container, response_body); } }; @@ -210,7 +210,7 @@ template class HttpRequestSendAction : public Action { this->max_response_buffer_size_ = max_response_buffer_size; } - void play(Ts... x) override { + void play(const Ts &...x) override { std::string body; if (this->body_.has_value()) { body = this->body_.value(x...); diff --git a/esphome/components/http_request/ota/automation.h b/esphome/components/http_request/ota/automation.h index d4c21f1c72..6c50bb9b0d 100644 --- a/esphome/components/http_request/ota/automation.h +++ b/esphome/components/http_request/ota/automation.h @@ -15,7 +15,7 @@ template class OtaHttpRequestComponentFlashAction : public Actio TEMPLATABLE_VALUE(std::string, url) TEMPLATABLE_VALUE(std::string, username) - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->md5_url_.has_value()) { this->parent_->set_md5_url(this->md5_url_.value(x...)); } diff --git a/esphome/components/htu21d/htu21d.h b/esphome/components/htu21d/htu21d.h index 9b3831b784..277c6ca3e5 100644 --- a/esphome/components/htu21d/htu21d.h +++ b/esphome/components/htu21d/htu21d.h @@ -41,7 +41,7 @@ template class SetHeaterLevelAction : public Action, publ public: TEMPLATABLE_VALUE(uint8_t, level) - void play(Ts... x) override { + void play(const Ts &...x) override { auto level = this->level_.value(x...); this->parent_->set_heater_level(level); @@ -52,7 +52,7 @@ template class SetHeaterAction : public Action, public Pa public: TEMPLATABLE_VALUE(bool, status) - void play(Ts... x) override { + void play(const Ts &...x) override { auto status = this->status_.value(x...); this->parent_->set_heater(status); diff --git a/esphome/components/integration/integration_sensor.h b/esphome/components/integration/integration_sensor.h index d9f2f5e50f..f075d163fe 100644 --- a/esphome/components/integration/integration_sensor.h +++ b/esphome/components/integration/integration_sensor.h @@ -75,7 +75,7 @@ template class ResetAction : public Action { public: explicit ResetAction(IntegrationSensor *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->reset(); } + void play(const Ts &...x) override { this->parent_->reset(); } protected: IntegrationSensor *parent_; diff --git a/esphome/components/key_collector/key_collector.h b/esphome/components/key_collector/key_collector.h index 35e8141ce5..735f396809 100644 --- a/esphome/components/key_collector/key_collector.h +++ b/esphome/components/key_collector/key_collector.h @@ -52,11 +52,11 @@ class KeyCollector : public Component { }; template class EnableAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_enabled(true); } + void play(const Ts &...x) override { this->parent_->set_enabled(true); } }; template class DisableAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_enabled(false); } + void play(const Ts &...x) override { this->parent_->set_enabled(false); } }; } // namespace key_collector diff --git a/esphome/components/ld2410/automation.h b/esphome/components/ld2410/automation.h index 7cb9855f84..f4f1c197b2 100644 --- a/esphome/components/ld2410/automation.h +++ b/esphome/components/ld2410/automation.h @@ -12,7 +12,7 @@ template class BluetoothPasswordSetAction : public Action explicit BluetoothPasswordSetAction(LD2410Component *ld2410_comp) : ld2410_comp_(ld2410_comp) {} TEMPLATABLE_VALUE(std::string, password) - void play(Ts... x) override { this->ld2410_comp_->set_bluetooth_password(this->password_.value(x...)); } + void play(const Ts &...x) override { this->ld2410_comp_->set_bluetooth_password(this->password_.value(x...)); } protected: LD2410Component *ld2410_comp_; diff --git a/esphome/components/ledc/ledc_output.h b/esphome/components/ledc/ledc_output.h index f04543bc5b..b24e3cfdb2 100644 --- a/esphome/components/ledc/ledc_output.h +++ b/esphome/components/ledc/ledc_output.h @@ -47,7 +47,7 @@ template class SetFrequencyAction : public Action { SetFrequencyAction(LEDCOutput *parent) : parent_(parent) {} TEMPLATABLE_VALUE(float, frequency); - void play(Ts... x) { + void play(const Ts &...x) { float freq = this->frequency_.value(x...); this->parent_->update_frequency(freq); } diff --git a/esphome/components/libretiny_pwm/libretiny_pwm.h b/esphome/components/libretiny_pwm/libretiny_pwm.h index 42ce40ca39..f911709054 100644 --- a/esphome/components/libretiny_pwm/libretiny_pwm.h +++ b/esphome/components/libretiny_pwm/libretiny_pwm.h @@ -40,7 +40,7 @@ template class SetFrequencyAction : public Action { SetFrequencyAction(LibreTinyPWM *parent) : parent_(parent) {} TEMPLATABLE_VALUE(float, frequency); - void play(Ts... x) { + void play(const Ts &...x) { float freq = this->frequency_.value(x...); this->parent_->update_frequency(freq); } diff --git a/esphome/components/light/automation.h b/esphome/components/light/automation.h index 6e055741da..8899db8bba 100644 --- a/esphome/components/light/automation.h +++ b/esphome/components/light/automation.h @@ -15,7 +15,7 @@ template class ToggleAction : public Action { TEMPLATABLE_VALUE(uint32_t, transition_length) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->state_->toggle(); call.set_transition_length(this->transition_length_.optional_value(x...)); call.perform(); @@ -44,7 +44,7 @@ template class LightControlAction : public Action { TEMPLATABLE_VALUE(float, warm_white) TEMPLATABLE_VALUE(std::string, effect) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->parent_->make_call(); call.set_color_mode(this->color_mode_.optional_value(x...)); call.set_state(this->state_.optional_value(x...)); @@ -74,7 +74,7 @@ template class DimRelativeAction : public Action { TEMPLATABLE_VALUE(float, relative_brightness) TEMPLATABLE_VALUE(uint32_t, transition_length) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->parent_->make_call(); float rel = this->relative_brightness_.value(x...); float cur; @@ -107,7 +107,7 @@ template class DimRelativeAction : public Action { template class LightIsOnCondition : public Condition { public: explicit LightIsOnCondition(LightState *state) : state_(state) {} - bool check(Ts... x) override { return this->state_->current_values.is_on(); } + bool check(const Ts &...x) override { return this->state_->current_values.is_on(); } protected: LightState *state_; @@ -115,7 +115,7 @@ template class LightIsOnCondition : public Condition { template class LightIsOffCondition : public Condition { public: explicit LightIsOffCondition(LightState *state) : state_(state) {} - bool check(Ts... x) override { return !this->state_->current_values.is_on(); } + bool check(const Ts &...x) override { return !this->state_->current_values.is_on(); } protected: LightState *state_; @@ -179,7 +179,7 @@ template class AddressableSet : public Action { TEMPLATABLE_VALUE(float, blue) TEMPLATABLE_VALUE(float, white) - void play(Ts... x) override { + void play(const Ts &...x) override { auto *out = (AddressableLight *) this->parent_->get_output(); int32_t range_from = interpret_index(this->range_from_.value_or(x..., 0), out->size()); if (range_from < 0 || range_from >= out->size()) diff --git a/esphome/components/lightwaverf/lightwaverf.h b/esphome/components/lightwaverf/lightwaverf.h index b9f2abfcb3..ee4e91e9d1 100644 --- a/esphome/components/lightwaverf/lightwaverf.h +++ b/esphome/components/lightwaverf/lightwaverf.h @@ -51,7 +51,7 @@ template class SendRawAction : public Action { void set_pulse_length(const int &data) { pulse_length_ = data; } void set_data(const std::vector &data) { code_ = data; } - void play(Ts... x) { + void play(const Ts &...x) { int repeats = this->repeat_.value(x...); int inverted = this->inverted_.value(x...); int pulse_length = this->pulse_length_.value(x...); diff --git a/esphome/components/lock/automation.h b/esphome/components/lock/automation.h index 8cb3b64ffe..0f596ef5e6 100644 --- a/esphome/components/lock/automation.h +++ b/esphome/components/lock/automation.h @@ -11,7 +11,7 @@ template class LockAction : public Action { public: explicit LockAction(Lock *a_lock) : lock_(a_lock) {} - void play(Ts... x) override { this->lock_->lock(); } + void play(const Ts &...x) override { this->lock_->lock(); } protected: Lock *lock_; @@ -21,7 +21,7 @@ template class UnlockAction : public Action { public: explicit UnlockAction(Lock *a_lock) : lock_(a_lock) {} - void play(Ts... x) override { this->lock_->unlock(); } + void play(const Ts &...x) override { this->lock_->unlock(); } protected: Lock *lock_; @@ -31,7 +31,7 @@ template class OpenAction : public Action { public: explicit OpenAction(Lock *a_lock) : lock_(a_lock) {} - void play(Ts... x) override { this->lock_->open(); } + void play(const Ts &...x) override { this->lock_->open(); } protected: Lock *lock_; @@ -40,7 +40,7 @@ template class OpenAction : public Action { template class LockCondition : public Condition { public: LockCondition(Lock *parent, bool state) : parent_(parent), state_(state) {} - bool check(Ts... x) override { + bool check(const Ts &...x) override { auto check_state = this->state_ ? LockState::LOCK_STATE_LOCKED : LockState::LOCK_STATE_UNLOCKED; return this->parent_->state == check_state; } diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 50d192fde3..1ae05f933f 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -129,7 +129,7 @@ template class ObjUpdateAction : public Action { public: explicit ObjUpdateAction(std::function &&lamb) : lamb_(std::move(lamb)) {} - void play(Ts... x) override { this->lamb_(x...); } + void play(const Ts &...x) override { this->lamb_(x...); } protected: std::function lamb_; @@ -263,7 +263,7 @@ class IdleTrigger : public Trigger<> { template class LvglAction : public Action, public Parented { public: explicit LvglAction(std::function &&lamb) : action_(std::move(lamb)) {} - void play(Ts... x) override { this->action_(this->parent_); } + void play(const Ts &...x) override { this->action_(this->parent_); } protected: std::function action_{}; @@ -272,7 +272,7 @@ template class LvglAction : public Action, public Parente template class LvglCondition : public Condition, public Parented { public: LvglCondition(std::function &&condition_lambda) : condition_lambda_(std::move(condition_lambda)) {} - bool check(Ts... x) override { return this->condition_lambda_(this->parent_); } + bool check(const Ts &...x) override { return this->condition_lambda_(this->parent_); } protected: std::function condition_lambda_{}; diff --git a/esphome/components/max17043/automation.h b/esphome/components/max17043/automation.h index 44729d119b..ac201a7309 100644 --- a/esphome/components/max17043/automation.h +++ b/esphome/components/max17043/automation.h @@ -10,7 +10,7 @@ template class SleepAction : public Action { public: explicit SleepAction(MAX17043Component *max17043) : max17043_(max17043) {} - void play(Ts... x) override { this->max17043_->sleep_mode(); } + void play(const Ts &...x) override { this->max17043_->sleep_mode(); } protected: MAX17043Component *max17043_; diff --git a/esphome/components/max6956/automation.h b/esphome/components/max6956/automation.h index c0b491dc7f..ca2c3e3ce4 100644 --- a/esphome/components/max6956/automation.h +++ b/esphome/components/max6956/automation.h @@ -13,7 +13,7 @@ template class SetCurrentGlobalAction : public Action { TEMPLATABLE_VALUE(uint8_t, brightness_global) - void play(Ts... x) override { + void play(const Ts &...x) override { this->max6956_->set_brightness_global(this->brightness_global_.value(x...)); this->max6956_->write_brightness_global(); } @@ -28,7 +28,7 @@ template class SetCurrentModeAction : public Action { TEMPLATABLE_VALUE(max6956::MAX6956CURRENTMODE, brightness_mode) - void play(Ts... x) override { + void play(const Ts &...x) override { this->max6956_->set_brightness_mode(this->brightness_mode_.value(x...)); this->max6956_->write_brightness_mode(); } diff --git a/esphome/components/max7219digit/automation.h b/esphome/components/max7219digit/automation.h index 02acebb109..be8245d14d 100644 --- a/esphome/components/max7219digit/automation.h +++ b/esphome/components/max7219digit/automation.h @@ -12,7 +12,7 @@ template class DisplayInvertAction : public Action, publi public: TEMPLATABLE_VALUE(bool, state) - void play(Ts... x) override { + void play(const Ts &...x) override { bool state = this->state_.value(x...); this->parent_->invert_on_off(state); } @@ -22,7 +22,7 @@ template class DisplayVisibilityAction : public Action, p public: TEMPLATABLE_VALUE(bool, state) - void play(Ts... x) override { + void play(const Ts &...x) override { bool state = this->state_.value(x...); this->parent_->turn_on_off(state); } @@ -32,7 +32,7 @@ template class DisplayReverseAction : public Action, publ public: TEMPLATABLE_VALUE(bool, state) - void play(Ts... x) override { + void play(const Ts &...x) override { bool state = this->state_.value(x...); this->parent_->set_reverse(state); } @@ -42,7 +42,7 @@ template class DisplayIntensityAction : public Action, pu public: TEMPLATABLE_VALUE(uint8_t, state) - void play(Ts... x) override { + void play(const Ts &...x) override { uint8_t state = this->state_.value(x...); this->parent_->set_intensity(state); } diff --git a/esphome/components/media_player/automation.h b/esphome/components/media_player/automation.h index 3af5959f32..50e7693cb5 100644 --- a/esphome/components/media_player/automation.h +++ b/esphome/components/media_player/automation.h @@ -11,7 +11,7 @@ template class MediaPlayerCommandAction : public Action, public Parented { public: TEMPLATABLE_VALUE(bool, announcement); - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->make_call().set_command(Command).set_announcement(this->announcement_.value(x...)).perform(); } }; @@ -36,7 +36,7 @@ using TurnOffAction = MediaPlayerCommandAction class PlayMediaAction : public Action, public Parented { TEMPLATABLE_VALUE(std::string, media_url) TEMPLATABLE_VALUE(bool, announcement) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->make_call() .set_media_url(this->media_url_.value(x...)) .set_announcement(this->announcement_.value(x...)) @@ -46,7 +46,7 @@ template class PlayMediaAction : public Action, public Pa template class VolumeSetAction : public Action, public Parented { TEMPLATABLE_VALUE(float, volume) - void play(Ts... x) override { this->parent_->make_call().set_volume(this->volume_.value(x...)).perform(); } + void play(const Ts &...x) override { this->parent_->make_call().set_volume(this->volume_.value(x...)).perform(); } }; class StateTrigger : public Trigger<> { @@ -75,32 +75,34 @@ using OffTrigger = MediaPlayerStateTrigger class IsIdleCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_IDLE; } + bool check(const Ts &...x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_IDLE; } }; template class IsPlayingCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING; } + bool check(const Ts &...x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING; } }; template class IsPausedCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED; } + bool check(const Ts &...x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED; } }; template class IsAnnouncingCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING; } + bool check(const Ts &...x) override { + return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING; + } }; template class IsOnCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_ON; } + bool check(const Ts &...x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_ON; } }; template class IsOffCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_OFF; } + bool check(const Ts &...x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_OFF; } }; } // namespace media_player diff --git a/esphome/components/mhz19/mhz19.h b/esphome/components/mhz19/mhz19.h index ec38f2cd2f..be36886d62 100644 --- a/esphome/components/mhz19/mhz19.h +++ b/esphome/components/mhz19/mhz19.h @@ -40,7 +40,7 @@ template class MHZ19CalibrateZeroAction : public Action { public: MHZ19CalibrateZeroAction(MHZ19Component *mhz19) : mhz19_(mhz19) {} - void play(Ts... x) override { this->mhz19_->calibrate_zero(); } + void play(const Ts &...x) override { this->mhz19_->calibrate_zero(); } protected: MHZ19Component *mhz19_; @@ -50,7 +50,7 @@ template class MHZ19ABCEnableAction : public Action { public: MHZ19ABCEnableAction(MHZ19Component *mhz19) : mhz19_(mhz19) {} - void play(Ts... x) override { this->mhz19_->abc_enable(); } + void play(const Ts &...x) override { this->mhz19_->abc_enable(); } protected: MHZ19Component *mhz19_; @@ -60,7 +60,7 @@ template class MHZ19ABCDisableAction : public Action { public: MHZ19ABCDisableAction(MHZ19Component *mhz19) : mhz19_(mhz19) {} - void play(Ts... x) override { this->mhz19_->abc_disable(); } + void play(const Ts &...x) override { this->mhz19_->abc_disable(); } protected: MHZ19Component *mhz19_; diff --git a/esphome/components/micro_wake_word/automation.h b/esphome/components/micro_wake_word/automation.h index f10a4ed347..e1795a7e64 100644 --- a/esphome/components/micro_wake_word/automation.h +++ b/esphome/components/micro_wake_word/automation.h @@ -9,23 +9,23 @@ namespace micro_wake_word { template class StartAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->start(); } + void play(const Ts &...x) override { this->parent_->start(); } }; template class StopAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->stop(); } + void play(const Ts &...x) override { this->parent_->stop(); } }; template class IsRunningCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_running(); } + bool check(const Ts &...x) override { return this->parent_->is_running(); } }; template class EnableModelAction : public Action { public: explicit EnableModelAction(WakeWordModel *wake_word_model) : wake_word_model_(wake_word_model) {} - void play(Ts... x) override { this->wake_word_model_->enable(); } + void play(const Ts &...x) override { this->wake_word_model_->enable(); } protected: WakeWordModel *wake_word_model_; @@ -34,7 +34,7 @@ template class EnableModelAction : public Action { template class DisableModelAction : public Action { public: explicit DisableModelAction(WakeWordModel *wake_word_model) : wake_word_model_(wake_word_model) {} - void play(Ts... x) override { this->wake_word_model_->disable(); } + void play(const Ts &...x) override { this->wake_word_model_->disable(); } protected: WakeWordModel *wake_word_model_; @@ -43,7 +43,7 @@ template class DisableModelAction : public Action { template class ModelIsEnabledCondition : public Condition { public: explicit ModelIsEnabledCondition(WakeWordModel *wake_word_model) : wake_word_model_(wake_word_model) {} - bool check(Ts... x) override { return this->wake_word_model_->is_enabled(); } + bool check(const Ts &...x) override { return this->wake_word_model_->is_enabled(); } protected: WakeWordModel *wake_word_model_; diff --git a/esphome/components/microphone/automation.h b/esphome/components/microphone/automation.h index 5745909c46..a6c4bdae66 100644 --- a/esphome/components/microphone/automation.h +++ b/esphome/components/microphone/automation.h @@ -9,18 +9,18 @@ namespace esphome { namespace microphone { template class CaptureAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->start(); } + void play(const Ts &...x) override { this->parent_->start(); } }; template class StopCaptureAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->stop(); } + void play(const Ts &...x) override { this->parent_->stop(); } }; template class MuteAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_mute_state(true); } + void play(const Ts &...x) override { this->parent_->set_mute_state(true); } }; template class UnmuteAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_mute_state(false); } + void play(const Ts &...x) override { this->parent_->set_mute_state(false); } }; class DataTrigger : public Trigger &> { @@ -32,12 +32,12 @@ class DataTrigger : public Trigger &> { template class IsCapturingCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_running(); } + bool check(const Ts &...x) override { return this->parent_->is_running(); } }; template class IsMutedCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->get_mute_state(); } + bool check(const Ts &...x) override { return this->parent_->get_mute_state(); } }; } // namespace microphone diff --git a/esphome/components/midea/ac_automations.h b/esphome/components/midea/ac_automations.h index e6fffa2511..760737be87 100644 --- a/esphome/components/midea/ac_automations.h +++ b/esphome/components/midea/ac_automations.h @@ -22,7 +22,7 @@ template class FollowMeAction : public MideaActionBase { TEMPLATABLE_VALUE(bool, use_fahrenheit) TEMPLATABLE_VALUE(bool, beeper) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->do_follow_me(this->temperature_.value(x...), this->use_fahrenheit_.value(x...), this->beeper_.value(x...)); } @@ -30,37 +30,37 @@ template class FollowMeAction : public MideaActionBase { template class SwingStepAction : public MideaActionBase { public: - void play(Ts... x) override { this->parent_->do_swing_step(); } + void play(const Ts &...x) override { this->parent_->do_swing_step(); } }; template class DisplayToggleAction : public MideaActionBase { public: - void play(Ts... x) override { this->parent_->do_display_toggle(); } + void play(const Ts &...x) override { this->parent_->do_display_toggle(); } }; template class BeeperOnAction : public MideaActionBase { public: - void play(Ts... x) override { this->parent_->do_beeper_on(); } + void play(const Ts &...x) override { this->parent_->do_beeper_on(); } }; template class BeeperOffAction : public MideaActionBase { public: - void play(Ts... x) override { this->parent_->do_beeper_off(); } + void play(const Ts &...x) override { this->parent_->do_beeper_off(); } }; template class PowerOnAction : public MideaActionBase { public: - void play(Ts... x) override { this->parent_->do_power_on(); } + void play(const Ts &...x) override { this->parent_->do_power_on(); } }; template class PowerOffAction : public MideaActionBase { public: - void play(Ts... x) override { this->parent_->do_power_off(); } + void play(const Ts &...x) override { this->parent_->do_power_off(); } }; template class PowerToggleAction : public MideaActionBase { public: - void play(Ts... x) override { this->parent_->do_power_toggle(); } + void play(const Ts &...x) override { this->parent_->do_power_toggle(); } }; } // namespace ac diff --git a/esphome/components/mixer/speaker/automation.h b/esphome/components/mixer/speaker/automation.h index b688fa2c1e..2234936628 100644 --- a/esphome/components/mixer/speaker/automation.h +++ b/esphome/components/mixer/speaker/automation.h @@ -9,7 +9,7 @@ namespace mixer_speaker { template class DuckingApplyAction : public Action, public Parented { TEMPLATABLE_VALUE(uint8_t, decibel_reduction) TEMPLATABLE_VALUE(uint32_t, duration) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->apply_ducking(this->decibel_reduction_.value(x...), this->duration_.value(x...)); } }; diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 325ca56f4b..79383ee857 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -389,7 +389,7 @@ template class MQTTPublishAction : public Action { TEMPLATABLE_VALUE(uint8_t, qos) TEMPLATABLE_VALUE(bool, retain) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->publish(this->topic_.value(x...), this->payload_.value(x...), this->qos_.value(x...), this->retain_.value(x...)); } @@ -407,7 +407,7 @@ template class MQTTPublishJsonAction : public Action { void set_payload(std::function payload) { this->payload_ = payload; } - void play(Ts... x) override { + void play(const Ts &...x) override { auto f = std::bind(&MQTTPublishJsonAction::encode_, this, x..., std::placeholders::_1); auto topic = this->topic_.value(x...); auto qos = this->qos_.value(x...); @@ -424,7 +424,7 @@ template class MQTTPublishJsonAction : public Action { template class MQTTConnectedCondition : public Condition { public: MQTTConnectedCondition(MQTTClientComponent *parent) : parent_(parent) {} - bool check(Ts... x) override { return this->parent_->is_connected(); } + bool check(const Ts &...x) override { return this->parent_->is_connected(); } protected: MQTTClientComponent *parent_; @@ -434,7 +434,7 @@ template class MQTTEnableAction : public Action { public: MQTTEnableAction(MQTTClientComponent *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->enable(); } + void play(const Ts &...x) override { this->parent_->enable(); } protected: MQTTClientComponent *parent_; @@ -444,7 +444,7 @@ template class MQTTDisableAction : public Action { public: MQTTDisableAction(MQTTClientComponent *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->disable(); } + void play(const Ts &...x) override { this->parent_->disable(); } protected: MQTTClientComponent *parent_; diff --git a/esphome/components/nau7802/nau7802.h b/esphome/components/nau7802/nau7802.h index 17e426ccc6..05452851ca 100644 --- a/esphome/components/nau7802/nau7802.h +++ b/esphome/components/nau7802/nau7802.h @@ -101,18 +101,18 @@ class NAU7802Sensor : public sensor::Sensor, public PollingComponent, public i2c template class NAU7802CalbrateExternalOffsetAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->calibrate_external_offset(); } + void play(const Ts &...x) override { this->parent_->calibrate_external_offset(); } }; template class NAU7802CalbrateInternalOffsetAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->calibrate_internal_offset(); } + void play(const Ts &...x) override { this->parent_->calibrate_internal_offset(); } }; template class NAU7802CalbrateGainAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->calibrate_gain(); } + void play(const Ts &...x) override { this->parent_->calibrate_gain(); } }; } // namespace nau7802 diff --git a/esphome/components/nextion/automation.h b/esphome/components/nextion/automation.h index c718355af8..8e85e15823 100644 --- a/esphome/components/nextion/automation.h +++ b/esphome/components/nextion/automation.h @@ -55,7 +55,7 @@ template class NextionSetBrightnessAction : public Action TEMPLATABLE_VALUE(float, brightness) - void play(Ts... x) override { + void play(const Ts &...x) override { this->component_->set_brightness(this->brightness_.value(x...)); this->component_->set_backlight_brightness(this->brightness_.value(x...)); } @@ -74,7 +74,7 @@ template class NextionPublishFloatAction : public Action TEMPLATABLE_VALUE(bool, publish_state) TEMPLATABLE_VALUE(bool, send_to_nextion) - void play(Ts... x) override { + void play(const Ts &...x) override { this->component_->set_state(this->state_.value(x...), this->publish_state_.value(x...), this->send_to_nextion_.value(x...)); } @@ -97,7 +97,7 @@ template class NextionPublishTextAction : public Action { TEMPLATABLE_VALUE(bool, publish_state) TEMPLATABLE_VALUE(bool, send_to_nextion) - void play(Ts... x) override { + void play(const Ts &...x) override { this->component_->set_state(this->state_.value(x...), this->publish_state_.value(x...), this->send_to_nextion_.value(x...)); } @@ -120,7 +120,7 @@ template class NextionPublishBoolAction : public Action { TEMPLATABLE_VALUE(bool, publish_state) TEMPLATABLE_VALUE(bool, send_to_nextion) - void play(Ts... x) override { + void play(const Ts &...x) override { this->component_->set_state(this->state_.value(x...), this->publish_state_.value(x...), this->send_to_nextion_.value(x...)); } diff --git a/esphome/components/number/automation.h b/esphome/components/number/automation.h index 33f0f9727e..79eba883c4 100644 --- a/esphome/components/number/automation.h +++ b/esphome/components/number/automation.h @@ -19,7 +19,7 @@ template class NumberSetAction : public Action { NumberSetAction(Number *number) : number_(number) {} TEMPLATABLE_VALUE(float, value) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->number_->make_call(); call.set_value(this->value_.value(x...)); call.perform(); @@ -35,7 +35,7 @@ template class NumberOperationAction : public Action { TEMPLATABLE_VALUE(NumberOperation, operation) TEMPLATABLE_VALUE(bool, cycle) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->number_->make_call(); call.with_operation(this->operation_.value(x...)); if (this->cycle_.has_value()) { @@ -74,7 +74,7 @@ template class NumberInRangeCondition : public Condition void set_min(float min) { this->min_ = min; } void set_max(float max) { this->max_ = max; } - bool check(Ts... x) override { + bool check(const Ts &...x) override { const float state = this->parent_->state; if (std::isnan(this->min_)) { return state <= this->max_; diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 3326cbe8d6..12d409ca29 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -207,7 +207,7 @@ template class OnlineImageSetUrlAction : public Action { OnlineImageSetUrlAction(OnlineImage *parent) : parent_(parent) {} TEMPLATABLE_VALUE(std::string, url) TEMPLATABLE_VALUE(bool, update) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->set_url(this->url_.value(x...)); if (this->update_.value(x...)) { this->parent_->update(); @@ -221,7 +221,7 @@ template class OnlineImageSetUrlAction : public Action { template class OnlineImageReleaseAction : public Action { public: OnlineImageReleaseAction(OnlineImage *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->release(); } + void play(const Ts &...x) override { this->parent_->release(); } protected: OnlineImage *parent_; diff --git a/esphome/components/output/automation.h b/esphome/components/output/automation.h index de84bb91ca..3279378129 100644 --- a/esphome/components/output/automation.h +++ b/esphome/components/output/automation.h @@ -12,7 +12,7 @@ template class TurnOffAction : public Action { public: TurnOffAction(BinaryOutput *output) : output_(output) {} - void play(Ts... x) override { this->output_->turn_off(); } + void play(const Ts &...x) override { this->output_->turn_off(); } protected: BinaryOutput *output_; @@ -22,7 +22,7 @@ template class TurnOnAction : public Action { public: TurnOnAction(BinaryOutput *output) : output_(output) {} - void play(Ts... x) override { this->output_->turn_on(); } + void play(const Ts &...x) override { this->output_->turn_on(); } protected: BinaryOutput *output_; @@ -34,7 +34,7 @@ template class SetLevelAction : public Action { TEMPLATABLE_VALUE(float, level) - void play(Ts... x) override { this->output_->set_level(this->level_.value(x...)); } + void play(const Ts &...x) override { this->output_->set_level(this->level_.value(x...)); } protected: FloatOutput *output_; @@ -46,7 +46,7 @@ template class SetMinPowerAction : public Action { TEMPLATABLE_VALUE(float, min_power) - void play(Ts... x) override { this->output_->set_min_power(this->min_power_.value(x...)); } + void play(const Ts &...x) override { this->output_->set_min_power(this->min_power_.value(x...)); } protected: FloatOutput *output_; @@ -58,7 +58,7 @@ template class SetMaxPowerAction : public Action { TEMPLATABLE_VALUE(float, max_power) - void play(Ts... x) override { this->output_->set_max_power(this->max_power_.value(x...)); } + void play(const Ts &...x) override { this->output_->set_max_power(this->max_power_.value(x...)); } protected: FloatOutput *output_; diff --git a/esphome/components/pcf85063/pcf85063.h b/esphome/components/pcf85063/pcf85063.h index 1a3fd704e5..b7034d4f00 100644 --- a/esphome/components/pcf85063/pcf85063.h +++ b/esphome/components/pcf85063/pcf85063.h @@ -85,12 +85,12 @@ class PCF85063Component : public time::RealTimeClock, public i2c::I2CDevice { template class WriteAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->write_time(); } + void play(const Ts &...x) override { this->parent_->write_time(); } }; template class ReadAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->read_time(); } + void play(const Ts &...x) override { this->parent_->read_time(); } }; } // namespace pcf85063 } // namespace esphome diff --git a/esphome/components/pcf8563/pcf8563.h b/esphome/components/pcf8563/pcf8563.h index b6832efe72..81aa816b42 100644 --- a/esphome/components/pcf8563/pcf8563.h +++ b/esphome/components/pcf8563/pcf8563.h @@ -113,12 +113,12 @@ class PCF8563Component : public time::RealTimeClock, public i2c::I2CDevice { template class WriteAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->write_time(); } + void play(const Ts &...x) override { this->parent_->write_time(); } }; template class ReadAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->read_time(); } + void play(const Ts &...x) override { this->parent_->read_time(); } }; } // namespace pcf8563 } // namespace esphome diff --git a/esphome/components/pid/pid_climate.h b/esphome/components/pid/pid_climate.h index 1a09ffdd20..dc0a92efed 100644 --- a/esphome/components/pid/pid_climate.h +++ b/esphome/components/pid/pid_climate.h @@ -109,7 +109,7 @@ template class PIDAutotuneAction : public Action { void set_positive_output(float positive_output) { positive_output_ = positive_output; } void set_negative_output(float negative_output) { negative_output_ = negative_output; } - void play(Ts... x) { + void play(const Ts &...x) { auto tuner = make_unique(); tuner->set_noiseband(this->noiseband_); tuner->set_output_negative(this->negative_output_); @@ -128,7 +128,7 @@ template class PIDResetIntegralTermAction : public Action public: PIDResetIntegralTermAction(PIDClimate *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->reset_integral_term(); } + void play(const Ts &...x) { this->parent_->reset_integral_term(); } protected: PIDClimate *parent_; @@ -138,7 +138,7 @@ template class PIDSetControlParametersAction : public Actionkp_.value(x...); auto ki = this->ki_.value(x...); auto kd = this->kd_.value(x...); diff --git a/esphome/components/pipsolar/output/pipsolar_output.h b/esphome/components/pipsolar/output/pipsolar_output.h index 29b2d116f2..b4b8000962 100644 --- a/esphome/components/pipsolar/output/pipsolar_output.h +++ b/esphome/components/pipsolar/output/pipsolar_output.h @@ -32,7 +32,7 @@ template class SetOutputAction : public Action { TEMPLATABLE_VALUE(float, level) - void play(Ts... x) override { this->output_->set_value(this->level_.value(x...)); } + void play(const Ts &...x) override { this->output_->set_value(this->level_.value(x...)); } protected: PipsolarOutput *output_; diff --git a/esphome/components/pmwcs3/pmwcs3.h b/esphome/components/pmwcs3/pmwcs3.h index d60f9d1f61..d63c516586 100644 --- a/esphome/components/pmwcs3/pmwcs3.h +++ b/esphome/components/pmwcs3/pmwcs3.h @@ -38,7 +38,7 @@ template class PMWCS3AirCalibrationAction : public Action public: PMWCS3AirCalibrationAction(PMWCS3Component *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->air_calibration(); } + void play(const Ts &...x) override { this->parent_->air_calibration(); } protected: PMWCS3Component *parent_; @@ -48,7 +48,7 @@ template class PMWCS3WaterCalibrationAction : public Actionparent_->water_calibration(); } + void play(const Ts &...x) override { this->parent_->water_calibration(); } protected: PMWCS3Component *parent_; @@ -59,7 +59,7 @@ template class PMWCS3NewI2cAddressAction : public Action PMWCS3NewI2cAddressAction(PMWCS3Component *parent) : parent_(parent) {} TEMPLATABLE_VALUE(int, new_address) - void play(Ts... x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); } + void play(const Ts &...x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); } protected: PMWCS3Component *parent_; diff --git a/esphome/components/pn532/pn532.h b/esphome/components/pn532/pn532.h index c8e9a40008..eeb15648fb 100644 --- a/esphome/components/pn532/pn532.h +++ b/esphome/components/pn532/pn532.h @@ -143,7 +143,7 @@ class PN532OnFinishedWriteTrigger : public Trigger<> { template class PN532IsWritingCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_writing(); } + bool check(const Ts &...x) override { return this->parent_->is_writing(); } }; } // namespace pn532 diff --git a/esphome/components/pn7150/automation.h b/esphome/components/pn7150/automation.h index aebb1b7573..21329a998a 100644 --- a/esphome/components/pn7150/automation.h +++ b/esphome/components/pn7150/automation.h @@ -23,42 +23,42 @@ class PN7150OnFinishedWriteTrigger : public Trigger<> { template class PN7150IsWritingCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_writing(); } + bool check(const Ts &...x) override { return this->parent_->is_writing(); } }; template class EmulationOffAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_tag_emulation_off(); } + void play(const Ts &...x) override { this->parent_->set_tag_emulation_off(); } }; template class EmulationOnAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_tag_emulation_on(); } + void play(const Ts &...x) override { this->parent_->set_tag_emulation_on(); } }; template class PollingOffAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_polling_off(); } + void play(const Ts &...x) override { this->parent_->set_polling_off(); } }; template class PollingOnAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_polling_on(); } + void play(const Ts &...x) override { this->parent_->set_polling_on(); } }; template class SetCleanModeAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->clean_mode(); } + void play(const Ts &...x) override { this->parent_->clean_mode(); } }; template class SetFormatModeAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->format_mode(); } + void play(const Ts &...x) override { this->parent_->format_mode(); } }; template class SetReadModeAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->read_mode(); } + void play(const Ts &...x) override { this->parent_->read_mode(); } }; template class SetEmulationMessageAction : public Action, public Parented { TEMPLATABLE_VALUE(std::string, message) TEMPLATABLE_VALUE(bool, include_android_app_record) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->set_tag_emulation_message(this->message_.optional_value(x...), this->include_android_app_record_.optional_value(x...)); } @@ -68,14 +68,14 @@ template class SetWriteMessageAction : public Action, pub TEMPLATABLE_VALUE(std::string, message) TEMPLATABLE_VALUE(bool, include_android_app_record) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->set_tag_write_message(this->message_.optional_value(x...), this->include_android_app_record_.optional_value(x...)); } }; template class SetWriteModeAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->write_mode(); } + void play(const Ts &...x) override { this->parent_->write_mode(); } }; } // namespace pn7150 diff --git a/esphome/components/pn7160/automation.h b/esphome/components/pn7160/automation.h index 854fb11684..08148c2311 100644 --- a/esphome/components/pn7160/automation.h +++ b/esphome/components/pn7160/automation.h @@ -23,42 +23,42 @@ class PN7160OnFinishedWriteTrigger : public Trigger<> { template class PN7160IsWritingCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_writing(); } + bool check(const Ts &...x) override { return this->parent_->is_writing(); } }; template class EmulationOffAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_tag_emulation_off(); } + void play(const Ts &...x) override { this->parent_->set_tag_emulation_off(); } }; template class EmulationOnAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_tag_emulation_on(); } + void play(const Ts &...x) override { this->parent_->set_tag_emulation_on(); } }; template class PollingOffAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_polling_off(); } + void play(const Ts &...x) override { this->parent_->set_polling_off(); } }; template class PollingOnAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->set_polling_on(); } + void play(const Ts &...x) override { this->parent_->set_polling_on(); } }; template class SetCleanModeAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->clean_mode(); } + void play(const Ts &...x) override { this->parent_->clean_mode(); } }; template class SetFormatModeAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->format_mode(); } + void play(const Ts &...x) override { this->parent_->format_mode(); } }; template class SetReadModeAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->read_mode(); } + void play(const Ts &...x) override { this->parent_->read_mode(); } }; template class SetEmulationMessageAction : public Action, public Parented { TEMPLATABLE_VALUE(std::string, message) TEMPLATABLE_VALUE(bool, include_android_app_record) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->set_tag_emulation_message(this->message_.optional_value(x...), this->include_android_app_record_.optional_value(x...)); } @@ -68,14 +68,14 @@ template class SetWriteMessageAction : public Action, pub TEMPLATABLE_VALUE(std::string, message) TEMPLATABLE_VALUE(bool, include_android_app_record) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->set_tag_write_message(this->message_.optional_value(x...), this->include_android_app_record_.optional_value(x...)); } }; template class SetWriteModeAction : public Action, public Parented { - void play(Ts... x) override { this->parent_->write_mode(); } + void play(const Ts &...x) override { this->parent_->write_mode(); } }; } // namespace pn7160 diff --git a/esphome/components/pulse_counter/automation.h b/esphome/components/pulse_counter/automation.h index d749540a95..0c0dc2552d 100644 --- a/esphome/components/pulse_counter/automation.h +++ b/esphome/components/pulse_counter/automation.h @@ -14,7 +14,7 @@ template class SetTotalPulsesAction : public Action { TEMPLATABLE_VALUE(uint32_t, total_pulses) - void play(Ts... x) override { this->pulse_counter_->set_total_pulses(this->total_pulses_.value(x...)); } + void play(const Ts &...x) override { this->pulse_counter_->set_total_pulses(this->total_pulses_.value(x...)); } protected: PulseCounterSensor *pulse_counter_; diff --git a/esphome/components/pulse_meter/automation.h b/esphome/components/pulse_meter/automation.h index 3112ded680..bf0768b7af 100644 --- a/esphome/components/pulse_meter/automation.h +++ b/esphome/components/pulse_meter/automation.h @@ -14,7 +14,7 @@ template class SetTotalPulsesAction : public Action { TEMPLATABLE_VALUE(uint32_t, total_pulses) - void play(Ts... x) override { this->pulse_meter_->set_total_pulses(this->total_pulses_.value(x...)); } + void play(const Ts &...x) override { this->pulse_meter_->set_total_pulses(this->total_pulses_.value(x...)); } protected: PulseMeterSensor *pulse_meter_; diff --git a/esphome/components/pzemac/pzemac.h b/esphome/components/pzemac/pzemac.h index 7a229b49ce..e5b96115f9 100644 --- a/esphome/components/pzemac/pzemac.h +++ b/esphome/components/pzemac/pzemac.h @@ -43,7 +43,7 @@ template class ResetEnergyAction : public Action { public: ResetEnergyAction(PZEMAC *pzemac) : pzemac_(pzemac) {} - void play(Ts... x) override { this->pzemac_->reset_energy_(); } + void play(const Ts &...x) override { this->pzemac_->reset_energy_(); } protected: PZEMAC *pzemac_; diff --git a/esphome/components/pzemdc/pzemdc.h b/esphome/components/pzemdc/pzemdc.h index b91ab4c0a5..2e6c26a10c 100644 --- a/esphome/components/pzemdc/pzemdc.h +++ b/esphome/components/pzemdc/pzemdc.h @@ -36,7 +36,7 @@ template class ResetEnergyAction : public Action { public: ResetEnergyAction(PZEMDC *pzemdc) : pzemdc_(pzemdc) {} - void play(Ts... x) override { this->pzemdc_->reset_energy(); } + void play(const Ts &...x) override { this->pzemdc_->reset_energy(); } protected: PZEMDC *pzemdc_; diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index b740ba8085..2cb79bf571 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -276,7 +276,7 @@ template class RemoteTransmitterActionBase : public RemoteTransm TEMPLATABLE_VALUE(uint32_t, send_wait) protected: - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->transmitter_->transmit(); this->encode(call.get_data(), x...); call.set_send_times(this->send_times_.value_or(x..., 1)); diff --git a/esphome/components/remote_transmitter/automation.h b/esphome/components/remote_transmitter/automation.h index 75b017ec61..bee1d0be8a 100644 --- a/esphome/components/remote_transmitter/automation.h +++ b/esphome/components/remote_transmitter/automation.h @@ -11,7 +11,7 @@ namespace remote_transmitter { template class DigitalWriteAction : public Action, public Parented { public: TEMPLATABLE_VALUE(bool, value) - void play(Ts... x) override { this->parent_->digital_write(this->value_.value(x...)); } + void play(const Ts &...x) override { this->parent_->digital_write(this->value_.value(x...)); } }; } // namespace remote_transmitter diff --git a/esphome/components/rf_bridge/rf_bridge.h b/esphome/components/rf_bridge/rf_bridge.h index fe6dd96b38..d2f75c819d 100644 --- a/esphome/components/rf_bridge/rf_bridge.h +++ b/esphome/components/rf_bridge/rf_bridge.h @@ -98,7 +98,7 @@ template class RFBridgeSendCodeAction : public Action { TEMPLATABLE_VALUE(uint16_t, high) TEMPLATABLE_VALUE(uint32_t, code) - void play(Ts... x) { + void play(const Ts &...x) { RFBridgeData data{}; data.sync = this->sync_.value(x...); data.low = this->low_.value(x...); @@ -118,7 +118,7 @@ template class RFBridgeSendAdvancedCodeAction : public Actionlength_.value(x...); data.protocol = this->protocol_.value(x...); @@ -134,7 +134,7 @@ template class RFBridgeLearnAction : public Action { public: RFBridgeLearnAction(RFBridgeComponent *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->learn(); } + void play(const Ts &...x) { this->parent_->learn(); } protected: RFBridgeComponent *parent_; @@ -144,7 +144,7 @@ template class RFBridgeStartAdvancedSniffingAction : public Acti public: RFBridgeStartAdvancedSniffingAction(RFBridgeComponent *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->start_advanced_sniffing(); } + void play(const Ts &...x) { this->parent_->start_advanced_sniffing(); } protected: RFBridgeComponent *parent_; @@ -154,7 +154,7 @@ template class RFBridgeStopAdvancedSniffingAction : public Actio public: RFBridgeStopAdvancedSniffingAction(RFBridgeComponent *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->stop_advanced_sniffing(); } + void play(const Ts &...x) { this->parent_->stop_advanced_sniffing(); } protected: RFBridgeComponent *parent_; @@ -164,7 +164,7 @@ template class RFBridgeStartBucketSniffingAction : public Action public: RFBridgeStartBucketSniffingAction(RFBridgeComponent *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->start_bucket_sniffing(); } + void play(const Ts &...x) { this->parent_->start_bucket_sniffing(); } protected: RFBridgeComponent *parent_; @@ -175,7 +175,7 @@ template class RFBridgeSendRawAction : public Action { RFBridgeSendRawAction(RFBridgeComponent *parent) : parent_(parent) {} TEMPLATABLE_VALUE(std::string, raw) - void play(Ts... x) { this->parent_->send_raw(this->raw_.value(x...)); } + void play(const Ts &...x) { this->parent_->send_raw(this->raw_.value(x...)); } protected: RFBridgeComponent *parent_; @@ -186,7 +186,7 @@ template class RFBridgeBeepAction : public Action { RFBridgeBeepAction(RFBridgeComponent *parent) : parent_(parent) {} TEMPLATABLE_VALUE(uint16_t, duration) - void play(Ts... x) { this->parent_->beep(this->duration_.value(x...)); } + void play(const Ts &...x) { this->parent_->beep(this->duration_.value(x...)); } protected: RFBridgeComponent *parent_; diff --git a/esphome/components/rotary_encoder/rotary_encoder.h b/esphome/components/rotary_encoder/rotary_encoder.h index e88ee9152a..14442f0565 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.h +++ b/esphome/components/rotary_encoder/rotary_encoder.h @@ -114,7 +114,7 @@ template class RotaryEncoderSetValueAction : public Actionencoder_->set_value(this->value_.value(x...)); } + void play(const Ts &...x) override { this->encoder_->set_value(this->value_.value(x...)); } protected: RotaryEncoderSensor *encoder_; diff --git a/esphome/components/rp2040_pwm/rp2040_pwm.h b/esphome/components/rp2040_pwm/rp2040_pwm.h index e499e72b06..b82765b1c0 100644 --- a/esphome/components/rp2040_pwm/rp2040_pwm.h +++ b/esphome/components/rp2040_pwm/rp2040_pwm.h @@ -45,7 +45,7 @@ template class SetFrequencyAction : public Action { SetFrequencyAction(RP2040PWM *parent) : parent_(parent) {} TEMPLATABLE_VALUE(float, frequency); - void play(Ts... x) { + void play(const Ts &...x) { float freq = this->frequency_.value(x...); this->parent_->update_frequency(freq); } diff --git a/esphome/components/rtttl/rtttl.h b/esphome/components/rtttl/rtttl.h index d536c6c08e..1e924a897c 100644 --- a/esphome/components/rtttl/rtttl.h +++ b/esphome/components/rtttl/rtttl.h @@ -122,7 +122,7 @@ template class PlayAction : public Action { PlayAction(Rtttl *rtttl) : rtttl_(rtttl) {} TEMPLATABLE_VALUE(std::string, value) - void play(Ts... x) override { this->rtttl_->play(this->value_.value(x...)); } + void play(const Ts &...x) override { this->rtttl_->play(this->value_.value(x...)); } protected: Rtttl *rtttl_; @@ -130,12 +130,12 @@ template class PlayAction : public Action { template class StopAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->stop(); } + void play(const Ts &...x) override { this->parent_->stop(); } }; template class IsPlayingCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_playing(); } + bool check(const Ts &...x) override { return this->parent_->is_playing(); } }; class FinishedPlaybackTrigger : public Trigger<> { diff --git a/esphome/components/scd30/automation.h b/esphome/components/scd30/automation.h index 37b3bc1674..1f89e7c815 100644 --- a/esphome/components/scd30/automation.h +++ b/esphome/components/scd30/automation.h @@ -9,7 +9,7 @@ namespace scd30 { template class ForceRecalibrationWithReference : public Action, public Parented { public: - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->value_.has_value()) { this->parent_->force_recalibration_with_reference(this->value_.value(x...)); } diff --git a/esphome/components/scd4x/automation.h b/esphome/components/scd4x/automation.h index dc43e9eb56..6ce1468577 100644 --- a/esphome/components/scd4x/automation.h +++ b/esphome/components/scd4x/automation.h @@ -9,7 +9,7 @@ namespace scd4x { template class PerformForcedCalibrationAction : public Action, public Parented { public: - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->value_.has_value()) { this->parent_->perform_forced_calibration(this->value_.value(x...)); } @@ -21,7 +21,7 @@ template class PerformForcedCalibrationAction : public Action class FactoryResetAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->factory_reset(); } + void play(const Ts &...x) override { this->parent_->factory_reset(); } }; } // namespace scd4x diff --git a/esphome/components/script/script.h b/esphome/components/script/script.h index 870a623f32..51cece01e4 100644 --- a/esphome/components/script/script.h +++ b/esphome/components/script/script.h @@ -215,7 +215,7 @@ template class ScriptExecuteAction, T template void set_args(F... x) { args_ = Args{x...}; } - void play(Ts... x) override { this->script_->execute_tuple(this->eval_args_(x...)); } + void play(const Ts &...x) override { this->script_->execute_tuple(this->eval_args_(x...)); } protected: // NOTE: @@ -249,7 +249,7 @@ template class ScriptStopAction : public Action public: ScriptStopAction(C *script) : script_(script) {} - void play(Ts... x) override { this->script_->stop(); } + void play(const Ts &...x) override { this->script_->stop(); } protected: C *script_; @@ -259,7 +259,7 @@ template class IsRunningCondition : public Conditionparent_->is_running(); } + bool check(const Ts &...x) override { return this->parent_->is_running(); } protected: C *parent_; @@ -281,7 +281,7 @@ template class ScriptWaitAction : public Action, this->disable_loop(); } - void play_complex(Ts... x) override { + void play_complex(const Ts &...x) override { this->num_running_++; // Check if we can continue immediately. if (!this->script_->is_running()) { @@ -312,7 +312,7 @@ template class ScriptWaitAction : public Action, this->disable_loop(); } - void play(Ts... x) override { /* ignore - see play_complex */ + void play(const Ts &...x) override { /* ignore - see play_complex */ } void stop() override { diff --git a/esphome/components/select/automation.h b/esphome/components/select/automation.h index 1250665188..3e42eaf98a 100644 --- a/esphome/components/select/automation.h +++ b/esphome/components/select/automation.h @@ -19,7 +19,7 @@ template class SelectSetAction : public Action { explicit SelectSetAction(Select *select) : select_(select) {} TEMPLATABLE_VALUE(std::string, option) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->select_->make_call(); call.set_option(this->option_.value(x...)); call.perform(); @@ -34,7 +34,7 @@ template class SelectSetIndexAction : public Action { explicit SelectSetIndexAction(Select *select) : select_(select) {} TEMPLATABLE_VALUE(size_t, index) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->select_->make_call(); call.set_index(this->index_.value(x...)); call.perform(); @@ -50,7 +50,7 @@ template class SelectOperationAction : public Action { TEMPLATABLE_VALUE(bool, cycle) TEMPLATABLE_VALUE(SelectOperation, operation) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->select_->make_call(); call.with_operation(this->operation_.value(x...)); if (this->cycle_.has_value()) { diff --git a/esphome/components/sen5x/automation.h b/esphome/components/sen5x/automation.h index 423b942000..558ea46e47 100644 --- a/esphome/components/sen5x/automation.h +++ b/esphome/components/sen5x/automation.h @@ -11,7 +11,7 @@ template class StartFanAction : public Action { public: explicit StartFanAction(SEN5XComponent *sen5x) : sen5x_(sen5x) {} - void play(Ts... x) override { this->sen5x_->start_fan_cleaning(); } + void play(const Ts &...x) override { this->sen5x_->start_fan_cleaning(); } protected: SEN5XComponent *sen5x_; diff --git a/esphome/components/senseair/senseair.h b/esphome/components/senseair/senseair.h index 5b66860f1a..9db849075d 100644 --- a/esphome/components/senseair/senseair.h +++ b/esphome/components/senseair/senseair.h @@ -42,7 +42,7 @@ template class SenseAirBackgroundCalibrationAction : public Acti public: SenseAirBackgroundCalibrationAction(SenseAirComponent *senseair) : senseair_(senseair) {} - void play(Ts... x) override { this->senseair_->background_calibration(); } + void play(const Ts &...x) override { this->senseair_->background_calibration(); } protected: SenseAirComponent *senseair_; @@ -52,7 +52,7 @@ template class SenseAirBackgroundCalibrationResultAction : publi public: SenseAirBackgroundCalibrationResultAction(SenseAirComponent *senseair) : senseair_(senseair) {} - void play(Ts... x) override { this->senseair_->background_calibration_result(); } + void play(const Ts &...x) override { this->senseair_->background_calibration_result(); } protected: SenseAirComponent *senseair_; @@ -62,7 +62,7 @@ template class SenseAirABCEnableAction : public Action { public: SenseAirABCEnableAction(SenseAirComponent *senseair) : senseair_(senseair) {} - void play(Ts... x) override { this->senseair_->abc_enable(); } + void play(const Ts &...x) override { this->senseair_->abc_enable(); } protected: SenseAirComponent *senseair_; @@ -72,7 +72,7 @@ template class SenseAirABCDisableAction : public Action { public: SenseAirABCDisableAction(SenseAirComponent *senseair) : senseair_(senseair) {} - void play(Ts... x) override { this->senseair_->abc_disable(); } + void play(const Ts &...x) override { this->senseair_->abc_disable(); } protected: SenseAirComponent *senseair_; @@ -82,7 +82,7 @@ template class SenseAirABCGetPeriodAction : public Action public: SenseAirABCGetPeriodAction(SenseAirComponent *senseair) : senseair_(senseair) {} - void play(Ts... x) override { this->senseair_->abc_get_period(); } + void play(const Ts &...x) override { this->senseair_->abc_get_period(); } protected: SenseAirComponent *senseair_; diff --git a/esphome/components/sensor/automation.h b/esphome/components/sensor/automation.h index 4f34c35023..df7d31a0c9 100644 --- a/esphome/components/sensor/automation.h +++ b/esphome/components/sensor/automation.h @@ -26,7 +26,7 @@ template class SensorPublishAction : public Action { SensorPublishAction(Sensor *sensor) : sensor_(sensor) {} TEMPLATABLE_VALUE(float, state) - void play(Ts... x) override { this->sensor_->publish_state(this->state_.value(x...)); } + void play(const Ts &...x) override { this->sensor_->publish_state(this->state_.value(x...)); } protected: Sensor *sensor_; @@ -90,7 +90,7 @@ template class SensorInRangeCondition : public Condition void set_min(float min) { this->min_ = min; } void set_max(float max) { this->max_ = max; } - bool check(Ts... x) override { + bool check(const Ts &...x) override { const float state = this->parent_->state; if (std::isnan(this->min_)) { return state <= this->max_; diff --git a/esphome/components/servo/servo.h b/esphome/components/servo/servo.h index ff1708dc53..3d15aefefe 100644 --- a/esphome/components/servo/servo.h +++ b/esphome/components/servo/servo.h @@ -57,7 +57,7 @@ template class ServoWriteAction : public Action { ServoWriteAction(Servo *servo) : servo_(servo) {} TEMPLATABLE_VALUE(float, value) - void play(Ts... x) override { this->servo_->write(this->value_.value(x...)); } + void play(const Ts &...x) override { this->servo_->write(this->value_.value(x...)); } protected: Servo *servo_; @@ -67,7 +67,7 @@ template class ServoDetachAction : public Action { public: ServoDetachAction(Servo *servo) : servo_(servo) {} - void play(Ts... x) override { this->servo_->detach(); } + void play(const Ts &...x) override { this->servo_->detach(); } protected: Servo *servo_; diff --git a/esphome/components/sim800l/sim800l.h b/esphome/components/sim800l/sim800l.h index bf7efd6915..a2da686ce1 100644 --- a/esphome/components/sim800l/sim800l.h +++ b/esphome/components/sim800l/sim800l.h @@ -162,7 +162,7 @@ template class Sim800LSendSmsAction : public Action { TEMPLATABLE_VALUE(std::string, recipient) TEMPLATABLE_VALUE(std::string, message) - void play(Ts... x) { + void play(const Ts &...x) { auto recipient = this->recipient_.value(x...); auto message = this->message_.value(x...); this->parent_->send_sms(recipient, message); @@ -177,7 +177,7 @@ template class Sim800LSendUssdAction : public Action { Sim800LSendUssdAction(Sim800LComponent *parent) : parent_(parent) {} TEMPLATABLE_VALUE(std::string, ussd) - void play(Ts... x) { + void play(const Ts &...x) { auto ussd_code = this->ussd_.value(x...); this->parent_->send_ussd(ussd_code); } @@ -191,7 +191,7 @@ template class Sim800LDialAction : public Action { Sim800LDialAction(Sim800LComponent *parent) : parent_(parent) {} TEMPLATABLE_VALUE(std::string, recipient) - void play(Ts... x) { + void play(const Ts &...x) { auto recipient = this->recipient_.value(x...); this->parent_->dial(recipient); } @@ -203,7 +203,7 @@ template class Sim800LConnectAction : public Action { public: Sim800LConnectAction(Sim800LComponent *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->connect(); } + void play(const Ts &...x) { this->parent_->connect(); } protected: Sim800LComponent *parent_; @@ -213,7 +213,7 @@ template class Sim800LDisconnectAction : public Action { public: Sim800LDisconnectAction(Sim800LComponent *parent) : parent_(parent) {} - void play(Ts... x) { this->parent_->disconnect(); } + void play(const Ts &...x) { this->parent_->disconnect(); } protected: Sim800LComponent *parent_; diff --git a/esphome/components/sound_level/sound_level.h b/esphome/components/sound_level/sound_level.h index 6a80a60ac7..dc35f69fe2 100644 --- a/esphome/components/sound_level/sound_level.h +++ b/esphome/components/sound_level/sound_level.h @@ -60,12 +60,12 @@ class SoundLevelComponent : public Component { template class StartAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->start(); } + void play(const Ts &...x) override { this->parent_->start(); } }; template class StopAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->stop(); } + void play(const Ts &...x) override { this->parent_->stop(); } }; } // namespace sound_level diff --git a/esphome/components/speaker/automation.h b/esphome/components/speaker/automation.h index c083796eea..80bba25030 100644 --- a/esphome/components/speaker/automation.h +++ b/esphome/components/speaker/automation.h @@ -19,7 +19,7 @@ template class PlayAction : public Action, public Parente this->static_ = true; } - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->static_) { this->parent_->play(this->data_static_); } else { @@ -36,14 +36,14 @@ template class PlayAction : public Action, public Parente template class VolumeSetAction : public Action, public Parented { TEMPLATABLE_VALUE(float, volume) - void play(Ts... x) override { this->parent_->set_volume(this->volume_.value(x...)); } + void play(const Ts &...x) override { this->parent_->set_volume(this->volume_.value(x...)); } }; template class MuteOnAction : public Action { public: explicit MuteOnAction(Speaker *speaker) : speaker_(speaker) {} - void play(Ts... x) override { this->speaker_->set_mute_state(true); } + void play(const Ts &...x) override { this->speaker_->set_mute_state(true); } protected: Speaker *speaker_; @@ -53,7 +53,7 @@ template class MuteOffAction : public Action { public: explicit MuteOffAction(Speaker *speaker) : speaker_(speaker) {} - void play(Ts... x) override { this->speaker_->set_mute_state(false); } + void play(const Ts &...x) override { this->speaker_->set_mute_state(false); } protected: Speaker *speaker_; @@ -61,22 +61,22 @@ template class MuteOffAction : public Action { template class StopAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->stop(); } + void play(const Ts &...x) override { this->parent_->stop(); } }; template class FinishAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->finish(); } + void play(const Ts &...x) override { this->parent_->finish(); } }; template class IsPlayingCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_running(); } + bool check(const Ts &...x) override { return this->parent_->is_running(); } }; template class IsStoppedCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_stopped(); } + bool check(const Ts &...x) override { return this->parent_->is_stopped(); } }; } // namespace speaker diff --git a/esphome/components/speaker/media_player/automation.h b/esphome/components/speaker/media_player/automation.h index d1a01aabc4..fdf3db07f9 100644 --- a/esphome/components/speaker/media_player/automation.h +++ b/esphome/components/speaker/media_player/automation.h @@ -14,7 +14,7 @@ template class PlayOnDeviceMediaAction : public Action, p TEMPLATABLE_VALUE(audio::AudioFile *, audio_file) TEMPLATABLE_VALUE(bool, announcement) TEMPLATABLE_VALUE(bool, enqueue) - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->play_file(this->audio_file_.value(x...), this->announcement_.value(x...), this->enqueue_.value(x...)); } diff --git a/esphome/components/sprinkler/automation.h b/esphome/components/sprinkler/automation.h index 59c6cd50e1..d6c877ae90 100644 --- a/esphome/components/sprinkler/automation.h +++ b/esphome/components/sprinkler/automation.h @@ -13,7 +13,7 @@ template class SetDividerAction : public Action { TEMPLATABLE_VALUE(uint32_t, divider) - void play(Ts... x) override { this->sprinkler_->set_divider(this->divider_.optional_value(x...)); } + void play(const Ts &...x) override { this->sprinkler_->set_divider(this->divider_.optional_value(x...)); } protected: Sprinkler *sprinkler_; @@ -25,7 +25,7 @@ template class SetMultiplierAction : public Action { TEMPLATABLE_VALUE(float, multiplier) - void play(Ts... x) override { this->sprinkler_->set_multiplier(this->multiplier_.optional_value(x...)); } + void play(const Ts &...x) override { this->sprinkler_->set_multiplier(this->multiplier_.optional_value(x...)); } protected: Sprinkler *sprinkler_; @@ -38,7 +38,7 @@ template class QueueValveAction : public Action { TEMPLATABLE_VALUE(size_t, valve_number) TEMPLATABLE_VALUE(uint32_t, valve_run_duration) - void play(Ts... x) override { + void play(const Ts &...x) override { this->sprinkler_->queue_valve(this->valve_number_.optional_value(x...), this->valve_run_duration_.optional_value(x...)); } @@ -51,7 +51,7 @@ template class ClearQueuedValvesAction : public Action { public: explicit ClearQueuedValvesAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {} - void play(Ts... x) override { this->sprinkler_->clear_queued_valves(); } + void play(const Ts &...x) override { this->sprinkler_->clear_queued_valves(); } protected: Sprinkler *sprinkler_; @@ -63,7 +63,7 @@ template class SetRepeatAction : public Action { TEMPLATABLE_VALUE(uint32_t, repeat) - void play(Ts... x) override { this->sprinkler_->set_repeat(this->repeat_.optional_value(x...)); } + void play(const Ts &...x) override { this->sprinkler_->set_repeat(this->repeat_.optional_value(x...)); } protected: Sprinkler *sprinkler_; @@ -76,7 +76,7 @@ template class SetRunDurationAction : public Action { TEMPLATABLE_VALUE(size_t, valve_number) TEMPLATABLE_VALUE(uint32_t, valve_run_duration) - void play(Ts... x) override { + void play(const Ts &...x) override { this->sprinkler_->set_valve_run_duration(this->valve_number_.optional_value(x...), this->valve_run_duration_.optional_value(x...)); } @@ -89,7 +89,7 @@ template class StartFromQueueAction : public Action { public: explicit StartFromQueueAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {} - void play(Ts... x) override { this->sprinkler_->start_from_queue(); } + void play(const Ts &...x) override { this->sprinkler_->start_from_queue(); } protected: Sprinkler *sprinkler_; @@ -99,7 +99,7 @@ template class StartFullCycleAction : public Action { public: explicit StartFullCycleAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {} - void play(Ts... x) override { this->sprinkler_->start_full_cycle(); } + void play(const Ts &...x) override { this->sprinkler_->start_full_cycle(); } protected: Sprinkler *sprinkler_; @@ -112,7 +112,7 @@ template class StartSingleValveAction : public Action { TEMPLATABLE_VALUE(size_t, valve_to_start) TEMPLATABLE_VALUE(uint32_t, valve_run_duration) - void play(Ts... x) override { + void play(const Ts &...x) override { this->sprinkler_->start_single_valve(this->valve_to_start_.optional_value(x...), this->valve_run_duration_.optional_value(x...)); } @@ -125,7 +125,7 @@ template class ShutdownAction : public Action { public: explicit ShutdownAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {} - void play(Ts... x) override { this->sprinkler_->shutdown(); } + void play(const Ts &...x) override { this->sprinkler_->shutdown(); } protected: Sprinkler *sprinkler_; @@ -135,7 +135,7 @@ template class NextValveAction : public Action { public: explicit NextValveAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {} - void play(Ts... x) override { this->sprinkler_->next_valve(); } + void play(const Ts &...x) override { this->sprinkler_->next_valve(); } protected: Sprinkler *sprinkler_; @@ -145,7 +145,7 @@ template class PreviousValveAction : public Action { public: explicit PreviousValveAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {} - void play(Ts... x) override { this->sprinkler_->previous_valve(); } + void play(const Ts &...x) override { this->sprinkler_->previous_valve(); } protected: Sprinkler *sprinkler_; @@ -155,7 +155,7 @@ template class PauseAction : public Action { public: explicit PauseAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {} - void play(Ts... x) override { this->sprinkler_->pause(); } + void play(const Ts &...x) override { this->sprinkler_->pause(); } protected: Sprinkler *sprinkler_; @@ -165,7 +165,7 @@ template class ResumeAction : public Action { public: explicit ResumeAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {} - void play(Ts... x) override { this->sprinkler_->resume(); } + void play(const Ts &...x) override { this->sprinkler_->resume(); } protected: Sprinkler *sprinkler_; @@ -175,7 +175,7 @@ template class ResumeOrStartAction : public Action { public: explicit ResumeOrStartAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {} - void play(Ts... x) override { this->sprinkler_->resume_or_start_full_cycle(); } + void play(const Ts &...x) override { this->sprinkler_->resume_or_start_full_cycle(); } protected: Sprinkler *sprinkler_; diff --git a/esphome/components/sps30/automation.h b/esphome/components/sps30/automation.h index 443aafb575..67af813687 100644 --- a/esphome/components/sps30/automation.h +++ b/esphome/components/sps30/automation.h @@ -11,7 +11,7 @@ template class StartFanAction : public Action { public: explicit StartFanAction(SPS30Component *sps30) : sps30_(sps30) {} - void play(Ts... x) override { this->sps30_->start_fan_cleaning(); } + void play(const Ts &...x) override { this->sps30_->start_fan_cleaning(); } protected: SPS30Component *sps30_; diff --git a/esphome/components/stepper/stepper.h b/esphome/components/stepper/stepper.h index 1cf4830b1f..2bad672494 100644 --- a/esphome/components/stepper/stepper.h +++ b/esphome/components/stepper/stepper.h @@ -44,7 +44,7 @@ template class SetTargetAction : public Action { TEMPLATABLE_VALUE(int32_t, target) - void play(Ts... x) override { this->parent_->set_target(this->target_.value(x...)); } + void play(const Ts &...x) override { this->parent_->set_target(this->target_.value(x...)); } protected: Stepper *parent_; @@ -56,7 +56,7 @@ template class ReportPositionAction : public Action { TEMPLATABLE_VALUE(int32_t, position) - void play(Ts... x) override { this->parent_->report_position(this->position_.value(x...)); } + void play(const Ts &...x) override { this->parent_->report_position(this->position_.value(x...)); } protected: Stepper *parent_; @@ -68,7 +68,7 @@ template class SetSpeedAction : public Action { TEMPLATABLE_VALUE(float, speed); - void play(Ts... x) override { + void play(const Ts &...x) override { float speed = this->speed_.value(x...); this->parent_->set_max_speed(speed); this->parent_->on_update_speed(); @@ -84,7 +84,7 @@ template class SetAccelerationAction : public Action { TEMPLATABLE_VALUE(float, acceleration); - void play(Ts... x) override { + void play(const Ts &...x) override { float acceleration = this->acceleration_.value(x...); this->parent_->set_acceleration(acceleration); } @@ -99,7 +99,7 @@ template class SetDecelerationAction : public Action { TEMPLATABLE_VALUE(float, deceleration); - void play(Ts... x) override { + void play(const Ts &...x) override { float deceleration = this->deceleration_.value(x...); this->parent_->set_deceleration(deceleration); } diff --git a/esphome/components/sun/sun.h b/esphome/components/sun/sun.h index 77d62d34c3..67a0306a37 100644 --- a/esphome/components/sun/sun.h +++ b/esphome/components/sun/sun.h @@ -115,7 +115,7 @@ template class SunCondition : public Condition, public Pa TEMPLATABLE_VALUE(double, elevation); void set_above(bool above) { above_ = above; } - bool check(Ts... x) override { + bool check(const Ts &...x) override { double elevation = this->elevation_.value(x...); double current = this->parent_->elevation(); if (this->above_) { diff --git a/esphome/components/switch/automation.h b/esphome/components/switch/automation.h index b8cbc9b976..27d3474c97 100644 --- a/esphome/components/switch/automation.h +++ b/esphome/components/switch/automation.h @@ -11,7 +11,7 @@ template class TurnOnAction : public Action { public: explicit TurnOnAction(Switch *a_switch) : switch_(a_switch) {} - void play(Ts... x) override { this->switch_->turn_on(); } + void play(const Ts &...x) override { this->switch_->turn_on(); } protected: Switch *switch_; @@ -21,7 +21,7 @@ template class TurnOffAction : public Action { public: explicit TurnOffAction(Switch *a_switch) : switch_(a_switch) {} - void play(Ts... x) override { this->switch_->turn_off(); } + void play(const Ts &...x) override { this->switch_->turn_off(); } protected: Switch *switch_; @@ -31,7 +31,7 @@ template class ToggleAction : public Action { public: explicit ToggleAction(Switch *a_switch) : switch_(a_switch) {} - void play(Ts... x) override { this->switch_->toggle(); } + void play(const Ts &...x) override { this->switch_->toggle(); } protected: Switch *switch_; @@ -43,7 +43,7 @@ template class ControlAction : public Action { TEMPLATABLE_VALUE(bool, state) - void play(Ts... x) override { + void play(const Ts &...x) override { auto state = this->state_.optional_value(x...); if (state.has_value()) { this->switch_->control(*state); @@ -57,7 +57,7 @@ template class ControlAction : public Action { template class SwitchCondition : public Condition { public: SwitchCondition(Switch *parent, bool state) : parent_(parent), state_(state) {} - bool check(Ts... x) override { return this->parent_->state == this->state_; } + bool check(const Ts &...x) override { return this->parent_->state == this->state_; } protected: Switch *parent_; @@ -98,7 +98,7 @@ template class SwitchPublishAction : public Action { SwitchPublishAction(Switch *a_switch) : switch_(a_switch) {} TEMPLATABLE_VALUE(bool, state) - void play(Ts... x) override { this->switch_->publish_state(this->state_.value(x...)); } + void play(const Ts &...x) override { this->switch_->publish_state(this->state_.value(x...)); } protected: Switch *switch_; diff --git a/esphome/components/sx126x/automation.h b/esphome/components/sx126x/automation.h index 520ef99718..6b2371e253 100644 --- a/esphome/components/sx126x/automation.h +++ b/esphome/components/sx126x/automation.h @@ -9,7 +9,7 @@ namespace sx126x { template class RunImageCalAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->run_image_cal(); } + void play(const Ts &...x) override { this->parent_->run_image_cal(); } }; template class SendPacketAction : public Action, public Parented { @@ -24,7 +24,7 @@ template class SendPacketAction : public Action, public P this->static_ = true; } - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->static_) { this->parent_->transmit_packet(this->data_static_); } else { @@ -40,22 +40,22 @@ template class SendPacketAction : public Action, public P template class SetModeTxAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->set_mode_tx(); } + void play(const Ts &...x) override { this->parent_->set_mode_tx(); } }; template class SetModeRxAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->set_mode_rx(); } + void play(const Ts &...x) override { this->parent_->set_mode_rx(); } }; template class SetModeSleepAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->set_mode_sleep(); } + void play(const Ts &...x) override { this->parent_->set_mode_sleep(); } }; template class SetModeStandbyAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->set_mode_standby(STDBY_XOSC); } + void play(const Ts &...x) override { this->parent_->set_mode_standby(STDBY_XOSC); } }; } // namespace sx126x diff --git a/esphome/components/sx127x/automation.h b/esphome/components/sx127x/automation.h index 2b9c261de1..eae16c11fa 100644 --- a/esphome/components/sx127x/automation.h +++ b/esphome/components/sx127x/automation.h @@ -9,7 +9,7 @@ namespace sx127x { template class RunImageCalAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->run_image_cal(); } + void play(const Ts &...x) override { this->parent_->run_image_cal(); } }; template class SendPacketAction : public Action, public Parented { @@ -24,7 +24,7 @@ template class SendPacketAction : public Action, public P this->static_ = true; } - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->static_) { this->parent_->transmit_packet(this->data_static_); } else { @@ -40,22 +40,22 @@ template class SendPacketAction : public Action, public P template class SetModeTxAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->set_mode_tx(); } + void play(const Ts &...x) override { this->parent_->set_mode_tx(); } }; template class SetModeRxAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->set_mode_rx(); } + void play(const Ts &...x) override { this->parent_->set_mode_rx(); } }; template class SetModeSleepAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->set_mode_sleep(); } + void play(const Ts &...x) override { this->parent_->set_mode_sleep(); } }; template class SetModeStandbyAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->set_mode_standby(); } + void play(const Ts &...x) override { this->parent_->set_mode_standby(); } }; } // namespace sx127x diff --git a/esphome/components/template/lock/automation.h b/esphome/components/template/lock/automation.h index 6124546592..bd110b7b0c 100644 --- a/esphome/components/template/lock/automation.h +++ b/esphome/components/template/lock/automation.h @@ -11,7 +11,7 @@ template class TemplateLockPublishAction : public Action, public: TEMPLATABLE_VALUE(lock::LockState, state) - void play(Ts... x) override { this->parent_->publish_state(this->state_.value(x...)); } + void play(const Ts &...x) override { this->parent_->publish_state(this->state_.value(x...)); } }; } // namespace template_ diff --git a/esphome/components/template/valve/automation.h b/esphome/components/template/valve/automation.h index af9b070c60..e3f394ac7c 100644 --- a/esphome/components/template/valve/automation.h +++ b/esphome/components/template/valve/automation.h @@ -11,7 +11,7 @@ template class TemplateValvePublishAction : public Action TEMPLATABLE_VALUE(float, position) TEMPLATABLE_VALUE(valve::ValveOperation, current_operation) - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->position_.has_value()) this->parent_->position = this->position_.value(x...); if (this->current_operation_.has_value()) diff --git a/esphome/components/text/automation.h b/esphome/components/text/automation.h index f20a4f433b..e7667fe491 100644 --- a/esphome/components/text/automation.h +++ b/esphome/components/text/automation.h @@ -19,7 +19,7 @@ template class TextSetAction : public Action { explicit TextSetAction(Text *text) : text_(text) {} TEMPLATABLE_VALUE(std::string, value) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->text_->make_call(); call.set_value(this->value_.value(x...)); call.perform(); diff --git a/esphome/components/text_sensor/automation.h b/esphome/components/text_sensor/automation.h index d7286845e0..709c54c140 100644 --- a/esphome/components/text_sensor/automation.h +++ b/esphome/components/text_sensor/automation.h @@ -29,7 +29,7 @@ template class TextSensorStateCondition : public Conditionparent_->state == this->state_.value(x...); } + bool check(const Ts &...x) override { return this->parent_->state == this->state_.value(x...); } protected: TextSensor *parent_; @@ -40,7 +40,7 @@ template class TextSensorPublishAction : public Action { TextSensorPublishAction(TextSensor *sensor) : sensor_(sensor) {} TEMPLATABLE_VALUE(std::string, state) - void play(Ts... x) override { this->sensor_->publish_state(this->state_.value(x...)); } + void play(const Ts &...x) override { this->sensor_->publish_state(this->state_.value(x...)); } protected: TextSensor *sensor_; diff --git a/esphome/components/time/real_time_clock.h b/esphome/components/time/real_time_clock.h index 7e60bbd234..bbcecaa628 100644 --- a/esphome/components/time/real_time_clock.h +++ b/esphome/components/time/real_time_clock.h @@ -67,7 +67,7 @@ class RealTimeClock : public PollingComponent { template class TimeHasTimeCondition : public Condition { public: TimeHasTimeCondition(RealTimeClock *parent) : parent_(parent) {} - bool check(Ts... x) override { return this->parent_->now().is_valid(); } + bool check(const Ts &...x) override { return this->parent_->now().is_valid(); } protected: RealTimeClock *parent_; diff --git a/esphome/components/tm1651/tm1651.h b/esphome/components/tm1651/tm1651.h index 7079910adf..83e74c5f33 100644 --- a/esphome/components/tm1651/tm1651.h +++ b/esphome/components/tm1651/tm1651.h @@ -61,7 +61,7 @@ template class SetBrightnessAction : public Action, publi public: TEMPLATABLE_VALUE(uint8_t, brightness) - void play(Ts... x) override { + void play(const Ts &...x) override { auto brightness = this->brightness_.value(x...); this->parent_->set_brightness(brightness); } @@ -71,7 +71,7 @@ template class SetLevelAction : public Action, public Par public: TEMPLATABLE_VALUE(uint8_t, level) - void play(Ts... x) override { + void play(const Ts &...x) override { auto level = this->level_.value(x...); this->parent_->set_level(level); } @@ -81,7 +81,7 @@ template class SetLevelPercentAction : public Action, pub public: TEMPLATABLE_VALUE(uint8_t, level_percent) - void play(Ts... x) override { + void play(const Ts &...x) override { auto level_percent = this->level_percent_.value(x...); this->parent_->set_level_percent(level_percent); } @@ -89,12 +89,12 @@ template class SetLevelPercentAction : public Action, pub template class TurnOnAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->turn_on(); } + void play(const Ts &...x) override { this->parent_->turn_on(); } }; template class TurnOffAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->turn_off(); } + void play(const Ts &...x) override { this->parent_->turn_off(); } }; } // namespace tm1651 diff --git a/esphome/components/uart/automation.h b/esphome/components/uart/automation.h index 9c599253de..ad2c4d2bf1 100644 --- a/esphome/components/uart/automation.h +++ b/esphome/components/uart/automation.h @@ -23,7 +23,7 @@ template class UARTWriteAction : public Action, public Pa this->static_ = true; } - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->static_) { this->parent_->write_array(this->data_static_); } else { diff --git a/esphome/components/udp/automation.h b/esphome/components/udp/automation.h index f75e6d35bf..c5e5e2eae8 100644 --- a/esphome/components/udp/automation.h +++ b/esphome/components/udp/automation.h @@ -20,7 +20,7 @@ template class UDPWriteAction : public Action, public Par this->static_ = true; } - void play(Ts... x) override { + void play(const Ts &...x) override { if (this->static_) { this->parent_->send_packet(this->data_static_); } else { diff --git a/esphome/components/ufire_ec/ufire_ec.h b/esphome/components/ufire_ec/ufire_ec.h index 3d436555a2..bfbed1b43e 100644 --- a/esphome/components/ufire_ec/ufire_ec.h +++ b/esphome/components/ufire_ec/ufire_ec.h @@ -65,7 +65,7 @@ template class UFireECCalibrateProbeAction : public Actionparent_->calibrate_probe(this->solution_.value(x...), this->temperature_.value(x...)); } @@ -77,7 +77,7 @@ template class UFireECResetAction : public Action { public: UFireECResetAction(UFireECComponent *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->reset_board(); } + void play(const Ts &...x) override { this->parent_->reset_board(); } protected: UFireECComponent *parent_; diff --git a/esphome/components/ufire_ise/ufire_ise.h b/esphome/components/ufire_ise/ufire_ise.h index 01efdcdb55..fe9a6dfb9c 100644 --- a/esphome/components/ufire_ise/ufire_ise.h +++ b/esphome/components/ufire_ise/ufire_ise.h @@ -64,7 +64,7 @@ template class UFireISECalibrateProbeLowAction : public Actionparent_->calibrate_probe_low(this->solution_.value(x...)); } + void play(const Ts &...x) override { this->parent_->calibrate_probe_low(this->solution_.value(x...)); } protected: UFireISEComponent *parent_; @@ -75,7 +75,7 @@ template class UFireISECalibrateProbeHighAction : public Action< UFireISECalibrateProbeHighAction(UFireISEComponent *parent) : parent_(parent) {} TEMPLATABLE_VALUE(float, solution) - void play(Ts... x) override { this->parent_->calibrate_probe_high(this->solution_.value(x...)); } + void play(const Ts &...x) override { this->parent_->calibrate_probe_high(this->solution_.value(x...)); } protected: UFireISEComponent *parent_; @@ -85,7 +85,7 @@ template class UFireISEResetAction : public Action { public: UFireISEResetAction(UFireISEComponent *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->reset_board(); } + void play(const Ts &...x) override { this->parent_->reset_board(); } protected: UFireISEComponent *parent_; diff --git a/esphome/components/update/automation.h b/esphome/components/update/automation.h index df50f86a0c..8563b855fe 100644 --- a/esphome/components/update/automation.h +++ b/esphome/components/update/automation.h @@ -11,12 +11,12 @@ template class PerformAction : public Action, public Pare TEMPLATABLE_VALUE(bool, force) public: - void play(Ts... x) override { this->parent_->perform(this->force_.value(x...)); } + void play(const Ts &...x) override { this->parent_->perform(this->force_.value(x...)); } }; template class IsAvailableCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->state == UPDATE_STATE_AVAILABLE; } + bool check(const Ts &...x) override { return this->parent_->state == UPDATE_STATE_AVAILABLE; } }; } // namespace update diff --git a/esphome/components/valve/automation.h b/esphome/components/valve/automation.h index f2c06270c0..87e9cde088 100644 --- a/esphome/components/valve/automation.h +++ b/esphome/components/valve/automation.h @@ -11,7 +11,7 @@ template class OpenAction : public Action { public: explicit OpenAction(Valve *valve) : valve_(valve) {} - void play(Ts... x) override { this->valve_->make_call().set_command_open().perform(); } + void play(const Ts &...x) override { this->valve_->make_call().set_command_open().perform(); } protected: Valve *valve_; @@ -21,7 +21,7 @@ template class CloseAction : public Action { public: explicit CloseAction(Valve *valve) : valve_(valve) {} - void play(Ts... x) override { this->valve_->make_call().set_command_close().perform(); } + void play(const Ts &...x) override { this->valve_->make_call().set_command_close().perform(); } protected: Valve *valve_; @@ -31,7 +31,7 @@ template class StopAction : public Action { public: explicit StopAction(Valve *valve) : valve_(valve) {} - void play(Ts... x) override { this->valve_->make_call().set_command_stop().perform(); } + void play(const Ts &...x) override { this->valve_->make_call().set_command_stop().perform(); } protected: Valve *valve_; @@ -41,7 +41,7 @@ template class ToggleAction : public Action { public: explicit ToggleAction(Valve *valve) : valve_(valve) {} - void play(Ts... x) override { this->valve_->make_call().set_command_toggle().perform(); } + void play(const Ts &...x) override { this->valve_->make_call().set_command_toggle().perform(); } protected: Valve *valve_; @@ -54,7 +54,7 @@ template class ControlAction : public Action { TEMPLATABLE_VALUE(bool, stop) TEMPLATABLE_VALUE(float, position) - void play(Ts... x) override { + void play(const Ts &...x) override { auto call = this->valve_->make_call(); if (this->stop_.has_value()) call.set_stop(this->stop_.value(x...)); @@ -70,7 +70,7 @@ template class ControlAction : public Action { template class ValveIsOpenCondition : public Condition { public: ValveIsOpenCondition(Valve *valve) : valve_(valve) {} - bool check(Ts... x) override { return this->valve_->is_fully_open(); } + bool check(const Ts &...x) override { return this->valve_->is_fully_open(); } protected: Valve *valve_; @@ -79,7 +79,7 @@ template class ValveIsOpenCondition : public Condition { template class ValveIsClosedCondition : public Condition { public: ValveIsClosedCondition(Valve *valve) : valve_(valve) {} - bool check(Ts... x) override { return this->valve_->is_fully_closed(); } + bool check(const Ts &...x) override { return this->valve_->is_fully_closed(); } protected: Valve *valve_; diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index 95f77dbf09..8d3d3497ec 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -324,7 +324,7 @@ template class StartAction : public Action, public Parent TEMPLATABLE_VALUE(std::string, wake_word); public: - void play(Ts... x) override { + void play(const Ts &...x) override { this->parent_->set_wake_word(this->wake_word_.value(x...)); this->parent_->request_start(false, this->silence_detection_); } @@ -337,22 +337,22 @@ template class StartAction : public Action, public Parent template class StartContinuousAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->request_start(true, true); } + void play(const Ts &...x) override { this->parent_->request_start(true, true); } }; template class StopAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->request_stop(); } + void play(const Ts &...x) override { this->parent_->request_stop(); } }; template class IsRunningCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_running() || this->parent_->is_continuous(); } + bool check(const Ts &...x) override { return this->parent_->is_running() || this->parent_->is_continuous(); } }; template class ConnectedCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->get_api_connection() != nullptr; } + bool check(const Ts &...x) override { return this->parent_->get_api_connection() != nullptr; } }; extern VoiceAssistant *global_voice_assistant; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 89b7b1fa41..ac63e0eb0c 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -455,22 +455,22 @@ extern WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid- template class WiFiConnectedCondition : public Condition { public: - bool check(Ts... x) override { return global_wifi_component->is_connected(); } + bool check(const Ts &...x) override { return global_wifi_component->is_connected(); } }; template class WiFiEnabledCondition : public Condition { public: - bool check(Ts... x) override { return !global_wifi_component->is_disabled(); } + bool check(const Ts &...x) override { return !global_wifi_component->is_disabled(); } }; template class WiFiEnableAction : public Action { public: - void play(Ts... x) override { global_wifi_component->enable(); } + void play(const Ts &...x) override { global_wifi_component->enable(); } }; template class WiFiDisableAction : public Action { public: - void play(Ts... x) override { global_wifi_component->disable(); } + void play(const Ts &...x) override { global_wifi_component->disable(); } }; template class WiFiConfigureAction : public Action, public Component { @@ -480,7 +480,7 @@ template class WiFiConfigureAction : public Action, publi TEMPLATABLE_VALUE(bool, save) TEMPLATABLE_VALUE(uint32_t, connection_timeout) - void play(Ts... x) override { + void play(const Ts &...x) override { auto ssid = this->ssid_.value(x...); auto password = this->password_.value(x...); // Avoid multiple calls diff --git a/esphome/components/wireguard/wireguard.h b/esphome/components/wireguard/wireguard.h index 5db9a48c90..f8f79b835d 100644 --- a/esphome/components/wireguard/wireguard.h +++ b/esphome/components/wireguard/wireguard.h @@ -148,25 +148,25 @@ std::string mask_key(const std::string &key); /// Condition to check if remote peer is online. template class WireguardPeerOnlineCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_peer_up(); } + bool check(const Ts &...x) override { return this->parent_->is_peer_up(); } }; /// Condition to check if Wireguard component is enabled. template class WireguardEnabledCondition : public Condition, public Parented { public: - bool check(Ts... x) override { return this->parent_->is_enabled(); } + bool check(const Ts &...x) override { return this->parent_->is_enabled(); } }; /// Action to enable Wireguard component. template class WireguardEnableAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->enable(); } + void play(const Ts &...x) override { this->parent_->enable(); } }; /// Action to disable Wireguard component. template class WireguardDisableAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->disable(); } + void play(const Ts &...x) override { this->parent_->disable(); } }; } // namespace wireguard diff --git a/esphome/core/automation.h b/esphome/core/automation.h index c22b3ca0e3..33e08c9c1c 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -148,7 +148,7 @@ template class TemplatableValue { template class Condition { public: /// Check whether this condition passes. This condition check must be instant, and not cause any delays. - virtual bool check(Ts... x) = 0; + virtual bool check(const Ts &...x) = 0; /// Call check with a tuple of values as parameter. bool check_tuple(const std::tuple &tuple) { @@ -166,7 +166,7 @@ template class Automation; template class Trigger { public: /// Inform the parent automation that the event has triggered. - void trigger(Ts... x) { + void trigger(const Ts &...x) { if (this->automation_parent_ == nullptr) return; this->automation_parent_->trigger(x...); @@ -194,7 +194,7 @@ template class ActionList; template class Action { public: - virtual void play_complex(Ts... x) { + virtual void play_complex(const Ts &...x) { this->num_running_++; this->play(x...); this->play_next_(x...); @@ -222,8 +222,8 @@ template class Action { friend ActionList; template friend class ContinuationAction; - virtual void play(Ts... x) = 0; - void play_next_(Ts... x) { + virtual void play(const Ts &...x) = 0; + void play_next_(const Ts &...x) { if (this->num_running_ > 0) { this->num_running_--; if (this->next_ != nullptr) { @@ -273,7 +273,7 @@ template class ActionList { this->add_action(action); } } - void play(Ts... x) { + void play(const Ts &...x) { if (this->actions_begin_ != nullptr) this->actions_begin_->play_complex(x...); } @@ -315,7 +315,7 @@ template class Automation { void stop() { this->actions_.stop(); } - void trigger(Ts... x) { this->actions_.play(x...); } + void trigger(const Ts &...x) { this->actions_.play(x...); } bool is_running() { return this->actions_.is_running(); } diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index 083bb3ae31..128a2d4b08 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -17,7 +17,7 @@ namespace esphome { template class AndCondition : public Condition { public: explicit AndCondition(std::initializer_list *> conditions) : conditions_(conditions) {} - bool check(Ts... x) override { + bool check(const Ts &...x) override { for (auto *condition : this->conditions_) { if (!condition->check(x...)) return false; @@ -33,7 +33,7 @@ template class AndCondition : public Condition { template class OrCondition : public Condition { public: explicit OrCondition(std::initializer_list *> conditions) : conditions_(conditions) {} - bool check(Ts... x) override { + bool check(const Ts &...x) override { for (auto *condition : this->conditions_) { if (condition->check(x...)) return true; @@ -49,7 +49,7 @@ template class OrCondition : public Condition { template class NotCondition : public Condition { public: explicit NotCondition(Condition *condition) : condition_(condition) {} - bool check(Ts... x) override { return !this->condition_->check(x...); } + bool check(const Ts &...x) override { return !this->condition_->check(x...); } protected: Condition *condition_; @@ -58,7 +58,7 @@ template class NotCondition : public Condition { template class XorCondition : public Condition { public: explicit XorCondition(std::initializer_list *> conditions) : conditions_(conditions) {} - bool check(Ts... x) override { + bool check(const Ts &...x) override { size_t result = 0; for (auto *condition : this->conditions_) { result += condition->check(x...); @@ -74,7 +74,7 @@ template class XorCondition : public Condition { template class LambdaCondition : public Condition { public: explicit LambdaCondition(std::function &&f) : f_(std::move(f)) {} - bool check(Ts... x) override { return this->f_(x...); } + bool check(const Ts &...x) override { return this->f_(x...); } protected: std::function f_; @@ -86,7 +86,7 @@ template class LambdaCondition : public Condition { template class StatelessLambdaCondition : public Condition { public: explicit StatelessLambdaCondition(bool (*f)(Ts...)) : f_(f) {} - bool check(Ts... x) override { return this->f_(x...); } + bool check(const Ts &...x) override { return this->f_(x...); } protected: bool (*f_)(Ts...); @@ -107,7 +107,7 @@ template class ForCondition : public Condition, public Co return cond; } - bool check(Ts... x) override { + bool check(const Ts &...x) override { if (!this->check_internal()) return false; return millis() - this->last_inactive_ >= this->time_.value(x...); @@ -171,7 +171,7 @@ template class DelayAction : public Action, public Compon TEMPLATABLE_VALUE(uint32_t, delay) - void play_complex(Ts... x) override { + void play_complex(const Ts &...x) override { auto f = std::bind(&DelayAction::play_next_, this, x...); this->num_running_++; @@ -187,7 +187,7 @@ template class DelayAction : public Action, public Compon } float get_setup_priority() const override { return setup_priority::HARDWARE; } - void play(Ts... x) override { /* ignore - see play_complex */ + void play(const Ts &...x) override { /* ignore - see play_complex */ } void stop() override { this->cancel_timeout("delay"); } @@ -197,7 +197,7 @@ template class LambdaAction : public Action { public: explicit LambdaAction(std::function &&f) : f_(std::move(f)) {} - void play(Ts... x) override { this->f_(x...); } + void play(const Ts &...x) override { this->f_(x...); } protected: std::function f_; @@ -210,7 +210,7 @@ template class StatelessLambdaAction : public Action { public: explicit StatelessLambdaAction(void (*f)(Ts...)) : f_(f) {} - void play(Ts... x) override { this->f_(x...); } + void play(const Ts &...x) override { this->f_(x...); } protected: void (*f_)(Ts...); @@ -223,7 +223,7 @@ template class ContinuationAction : public Action { public: explicit ContinuationAction(Action *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->play_next_(x...); } + void play(const Ts &...x) override { this->parent_->play_next_(x...); } protected: Action *parent_; @@ -238,7 +238,7 @@ template class WhileLoopContinuation : public Action { public: explicit WhileLoopContinuation(WhileAction *parent) : parent_(parent) {} - void play(Ts... x) override; + void play(const Ts &...x) override; protected: WhileAction *parent_; @@ -258,7 +258,7 @@ template class IfAction : public Action { this->else_.add_action(new ContinuationAction(this)); } - void play_complex(Ts... x) override { + void play_complex(const Ts &...x) override { this->num_running_++; bool res = this->condition_->check(x...); if (res) { @@ -276,7 +276,7 @@ template class IfAction : public Action { } } - void play(Ts... x) override { /* ignore - see play_complex */ + void play(const Ts &...x) override { /* ignore - see play_complex */ } void stop() override { @@ -301,7 +301,7 @@ template class WhileAction : public Action { friend class WhileLoopContinuation; - void play_complex(Ts... x) override { + void play_complex(const Ts &...x) override { this->num_running_++; // Initial condition check if (!this->condition_->check(x...)) { @@ -316,7 +316,7 @@ template class WhileAction : public Action { } } - void play(Ts... x) override { /* ignore - see play_complex */ + void play(const Ts &...x) override { /* ignore - see play_complex */ } void stop() override { this->then_.stop(); } @@ -327,7 +327,7 @@ template class WhileAction : public Action { }; // Implementation of WhileLoopContinuation::play -template void WhileLoopContinuation::play(Ts... x) { +template void WhileLoopContinuation::play(const Ts &...x) { if (this->parent_->num_running_ > 0 && this->parent_->condition_->check(x...)) { // play again this->parent_->then_.play(x...); @@ -346,7 +346,7 @@ template class RepeatLoopContinuation : public Action *parent) : parent_(parent) {} - void play(uint32_t iteration, Ts... x) override; + void play(const uint32_t &iteration, const Ts &...x) override; protected: RepeatAction *parent_; @@ -363,7 +363,7 @@ template class RepeatAction : public Action { friend class RepeatLoopContinuation; - void play_complex(Ts... x) override { + void play_complex(const Ts &...x) override { this->num_running_++; if (this->count_.value(x...) > 0) { this->then_.play(0, x...); @@ -372,7 +372,7 @@ template class RepeatAction : public Action { } } - void play(Ts... x) override { /* ignore - see play_complex */ + void play(const Ts &...x) override { /* ignore - see play_complex */ } void stop() override { this->then_.stop(); } @@ -382,12 +382,12 @@ template class RepeatAction : public Action { }; // Implementation of RepeatLoopContinuation::play -template void RepeatLoopContinuation::play(uint32_t iteration, Ts... x) { - iteration++; - if (iteration >= this->parent_->count_.value(x...)) { +template void RepeatLoopContinuation::play(const uint32_t &iteration, const Ts &...x) { + uint32_t next_iteration = iteration + 1; + if (next_iteration >= this->parent_->count_.value(x...)) { this->parent_->play_next_(x...); } else { - this->parent_->then_.play(iteration, x...); + this->parent_->then_.play(next_iteration, x...); } } @@ -409,7 +409,7 @@ template class WaitUntilAction : public Action, public Co this->disable_loop(); } - void play_complex(Ts... x) override { + void play_complex(const Ts &...x) override { this->num_running_++; // Check if we can continue immediately. if (this->condition_->check(x...)) { @@ -463,7 +463,7 @@ template class WaitUntilAction : public Action, public Co float get_setup_priority() const override { return setup_priority::DATA; } - void play(Ts... x) override { /* ignore - see play_complex */ + void play(const Ts &...x) override { /* ignore - see play_complex */ } protected: @@ -475,7 +475,7 @@ template class UpdateComponentAction : public Action { public: UpdateComponentAction(PollingComponent *component) : component_(component) {} - void play(Ts... x) override { + void play(const Ts &...x) override { if (!this->component_->is_ready()) return; this->component_->update(); @@ -489,7 +489,7 @@ template class SuspendComponentAction : public Action { public: SuspendComponentAction(PollingComponent *component) : component_(component) {} - void play(Ts... x) override { + void play(const Ts &...x) override { if (!this->component_->is_ready()) return; this->component_->stop_poller(); @@ -504,7 +504,7 @@ template class ResumeComponentAction : public Action { ResumeComponentAction(PollingComponent *component) : component_(component) {} TEMPLATABLE_VALUE(uint32_t, update_interval) - void play(Ts... x) override { + void play(const Ts &...x) override { if (!this->component_->is_ready()) { return; } diff --git a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h index cdc04d491b..3dca2da2e9 100644 --- a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h +++ b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h @@ -39,7 +39,7 @@ template class EnableAction : public Action { public: EnableAction(LoopTestComponent *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->service_enable(); } + void play(const Ts &...x) override { this->parent_->service_enable(); } protected: LoopTestComponent *parent_; @@ -49,7 +49,7 @@ template class DisableAction : public Action { public: DisableAction(LoopTestComponent *parent) : parent_(parent) {} - void play(Ts... x) override { this->parent_->service_disable(); } + void play(const Ts &...x) override { this->parent_->service_disable(); } protected: LoopTestComponent *parent_; From 32975c9d8be215e06795e1f7cd64d59b22a44386 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 4 Nov 2025 19:49:27 -0600 Subject: [PATCH 395/526] [select][lvgl] Fix FixedVector size() returning 0 when using operator[] after init() (#11721) --- esphome/components/lvgl/select/lvgl_select.h | 4 ++-- esphome/components/select/select_traits.cpp | 4 ++-- esphome/core/helpers.h | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/esphome/components/lvgl/select/lvgl_select.h b/esphome/components/lvgl/select/lvgl_select.h index 3b1fd67d68..d4c9631073 100644 --- a/esphome/components/lvgl/select/lvgl_select.h +++ b/esphome/components/lvgl/select/lvgl_select.h @@ -59,8 +59,8 @@ class LVGLSelect : public select::Select, public Component { const auto &opts = this->widget_->get_options(); FixedVector opt_ptrs; opt_ptrs.init(opts.size()); - for (size_t i = 0; i < opts.size(); i++) { - opt_ptrs[i] = opts[i].c_str(); + for (const auto &opt : opts) { + opt_ptrs.push_back(opt.c_str()); } this->traits.set_options(opt_ptrs); } diff --git a/esphome/components/select/select_traits.cpp b/esphome/components/select/select_traits.cpp index c6ded98ebf..e5e12bdc7a 100644 --- a/esphome/components/select/select_traits.cpp +++ b/esphome/components/select/select_traits.cpp @@ -7,8 +7,8 @@ void SelectTraits::set_options(const std::initializer_list &option void SelectTraits::set_options(const FixedVector &options) { this->options_.init(options.size()); - for (size_t i = 0; i < options.size(); i++) { - this->options_[i] = options[i]; + for (const auto &opt : options) { + this->options_.push_back(opt); } } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 91ddc70afa..660874ed1a 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -248,6 +248,8 @@ template class FixedVector { } // Allocate capacity - can be called multiple times to reinit + // IMPORTANT: After calling init(), you MUST use push_back() to add elements. + // Direct assignment via operator[] does NOT update the size counter. void init(size_t n) { cleanup_(); reset_(); From 6b522dfee6880df49d6826734310ca6ab7f3d25c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 09:14:21 -0600 Subject: [PATCH 396/526] [wifi_info] Reduce heap usage by up to 1.7KB in scan_results sensor (#11723) --- esphome/components/wifi_info/wifi_info_text_sensor.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.h b/esphome/components/wifi_info/wifi_info_text_sensor.h index 2cb96123a0..04889d6bb3 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.h +++ b/esphome/components/wifi_info/wifi_info_text_sensor.h @@ -10,6 +10,8 @@ namespace esphome { namespace wifi_info { +static constexpr size_t MAX_STATE_LENGTH = 255; + class IPAddressWiFiInfo : public PollingComponent, public text_sensor::TextSensor { public: void update() override { @@ -71,11 +73,14 @@ class ScanResultsWiFiInfo : public PollingComponent, public text_sensor::TextSen scan_results += "dB\n"; } + // There's a limit of 255 characters per state. + // Longer states just don't get sent so we truncate it. + if (scan_results.length() > MAX_STATE_LENGTH) { + scan_results.resize(MAX_STATE_LENGTH); + } if (this->last_scan_results_ != scan_results) { this->last_scan_results_ = scan_results; - // There's a limit of 255 characters per state. - // Longer states just don't get sent so we truncate it. - this->publish_state(scan_results.substr(0, 255)); + this->publish_state(scan_results); } } float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } From 6e2dbbf636dbe98665904618553caddb0eb46315 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 09:15:05 -0600 Subject: [PATCH 397/526] [voice_assistant] Eliminate substr() allocations in text truncation (#11725) --- esphome/components/voice_assistant/voice_assistant.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 7ece73994f..fd35dc7d09 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -657,7 +657,8 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { ESP_LOGW(TAG, "No text in STT_END event"); return; } else if (text.length() > 500) { - text = text.substr(0, 497) + "..."; + text.resize(497); + text += "..."; } ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str()); this->defer([this, text]() { this->stt_end_trigger_->trigger(text); }); @@ -714,7 +715,8 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { return; } if (text.length() > 500) { - text = text.substr(0, 497) + "..."; + text.resize(497); + text += "..."; } ESP_LOGD(TAG, "Response: \"%s\"", text.c_str()); this->defer([this, text]() { From 479f8dd85c75b7ca961a7c031330eb5ca4ea41a3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 09:17:28 -0600 Subject: [PATCH 398/526] [rtttl] Reduce flash usage by eliminating substr() allocations (#11722) --- esphome/components/rtttl/rtttl.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index b79f27e2e5..65fcc207d4 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -35,9 +35,9 @@ void Rtttl::dump_config() { void Rtttl::play(std::string rtttl) { if (this->state_ != State::STATE_STOPPED && this->state_ != State::STATE_STOPPING) { - int pos = this->rtttl_.find(':'); - auto name = this->rtttl_.substr(0, pos); - ESP_LOGW(TAG, "Already playing: %s", name.c_str()); + size_t pos = this->rtttl_.find(':'); + size_t len = (pos != std::string::npos) ? pos : this->rtttl_.length(); + ESP_LOGW(TAG, "Already playing: %.*s", (int) len, this->rtttl_.c_str()); return; } @@ -59,8 +59,7 @@ void Rtttl::play(std::string rtttl) { return; } - auto name = this->rtttl_.substr(0, this->position_); - ESP_LOGD(TAG, "Playing song %s", name.c_str()); + ESP_LOGD(TAG, "Playing song %.*s", (int) this->position_, this->rtttl_.c_str()); // get default duration this->position_ = this->rtttl_.find("d=", this->position_); From b7838671ae53b501d829a5fc7d50e04f7dc7790d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 10:57:20 -0600 Subject: [PATCH 399/526] [ld2420] Eliminate substr() allocation in firmware version parsing (#11724) --- esphome/components/ld2420/ld2420.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ld2420/ld2420.cpp b/esphome/components/ld2420/ld2420.cpp index b48c336d4e..f544acc112 100644 --- a/esphome/components/ld2420/ld2420.cpp +++ b/esphome/components/ld2420/ld2420.cpp @@ -174,7 +174,7 @@ static uint8_t calc_checksum(void *data, size_t size) { static int get_firmware_int(const char *version_string) { std::string version_str = version_string; if (version_str[0] == 'v') { - version_str = version_str.substr(1); + version_str.erase(0, 1); } version_str.erase(remove(version_str.begin(), version_str.end(), '.'), version_str.end()); int version_integer = stoi(version_str); From df53ff7afed11a1fc18ad03484d77c9c64bf1024 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 12:13:12 -0600 Subject: [PATCH 400/526] [scheduler] Extract helper functions to improve code readability (#11730) --- esphome/core/scheduler.cpp | 45 ++++++++++++++++++++------------------ esphome/core/scheduler.h | 34 ++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 11d59c2499..d285af2d0e 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -117,12 +117,8 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type item->set_name(name_cstr, !is_static_string); item->type = type; item->callback = std::move(func); - // Initialize remove to false (though it should already be from constructor) -#ifdef ESPHOME_THREAD_MULTI_ATOMICS - item->remove.store(false, std::memory_order_relaxed); -#else - item->remove = false; -#endif + // Reset remove flag - recycled items may have been cancelled (remove=true) in previous use + this->set_item_removed_(item.get(), false); item->is_retry = is_retry; #ifndef ESPHOME_THREAD_SINGLE @@ -153,21 +149,7 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type } #ifdef ESPHOME_DEBUG_SCHEDULER - // Validate static strings in debug mode - if (is_static_string && name_cstr != nullptr) { - validate_static_string(name_cstr); - } - - // Debug logging - const char *type_str = (type == SchedulerItem::TIMEOUT) ? "timeout" : "interval"; - if (type == SchedulerItem::TIMEOUT) { - ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ")", type_str, LOG_STR_ARG(item->get_source()), - name_cstr ? name_cstr : "(null)", type_str, delay); - } else { - ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ", offset=%" PRIu32 ")", type_str, LOG_STR_ARG(item->get_source()), - name_cstr ? name_cstr : "(null)", type_str, delay, - static_cast(item->get_next_execution() - now)); - } + this->debug_log_timer_(item.get(), is_static_string, name_cstr, type, delay, now); #endif /* ESPHOME_DEBUG_SCHEDULER */ // For retries, check if there's a cancelled timeout first @@ -787,4 +769,25 @@ void Scheduler::recycle_item_(std::unique_ptr item) { // else: unique_ptr will delete the item when it goes out of scope } +#ifdef ESPHOME_DEBUG_SCHEDULER +void Scheduler::debug_log_timer_(const SchedulerItem *item, bool is_static_string, const char *name_cstr, + SchedulerItem::Type type, uint32_t delay, uint64_t now) { + // Validate static strings in debug mode + if (is_static_string && name_cstr != nullptr) { + validate_static_string(name_cstr); + } + + // Debug logging + const char *type_str = (type == SchedulerItem::TIMEOUT) ? "timeout" : "interval"; + if (type == SchedulerItem::TIMEOUT) { + ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ")", type_str, LOG_STR_ARG(item->get_source()), + name_cstr ? name_cstr : "(null)", type_str, delay); + } else { + ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ", offset=%" PRIu32 ")", type_str, LOG_STR_ARG(item->get_source()), + name_cstr ? name_cstr : "(null)", type_str, delay, + static_cast(item->get_next_execution() - now)); + } +} +#endif /* ESPHOME_DEBUG_SCHEDULER */ + } // namespace esphome diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index f6ec07294d..fd16840240 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -266,6 +266,12 @@ class Scheduler { // Helper to perform full cleanup when too many items are cancelled void full_cleanup_removed_items_(); +#ifdef ESPHOME_DEBUG_SCHEDULER + // Helper for debug logging in set_timer_common_ - extracted to reduce code size + void debug_log_timer_(const SchedulerItem *item, bool is_static_string, const char *name_cstr, + SchedulerItem::Type type, uint32_t delay, uint64_t now); +#endif /* ESPHOME_DEBUG_SCHEDULER */ + #ifndef ESPHOME_THREAD_SINGLE // Helper to process defer queue - inline for performance in hot path inline void process_defer_queue_(uint32_t &now) { @@ -367,6 +373,24 @@ class Scheduler { #endif } + // Helper to set item removal flag (platform-specific) + // For ESPHOME_THREAD_MULTI_NO_ATOMICS platforms, the caller must hold the scheduler lock before calling this + // function. Uses memory_order_release when setting to true (for cancellation synchronization), + // and memory_order_relaxed when setting to false (for initialization). + void set_item_removed_(SchedulerItem *item, bool removed) { +#ifdef ESPHOME_THREAD_MULTI_ATOMICS + // Multi-threaded with atomics: use atomic store with appropriate ordering + // Release ordering when setting to true ensures cancellation is visible to other threads + // Relaxed ordering when setting to false is sufficient for initialization + item->remove.store(removed, removed ? std::memory_order_release : std::memory_order_relaxed); +#else + // Single-threaded (ESPHOME_THREAD_SINGLE) or + // multi-threaded without atomics (ESPHOME_THREAD_MULTI_NO_ATOMICS): direct write + // For ESPHOME_THREAD_MULTI_NO_ATOMICS, caller MUST hold lock! + item->remove = removed; +#endif + } + // Helper to mark matching items in a container as removed // Returns the number of items marked for removal // IMPORTANT: Caller must hold the scheduler lock before calling this function. @@ -383,15 +407,7 @@ class Scheduler { continue; if (this->matches_item_(item, component, name_cstr, type, match_retry)) { // Mark item for removal (platform-specific) -#ifdef ESPHOME_THREAD_MULTI_ATOMICS - // Multi-threaded with atomics: use atomic store - item->remove.store(true, std::memory_order_release); -#else - // Single-threaded (ESPHOME_THREAD_SINGLE) or - // multi-threaded without atomics (ESPHOME_THREAD_MULTI_NO_ATOMICS): direct write - // For ESPHOME_THREAD_MULTI_NO_ATOMICS, caller MUST hold lock! - item->remove = true; -#endif + this->set_item_removed_(item.get(), true); count++; } } From d36ef050a90d09a87e22b2cc5866b52007e9c875 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 12:15:50 -0600 Subject: [PATCH 401/526] [template] Mark all component classes as final (#11733) --- .../alarm_control_panel/template_alarm_control_panel.h | 2 +- .../template/binary_sensor/template_binary_sensor.h | 2 +- esphome/components/template/button/template_button.h | 2 +- esphome/components/template/cover/template_cover.h | 2 +- esphome/components/template/datetime/template_date.h | 2 +- esphome/components/template/datetime/template_datetime.h | 2 +- esphome/components/template/datetime/template_time.h | 2 +- esphome/components/template/event/template_event.h | 2 +- esphome/components/template/fan/template_fan.h | 2 +- esphome/components/template/lock/template_lock.h | 2 +- esphome/components/template/number/template_number.h | 2 +- esphome/components/template/output/template_output.h | 4 ++-- esphome/components/template/select/template_select.h | 2 +- esphome/components/template/sensor/template_sensor.h | 2 +- esphome/components/template/switch/template_switch.h | 2 +- esphome/components/template/text/template_text.h | 2 +- .../components/template/text_sensor/template_text_sensor.h | 2 +- esphome/components/template/valve/template_valve.h | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h index 40a79004da..202dc7c13f 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h @@ -49,7 +49,7 @@ struct SensorInfo { uint8_t store_index; }; -class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, public Component { +class TemplateAlarmControlPanel final : public alarm_control_panel::AlarmControlPanel, public Component { public: TemplateAlarmControlPanel(); void dump_config() override; diff --git a/esphome/components/template/binary_sensor/template_binary_sensor.h b/esphome/components/template/binary_sensor/template_binary_sensor.h index bc591391b9..0af709b097 100644 --- a/esphome/components/template/binary_sensor/template_binary_sensor.h +++ b/esphome/components/template/binary_sensor/template_binary_sensor.h @@ -7,7 +7,7 @@ namespace esphome { namespace template_ { -class TemplateBinarySensor : public Component, public binary_sensor::BinarySensor { +class TemplateBinarySensor final : public Component, public binary_sensor::BinarySensor { public: template void set_template(F &&f) { this->f_.set(std::forward(f)); } diff --git a/esphome/components/template/button/template_button.h b/esphome/components/template/button/template_button.h index 68e976f64b..5bda82c58f 100644 --- a/esphome/components/template/button/template_button.h +++ b/esphome/components/template/button/template_button.h @@ -5,7 +5,7 @@ namespace esphome { namespace template_ { -class TemplateButton : public button::Button { +class TemplateButton final : public button::Button { public: // Implements the abstract `press_action` but the `on_press` trigger already handles the press. void press_action() override{}; diff --git a/esphome/components/template/cover/template_cover.h b/esphome/components/template/cover/template_cover.h index faff69f867..125c67bb86 100644 --- a/esphome/components/template/cover/template_cover.h +++ b/esphome/components/template/cover/template_cover.h @@ -14,7 +14,7 @@ enum TemplateCoverRestoreMode { COVER_RESTORE_AND_CALL, }; -class TemplateCover : public cover::Cover, public Component { +class TemplateCover final : public cover::Cover, public Component { public: TemplateCover(); diff --git a/esphome/components/template/datetime/template_date.h b/esphome/components/template/datetime/template_date.h index 7fed704d0e..fe64b0ba14 100644 --- a/esphome/components/template/datetime/template_date.h +++ b/esphome/components/template/datetime/template_date.h @@ -14,7 +14,7 @@ namespace esphome { namespace template_ { -class TemplateDate : public datetime::DateEntity, public PollingComponent { +class TemplateDate final : public datetime::DateEntity, public PollingComponent { public: template void set_template(F &&f) { this->f_.set(std::forward(f)); } diff --git a/esphome/components/template/datetime/template_datetime.h b/esphome/components/template/datetime/template_datetime.h index ec45bf0326..c44bd85265 100644 --- a/esphome/components/template/datetime/template_datetime.h +++ b/esphome/components/template/datetime/template_datetime.h @@ -14,7 +14,7 @@ namespace esphome { namespace template_ { -class TemplateDateTime : public datetime::DateTimeEntity, public PollingComponent { +class TemplateDateTime final : public datetime::DateTimeEntity, public PollingComponent { public: template void set_template(F &&f) { this->f_.set(std::forward(f)); } diff --git a/esphome/components/template/datetime/template_time.h b/esphome/components/template/datetime/template_time.h index ea7474c0ba..0c95330d27 100644 --- a/esphome/components/template/datetime/template_time.h +++ b/esphome/components/template/datetime/template_time.h @@ -14,7 +14,7 @@ namespace esphome { namespace template_ { -class TemplateTime : public datetime::TimeEntity, public PollingComponent { +class TemplateTime final : public datetime::TimeEntity, public PollingComponent { public: template void set_template(F &&f) { this->f_.set(std::forward(f)); } diff --git a/esphome/components/template/event/template_event.h b/esphome/components/template/event/template_event.h index 251ae9299b..5467a64141 100644 --- a/esphome/components/template/event/template_event.h +++ b/esphome/components/template/event/template_event.h @@ -6,7 +6,7 @@ namespace esphome { namespace template_ { -class TemplateEvent : public Component, public event::Event {}; +class TemplateEvent final : public Component, public event::Event {}; } // namespace template_ } // namespace esphome diff --git a/esphome/components/template/fan/template_fan.h b/esphome/components/template/fan/template_fan.h index b09352f4d4..052b385b93 100644 --- a/esphome/components/template/fan/template_fan.h +++ b/esphome/components/template/fan/template_fan.h @@ -6,7 +6,7 @@ namespace esphome { namespace template_ { -class TemplateFan : public Component, public fan::Fan { +class TemplateFan final : public Component, public fan::Fan { public: TemplateFan() {} void setup() override; diff --git a/esphome/components/template/lock/template_lock.h b/esphome/components/template/lock/template_lock.h index 14fca4635e..ac10794e4d 100644 --- a/esphome/components/template/lock/template_lock.h +++ b/esphome/components/template/lock/template_lock.h @@ -8,7 +8,7 @@ namespace esphome { namespace template_ { -class TemplateLock : public lock::Lock, public Component { +class TemplateLock final : public lock::Lock, public Component { public: TemplateLock(); diff --git a/esphome/components/template/number/template_number.h b/esphome/components/template/number/template_number.h index a9307e9246..876ec96b3b 100644 --- a/esphome/components/template/number/template_number.h +++ b/esphome/components/template/number/template_number.h @@ -9,7 +9,7 @@ namespace esphome { namespace template_ { -class TemplateNumber : public number::Number, public PollingComponent { +class TemplateNumber final : public number::Number, public PollingComponent { public: template void set_template(F &&f) { this->f_.set(std::forward(f)); } diff --git a/esphome/components/template/output/template_output.h b/esphome/components/template/output/template_output.h index 90de801a5c..9ecfc446b9 100644 --- a/esphome/components/template/output/template_output.h +++ b/esphome/components/template/output/template_output.h @@ -7,7 +7,7 @@ namespace esphome { namespace template_ { -class TemplateBinaryOutput : public output::BinaryOutput { +class TemplateBinaryOutput final : public output::BinaryOutput { public: Trigger *get_trigger() const { return trigger_; } @@ -17,7 +17,7 @@ class TemplateBinaryOutput : public output::BinaryOutput { Trigger *trigger_ = new Trigger(); }; -class TemplateFloatOutput : public output::FloatOutput { +class TemplateFloatOutput final : public output::FloatOutput { public: Trigger *get_trigger() const { return trigger_; } diff --git a/esphome/components/template/select/template_select.h b/esphome/components/template/select/template_select.h index 2dad059ade..cb5b546976 100644 --- a/esphome/components/template/select/template_select.h +++ b/esphome/components/template/select/template_select.h @@ -9,7 +9,7 @@ namespace esphome { namespace template_ { -class TemplateSelect : public select::Select, public PollingComponent { +class TemplateSelect final : public select::Select, public PollingComponent { public: template void set_template(F &&f) { this->f_.set(std::forward(f)); } diff --git a/esphome/components/template/sensor/template_sensor.h b/esphome/components/template/sensor/template_sensor.h index 793d754a0f..3ca965dde3 100644 --- a/esphome/components/template/sensor/template_sensor.h +++ b/esphome/components/template/sensor/template_sensor.h @@ -7,7 +7,7 @@ namespace esphome { namespace template_ { -class TemplateSensor : public sensor::Sensor, public PollingComponent { +class TemplateSensor final : public sensor::Sensor, public PollingComponent { public: template void set_template(F &&f) { this->f_.set(std::forward(f)); } diff --git a/esphome/components/template/switch/template_switch.h b/esphome/components/template/switch/template_switch.h index 18a374df35..35c18af448 100644 --- a/esphome/components/template/switch/template_switch.h +++ b/esphome/components/template/switch/template_switch.h @@ -8,7 +8,7 @@ namespace esphome { namespace template_ { -class TemplateSwitch : public switch_::Switch, public Component { +class TemplateSwitch final : public switch_::Switch, public Component { public: TemplateSwitch(); diff --git a/esphome/components/template/text/template_text.h b/esphome/components/template/text/template_text.h index c12021f80e..1a0a66ed5b 100644 --- a/esphome/components/template/text/template_text.h +++ b/esphome/components/template/text/template_text.h @@ -60,7 +60,7 @@ template class TextSaver : public TemplateTextSaverBase { } }; -class TemplateText : public text::Text, public PollingComponent { +class TemplateText final : public text::Text, public PollingComponent { public: template void set_template(F &&f) { this->f_.set(std::forward(f)); } diff --git a/esphome/components/template/text_sensor/template_text_sensor.h b/esphome/components/template/text_sensor/template_text_sensor.h index 0d01c72023..da5c518c7f 100644 --- a/esphome/components/template/text_sensor/template_text_sensor.h +++ b/esphome/components/template/text_sensor/template_text_sensor.h @@ -8,7 +8,7 @@ namespace esphome { namespace template_ { -class TemplateTextSensor : public text_sensor::TextSensor, public PollingComponent { +class TemplateTextSensor final : public text_sensor::TextSensor, public PollingComponent { public: template void set_template(F &&f) { this->f_.set(std::forward(f)); } diff --git a/esphome/components/template/valve/template_valve.h b/esphome/components/template/valve/template_valve.h index d6235f8e5c..c452648193 100644 --- a/esphome/components/template/valve/template_valve.h +++ b/esphome/components/template/valve/template_valve.h @@ -14,7 +14,7 @@ enum TemplateValveRestoreMode { VALVE_RESTORE_AND_CALL, }; -class TemplateValve : public valve::Valve, public Component { +class TemplateValve final : public valve::Valve, public Component { public: TemplateValve(); From b08419fa473533e28a7cd86f98b2b3f98f1b7f27 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 12:30:45 -0600 Subject: [PATCH 402/526] [mqtt] Use StringRef to avoid string copies in discovery (#11731) --- .../mqtt/mqtt_alarm_control_panel.cpp | 2 +- .../components/mqtt/mqtt_binary_sensor.cpp | 9 ++++--- esphome/components/mqtt/mqtt_button.cpp | 7 ++--- esphome/components/mqtt/mqtt_component.cpp | 26 +++++++++++-------- esphome/components/mqtt/mqtt_component.h | 8 +++--- esphome/components/mqtt/mqtt_cover.cpp | 9 ++++--- esphome/components/mqtt/mqtt_event.cpp | 8 ++++-- esphome/components/mqtt/mqtt_fan.cpp | 14 +++++----- esphome/components/mqtt/mqtt_lock.cpp | 2 +- esphome/components/mqtt/mqtt_number.cpp | 14 +++++++--- esphome/components/mqtt/mqtt_sensor.cpp | 14 ++++++---- esphome/components/mqtt/mqtt_switch.cpp | 2 +- esphome/components/mqtt/mqtt_text_sensor.cpp | 8 +++--- esphome/components/mqtt/mqtt_update.cpp | 2 +- esphome/components/mqtt/mqtt_valve.cpp | 8 +++--- 15 files changed, 81 insertions(+), 52 deletions(-) diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp index 94460c31a7..dd3df5f8aa 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -36,7 +36,7 @@ void MQTTAlarmControlPanelComponent::setup() { } else if (strcasecmp(payload.c_str(), "TRIGGERED") == 0) { call.triggered(); } else { - ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name().c_str(), payload.c_str()); + ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name_().c_str(), payload.c_str()); } call.perform(); }); diff --git a/esphome/components/mqtt/mqtt_binary_sensor.cpp b/esphome/components/mqtt/mqtt_binary_sensor.cpp index 2ce4928574..479cee205a 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.cpp +++ b/esphome/components/mqtt/mqtt_binary_sensor.cpp @@ -30,9 +30,12 @@ MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor } void MQTTBinarySensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - if (!this->binary_sensor_->get_device_class().empty()) - root[MQTT_DEVICE_CLASS] = this->binary_sensor_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->binary_sensor_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) if (this->binary_sensor_->is_status_binary_sensor()) root[MQTT_PAYLOAD_ON] = mqtt::global_mqtt_client->get_availability().payload_available; if (this->binary_sensor_->is_status_binary_sensor()) diff --git a/esphome/components/mqtt/mqtt_button.cpp b/esphome/components/mqtt/mqtt_button.cpp index b3435edf38..f8eb0eab2d 100644 --- a/esphome/components/mqtt/mqtt_button.cpp +++ b/esphome/components/mqtt/mqtt_button.cpp @@ -20,7 +20,7 @@ void MQTTButtonComponent::setup() { if (payload == "PRESS") { this->button_->press(); } else { - ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str()); + ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str()); this->status_momentary_warning("state", 5000); } }); @@ -33,8 +33,9 @@ void MQTTButtonComponent::dump_config() { void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson config.state_topic = false; - if (!this->button_->get_device_class().empty()) { - root[MQTT_DEVICE_CLASS] = this->button_->get_device_class(); + const auto device_class = this->button_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; } // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index eb6114008a..1cd818964e 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -64,11 +64,11 @@ bool MQTTComponent::send_discovery_() { const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info(); if (discovery_info.clean) { - ESP_LOGV(TAG, "'%s': Cleaning discovery", this->friendly_name().c_str()); + ESP_LOGV(TAG, "'%s': Cleaning discovery", this->friendly_name_().c_str()); return global_mqtt_client->publish(this->get_discovery_topic_(discovery_info), "", 0, this->qos_, true); } - ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str()); + ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name_().c_str()); // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return global_mqtt_client->publish_json( @@ -85,12 +85,16 @@ bool MQTTComponent::send_discovery_() { } // Fields from EntityBase - root[MQTT_NAME] = this->get_entity()->has_own_name() ? this->friendly_name() : ""; + root[MQTT_NAME] = this->get_entity()->has_own_name() ? this->friendly_name_() : ""; - if (this->is_disabled_by_default()) + if (this->is_disabled_by_default_()) root[MQTT_ENABLED_BY_DEFAULT] = false; - if (!this->get_icon().empty()) - root[MQTT_ICON] = this->get_icon(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto icon_ref = this->get_icon_ref_(); + if (!icon_ref.empty()) { + root[MQTT_ICON] = icon_ref; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) const auto entity_category = this->get_entity()->get_entity_category(); switch (entity_category) { @@ -122,7 +126,7 @@ bool MQTTComponent::send_discovery_() { const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info(); if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) { char friendly_name_hash[9]; - sprintf(friendly_name_hash, "%08" PRIx32, fnv1_hash(this->friendly_name())); + sprintf(friendly_name_hash, "%08" PRIx32, fnv1_hash(this->friendly_name_())); friendly_name_hash[8] = 0; // ensure the hash-string ends with null root[MQTT_UNIQUE_ID] = get_mac_address() + "-" + this->component_type() + "-" + friendly_name_hash; } else { @@ -184,7 +188,7 @@ bool MQTTComponent::is_discovery_enabled() const { } std::string MQTTComponent::get_default_object_id_() const { - return str_sanitize(str_snake_case(this->friendly_name())); + return str_sanitize(str_snake_case(this->friendly_name_())); } void MQTTComponent::subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos) { @@ -268,9 +272,9 @@ void MQTTComponent::schedule_resend_state() { this->resend_state_ = true; } bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connected(); } // Pull these properties from EntityBase if not overridden -std::string MQTTComponent::friendly_name() const { return this->get_entity()->get_name(); } -std::string MQTTComponent::get_icon() const { return this->get_entity()->get_icon(); } -bool MQTTComponent::is_disabled_by_default() const { return this->get_entity()->is_disabled_by_default(); } +std::string MQTTComponent::friendly_name_() const { return this->get_entity()->get_name(); } +StringRef MQTTComponent::get_icon_ref_() const { return this->get_entity()->get_icon_ref(); } +bool MQTTComponent::is_disabled_by_default_() const { return this->get_entity()->is_disabled_by_default(); } bool MQTTComponent::is_internal() { if (this->has_custom_state_topic_) { // If the custom state_topic is null, return true as it is internal and should not publish diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 851fdd842c..2f8dfcf64e 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -165,13 +165,13 @@ class MQTTComponent : public Component { virtual const EntityBase *get_entity() const = 0; /// Get the friendly name of this MQTT component. - virtual std::string friendly_name() const; + std::string friendly_name_() const; - /// Get the icon field of this component - virtual std::string get_icon() const; + /// Get the icon field of this component as StringRef + StringRef get_icon_ref_() const; /// Get whether the underlying Entity is disabled by default - virtual bool is_disabled_by_default() const; + bool is_disabled_by_default_() const; /// Get the MQTT topic that new states will be shared to. std::string get_state_topic_() const; diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 6fb61ee469..b63aa66d29 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -67,9 +67,12 @@ void MQTTCoverComponent::dump_config() { } } void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - if (!this->cover_->get_device_class().empty()) - root[MQTT_DEVICE_CLASS] = this->cover_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->cover_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) auto traits = this->cover_->get_traits(); if (traits.get_is_assumed_state()) { diff --git a/esphome/components/mqtt/mqtt_event.cpp b/esphome/components/mqtt/mqtt_event.cpp index f972d545c6..e206335446 100644 --- a/esphome/components/mqtt/mqtt_event.cpp +++ b/esphome/components/mqtt/mqtt_event.cpp @@ -21,8 +21,12 @@ void MQTTEventComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConf for (const auto &event_type : this->event_->get_event_types()) event_types.add(event_type); - if (!this->event_->get_device_class().empty()) - root[MQTT_DEVICE_CLASS] = this->event_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->event_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) config.command_topic = false; } diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 70e1ae3b4a..2aefc3a4db 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -24,15 +24,15 @@ void MQTTFanComponent::setup() { auto val = parse_on_off(payload.c_str()); switch (val) { case PARSE_ON: - ESP_LOGD(TAG, "'%s' Turning Fan ON.", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s' Turning Fan ON.", this->friendly_name_().c_str()); this->state_->turn_on().perform(); break; case PARSE_OFF: - ESP_LOGD(TAG, "'%s' Turning Fan OFF.", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s' Turning Fan OFF.", this->friendly_name_().c_str()); this->state_->turn_off().perform(); break; case PARSE_TOGGLE: - ESP_LOGD(TAG, "'%s' Toggling Fan.", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s' Toggling Fan.", this->friendly_name_().c_str()); this->state_->toggle().perform(); break; case PARSE_NONE: @@ -48,11 +48,11 @@ void MQTTFanComponent::setup() { auto val = parse_on_off(payload.c_str(), "forward", "reverse"); switch (val) { case PARSE_ON: - ESP_LOGD(TAG, "'%s': Setting direction FORWARD", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s': Setting direction FORWARD", this->friendly_name_().c_str()); this->state_->make_call().set_direction(fan::FanDirection::FORWARD).perform(); break; case PARSE_OFF: - ESP_LOGD(TAG, "'%s': Setting direction REVERSE", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s': Setting direction REVERSE", this->friendly_name_().c_str()); this->state_->make_call().set_direction(fan::FanDirection::REVERSE).perform(); break; case PARSE_TOGGLE: @@ -75,11 +75,11 @@ void MQTTFanComponent::setup() { auto val = parse_on_off(payload.c_str(), "oscillate_on", "oscillate_off"); switch (val) { case PARSE_ON: - ESP_LOGD(TAG, "'%s': Setting oscillating ON", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s': Setting oscillating ON", this->friendly_name_().c_str()); this->state_->make_call().set_oscillating(true).perform(); break; case PARSE_OFF: - ESP_LOGD(TAG, "'%s': Setting oscillating OFF", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s': Setting oscillating OFF", this->friendly_name_().c_str()); this->state_->make_call().set_oscillating(false).perform(); break; case PARSE_TOGGLE: diff --git a/esphome/components/mqtt/mqtt_lock.cpp b/esphome/components/mqtt/mqtt_lock.cpp index 0412624983..0e15377ba4 100644 --- a/esphome/components/mqtt/mqtt_lock.cpp +++ b/esphome/components/mqtt/mqtt_lock.cpp @@ -24,7 +24,7 @@ void MQTTLockComponent::setup() { } else if (strcasecmp(payload.c_str(), "OPEN") == 0) { this->lock_->open(); } else { - ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str()); + ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str()); this->status_momentary_warning("state", 5000); } }); diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index a44632ff30..f419eac130 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -44,8 +44,11 @@ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon root[MQTT_MIN] = traits.get_min_value(); root[MQTT_MAX] = traits.get_max_value(); root[MQTT_STEP] = traits.get_step(); - if (!this->number_->traits.get_unit_of_measurement().empty()) - root[MQTT_UNIT_OF_MEASUREMENT] = this->number_->traits.get_unit_of_measurement(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto unit_of_measurement = this->number_->traits.get_unit_of_measurement_ref(); + if (!unit_of_measurement.empty()) { + root[MQTT_UNIT_OF_MEASUREMENT] = unit_of_measurement; + } switch (this->number_->traits.get_mode()) { case NUMBER_MODE_AUTO: break; @@ -56,8 +59,11 @@ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon root[MQTT_MODE] = "slider"; break; } - if (!this->number_->traits.get_device_class().empty()) - root[MQTT_DEVICE_CLASS] = this->number_->traits.get_device_class(); + const auto device_class = this->number_->traits.get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) config.command_topic = true; } diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index 9e61f6ef3b..010ac3013e 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -44,13 +44,17 @@ void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; } void MQTTSensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - if (!this->sensor_->get_device_class().empty()) { - root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->sensor_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; } - if (!this->sensor_->get_unit_of_measurement().empty()) - root[MQTT_UNIT_OF_MEASUREMENT] = this->sensor_->get_unit_of_measurement(); + const auto unit_of_measurement = this->sensor_->get_unit_of_measurement_ref(); + if (!unit_of_measurement.empty()) { + root[MQTT_UNIT_OF_MEASUREMENT] = unit_of_measurement; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) if (this->get_expire_after() > 0) root[MQTT_EXPIRE_AFTER] = this->get_expire_after() / 1000; diff --git a/esphome/components/mqtt/mqtt_switch.cpp b/esphome/components/mqtt/mqtt_switch.cpp index 8b1323bdb2..b3a35420b9 100644 --- a/esphome/components/mqtt/mqtt_switch.cpp +++ b/esphome/components/mqtt/mqtt_switch.cpp @@ -29,7 +29,7 @@ void MQTTSwitchComponent::setup() { break; case PARSE_NONE: default: - ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str()); + ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str()); this->status_momentary_warning("state", 5000); break; } diff --git a/esphome/components/mqtt/mqtt_text_sensor.cpp b/esphome/components/mqtt/mqtt_text_sensor.cpp index 42260ed2a8..e6e7cf04e8 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.cpp +++ b/esphome/components/mqtt/mqtt_text_sensor.cpp @@ -15,10 +15,12 @@ using namespace esphome::text_sensor; MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : sensor_(sensor) {} void MQTTTextSensor::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - if (!this->sensor_->get_device_class().empty()) { - root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->sensor_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) config.command_topic = false; } void MQTTTextSensor::setup() { diff --git a/esphome/components/mqtt/mqtt_update.cpp b/esphome/components/mqtt/mqtt_update.cpp index 5d4807c7f3..20f3a69a9e 100644 --- a/esphome/components/mqtt/mqtt_update.cpp +++ b/esphome/components/mqtt/mqtt_update.cpp @@ -20,7 +20,7 @@ void MQTTUpdateComponent::setup() { if (payload == "INSTALL") { this->update_->perform(); } else { - ESP_LOGW(TAG, "'%s': Received unknown update payload: %s", this->friendly_name().c_str(), payload.c_str()); + ESP_LOGW(TAG, "'%s': Received unknown update payload: %s", this->friendly_name_().c_str(), payload.c_str()); this->status_momentary_warning("state", 5000); } }); diff --git a/esphome/components/mqtt/mqtt_valve.cpp b/esphome/components/mqtt/mqtt_valve.cpp index 551398cf42..ae60670748 100644 --- a/esphome/components/mqtt/mqtt_valve.cpp +++ b/esphome/components/mqtt/mqtt_valve.cpp @@ -49,10 +49,12 @@ void MQTTValveComponent::dump_config() { } } void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - if (!this->valve_->get_device_class().empty()) { - root[MQTT_DEVICE_CLASS] = this->valve_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->valve_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) auto traits = this->valve_->get_traits(); if (traits.get_is_assumed_state()) { From be006ecaddad5df7c2be5cfa3ffa2499dda5c203 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 12:31:19 -0600 Subject: [PATCH 403/526] [mdns] Eliminate redundant hostname copy to save heap memory (#11734) --- esphome/components/mdns/mdns_component.cpp | 4 +--- esphome/components/mdns/mdns_component.h | 1 - esphome/components/mdns/mdns_esp32.cpp | 6 ++++-- esphome/components/mdns/mdns_esp8266.cpp | 3 ++- esphome/components/mdns/mdns_libretiny.cpp | 3 ++- esphome/components/mdns/mdns_rp2040.cpp | 3 ++- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index d476136554..2c3150ff5d 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -37,8 +37,6 @@ MDNS_STATIC_CONST_CHAR(SERVICE_TCP, "_tcp"); MDNS_STATIC_CONST_CHAR(VALUE_VERSION, ESPHOME_VERSION); void MDNSComponent::compile_records_(StaticVector &services) { - this->hostname_ = App.get_name(); - // IMPORTANT: The #ifdef blocks below must match COMPONENTS_WITH_MDNS_SERVICES // in mdns/__init__.py. If you add a new service here, update both locations. @@ -179,7 +177,7 @@ void MDNSComponent::dump_config() { ESP_LOGCONFIG(TAG, "mDNS:\n" " Hostname: %s", - this->hostname_.c_str()); + App.get_name().c_str()); #ifdef USE_MDNS_STORE_SERVICES ESP_LOGV(TAG, " Services:"); for (const auto &service : this->services_) { diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index 35371fd739..f4237d5a69 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -76,7 +76,6 @@ class MDNSComponent : public Component { #ifdef USE_MDNS_STORE_SERVICES StaticVector services_{}; #endif - std::string hostname_; void compile_records_(StaticVector &services); }; diff --git a/esphome/components/mdns/mdns_esp32.cpp b/esphome/components/mdns/mdns_esp32.cpp index c02bfcbadb..ecdc926cc9 100644 --- a/esphome/components/mdns/mdns_esp32.cpp +++ b/esphome/components/mdns/mdns_esp32.cpp @@ -2,6 +2,7 @@ #if defined(USE_ESP32) && defined(USE_MDNS) #include +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" #include "mdns_component.h" @@ -27,8 +28,9 @@ void MDNSComponent::setup() { return; } - mdns_hostname_set(this->hostname_.c_str()); - mdns_instance_name_set(this->hostname_.c_str()); + const char *hostname = App.get_name().c_str(); + mdns_hostname_set(hostname); + mdns_instance_name_set(hostname); for (const auto &service : services) { auto txt_records = std::make_unique(service.txt_records.size()); diff --git a/esphome/components/mdns/mdns_esp8266.cpp b/esphome/components/mdns/mdns_esp8266.cpp index 25a3defa7b..9bbb406070 100644 --- a/esphome/components/mdns/mdns_esp8266.cpp +++ b/esphome/components/mdns/mdns_esp8266.cpp @@ -4,6 +4,7 @@ #include #include "esphome/components/network/ip_address.h" #include "esphome/components/network/util.h" +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" #include "mdns_component.h" @@ -20,7 +21,7 @@ void MDNSComponent::setup() { this->compile_records_(services); #endif - MDNS.begin(this->hostname_.c_str()); + MDNS.begin(App.get_name().c_str()); for (const auto &service : services) { // Strip the leading underscore from the proto and service_type. While it is diff --git a/esphome/components/mdns/mdns_libretiny.cpp b/esphome/components/mdns/mdns_libretiny.cpp index a3e317a2bf..fb2088f719 100644 --- a/esphome/components/mdns/mdns_libretiny.cpp +++ b/esphome/components/mdns/mdns_libretiny.cpp @@ -3,6 +3,7 @@ #include "esphome/components/network/ip_address.h" #include "esphome/components/network/util.h" +#include "esphome/core/application.h" #include "esphome/core/log.h" #include "mdns_component.h" @@ -20,7 +21,7 @@ void MDNSComponent::setup() { this->compile_records_(services); #endif - MDNS.begin(this->hostname_.c_str()); + MDNS.begin(App.get_name().c_str()); for (const auto &service : services) { // Strip the leading underscore from the proto and service_type. While it is diff --git a/esphome/components/mdns/mdns_rp2040.cpp b/esphome/components/mdns/mdns_rp2040.cpp index 791fa3934d..a9f5349f14 100644 --- a/esphome/components/mdns/mdns_rp2040.cpp +++ b/esphome/components/mdns/mdns_rp2040.cpp @@ -3,6 +3,7 @@ #include "esphome/components/network/ip_address.h" #include "esphome/components/network/util.h" +#include "esphome/core/application.h" #include "esphome/core/log.h" #include "mdns_component.h" @@ -20,7 +21,7 @@ void MDNSComponent::setup() { this->compile_records_(services); #endif - MDNS.begin(this->hostname_.c_str()); + MDNS.begin(App.get_name().c_str()); for (const auto &service : services) { // Strip the leading underscore from the proto and service_type. While it is From 00c0854323a79663a27243f05db911be9ad956e1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 12:50:35 -0600 Subject: [PATCH 404/526] [core] Deprecate get_icon(), get_device_class(), get_unit_of_measurement() and fix remaining non-MQTT usages (#11732) --- esphome/components/graph/graph.cpp | 4 ++-- esphome/components/prometheus/prometheus_handler.cpp | 2 +- esphome/components/sprinkler/sprinkler.cpp | 4 ++-- esphome/core/entity_base.h | 12 +++++++++--- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/esphome/components/graph/graph.cpp b/esphome/components/graph/graph.cpp index ac6ace96ee..88bb306408 100644 --- a/esphome/components/graph/graph.cpp +++ b/esphome/components/graph/graph.cpp @@ -235,7 +235,7 @@ void GraphLegend::init(Graph *g) { std::string valstr = value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals()); if (this->units_) { - valstr += trace->sensor_->get_unit_of_measurement(); + valstr += trace->sensor_->get_unit_of_measurement_ref(); } this->font_value_->measure(valstr.c_str(), &fw, &fos, &fbl, &fh); if (fw > valw) @@ -371,7 +371,7 @@ void Graph::draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_of std::string valstr = value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals()); if (legend_->units_) { - valstr += trace->sensor_->get_unit_of_measurement(); + valstr += trace->sensor_->get_unit_of_measurement_ref(); } buff->printf(xv, yv, legend_->font_value_, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", valstr.c_str()); ESP_LOGV(TAG, " value: %s", valstr.c_str()); diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 6e7ed6f79f..5cfcacf0cb 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -158,7 +158,7 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor stream->print(ESPHOME_F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(ESPHOME_F("\",unit=\"")); - stream->print(obj->get_unit_of_measurement().c_str()); + stream->print(obj->get_unit_of_measurement_ref().c_str()); stream->print(ESPHOME_F("\"} ")); stream->print(value_accuracy_to_string(obj->state, obj->get_accuracy_decimals()).c_str()); stream->print(ESPHOME_F("\n")); diff --git a/esphome/components/sprinkler/sprinkler.cpp b/esphome/components/sprinkler/sprinkler.cpp index 7676e17468..8edb240a41 100644 --- a/esphome/components/sprinkler/sprinkler.cpp +++ b/esphome/components/sprinkler/sprinkler.cpp @@ -650,7 +650,7 @@ void Sprinkler::set_valve_run_duration(const optional valve_number, cons return; } auto call = this->valve_[valve_number.value()].run_duration_number->make_call(); - if (this->valve_[valve_number.value()].run_duration_number->traits.get_unit_of_measurement() == MIN_STR) { + if (this->valve_[valve_number.value()].run_duration_number->traits.get_unit_of_measurement_ref() == MIN_STR) { call.set_value(run_duration.value() / 60.0); } else { call.set_value(run_duration.value()); @@ -732,7 +732,7 @@ uint32_t Sprinkler::valve_run_duration(const size_t valve_number) { return 0; } if (this->valve_[valve_number].run_duration_number != nullptr) { - if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement() == MIN_STR) { + if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement_ref() == MIN_STR) { return static_cast(roundf(this->valve_[valve_number].run_duration_number->state * 60)); } else { return static_cast(roundf(this->valve_[valve_number].run_duration_number->state)); diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index 80cd6b8e77..6e5362464f 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -61,7 +61,9 @@ class EntityBase { } // Get/set this entity's icon - std::string get_icon() const; + [[deprecated("Use get_icon_ref() instead for better performance (avoids string copy). Will stop working in ESPHome " + "2026.5.0")]] std::string + get_icon() const; void set_icon(const char *icon); StringRef get_icon_ref() const { static constexpr auto EMPTY_STRING = StringRef::from_lit(""); @@ -158,7 +160,9 @@ class EntityBase { class EntityBase_DeviceClass { // NOLINT(readability-identifier-naming) public: /// Get the device class, using the manual override if set. - std::string get_device_class(); + [[deprecated("Use get_device_class_ref() instead for better performance (avoids string copy). Will stop working in " + "ESPHome 2026.5.0")]] std::string + get_device_class(); /// Manually set the device class. void set_device_class(const char *device_class); /// Get the device class as StringRef @@ -174,7 +178,9 @@ class EntityBase_DeviceClass { // NOLINT(readability-identifier-naming) class EntityBase_UnitOfMeasurement { // NOLINT(readability-identifier-naming) public: /// Get the unit of measurement, using the manual override if set. - std::string get_unit_of_measurement(); + [[deprecated("Use get_unit_of_measurement_ref() instead for better performance (avoids string copy). Will stop " + "working in ESPHome 2026.5.0")]] std::string + get_unit_of_measurement(); /// Manually set the unit of measurement. void set_unit_of_measurement(const char *unit_of_measurement); /// Get the unit of measurement as StringRef From aa5795c019c055e08807f9943633d3a8582582e4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 13:17:34 -0600 Subject: [PATCH 405/526] [tests] Fix ID collision between bl0940 and nau7802 component tests (#11739) --- tests/components/bl0940/common.yaml | 6 +++--- tests/components/nau7802/common.yaml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/components/bl0940/common.yaml b/tests/components/bl0940/common.yaml index 0b73fd6d55..e476ba10c0 100644 --- a/tests/components/bl0940/common.yaml +++ b/tests/components/bl0940/common.yaml @@ -1,11 +1,11 @@ button: - platform: bl0940 - bl0940_id: test_id + bl0940_id: bl0940_test_id name: Cal Reset sensor: - platform: bl0940 - id: test_id + id: bl0940_test_id voltage: name: BL0940 Voltage current: @@ -22,7 +22,7 @@ sensor: number: - platform: bl0940 id: bl0940_number_id - bl0940_id: test_id + bl0940_id: bl0940_test_id current_calibration: name: Cal Current min_value: -5 diff --git a/tests/components/nau7802/common.yaml b/tests/components/nau7802/common.yaml index 5c52c33dad..5251910df9 100644 --- a/tests/components/nau7802/common.yaml +++ b/tests/components/nau7802/common.yaml @@ -1,13 +1,13 @@ sensor: - platform: nau7802 i2c_id: i2c_bus - id: test_id + id: nau7802_test_id name: weight gain: 32 ldo_voltage: "3.0v" samples_per_second: 10 on_value: then: - - nau7802.calibrate_external_offset: test_id - - nau7802.calibrate_internal_offset: test_id - - nau7802.calibrate_gain: test_id + - nau7802.calibrate_external_offset: nau7802_test_id + - nau7802.calibrate_internal_offset: nau7802_test_id + - nau7802.calibrate_gain: nau7802_test_id From ce5e6088638d6b5cdc2becc34196205d3e03b2dc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 14:32:45 -0600 Subject: [PATCH 406/526] [ci] Skip memory impact analysis for release and beta branches (#11740) --- script/determine-jobs.py | 15 +++++++ script/helpers.py | 45 ++++++++++++++++--- tests/script/test_determine_jobs.py | 70 +++++++++++++++++++++++++++++ tests/script/test_helpers.py | 7 +++ 4 files changed, 130 insertions(+), 7 deletions(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 6f908b7150..e9d17d8fe5 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -63,6 +63,7 @@ from helpers import ( get_components_from_integration_fixtures, get_components_with_dependencies, get_cpp_changed_components, + get_target_branch, git_ls_files, parse_test_filename, root_path, @@ -471,6 +472,20 @@ def detect_memory_impact_config( - platform: platform name for the merged build - use_merged_config: "true" (always use merged config) """ + # Skip memory impact analysis for release* or beta* branches + # These branches typically contain many merged changes from dev, and building + # all components at once would produce nonsensical memory impact results. + # Memory impact analysis is most useful for focused PRs targeting dev. + target_branch = get_target_branch() + if target_branch and ( + target_branch.startswith("release") or target_branch.startswith("beta") + ): + print( + f"Memory impact: Skipping analysis for target branch {target_branch} " + f"(would try to build all components at once, giving nonsensical results)", + file=sys.stderr, + ) + return {"should_run": "false"} # Get actually changed files (not dependencies) files = changed_files(branch) diff --git a/script/helpers.py b/script/helpers.py index 5b2fe6cd06..1039ef39ac 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -196,6 +196,20 @@ def splitlines_no_ends(string: str) -> list[str]: return [s.strip() for s in string.splitlines()] +@cache +def _get_github_event_data() -> dict | None: + """Read and parse GitHub event file (cached). + + Returns: + Parsed event data dictionary, or None if not available + """ + github_event_path = os.environ.get("GITHUB_EVENT_PATH") + if github_event_path and os.path.exists(github_event_path): + with open(github_event_path) as f: + return json.load(f) + return None + + def _get_pr_number_from_github_env() -> str | None: """Extract PR number from GitHub environment variables. @@ -208,13 +222,30 @@ def _get_pr_number_from_github_env() -> str | None: return github_ref.split("/pull/")[1].split("/")[0] # Fallback to GitHub event file - github_event_path = os.environ.get("GITHUB_EVENT_PATH") - if github_event_path and os.path.exists(github_event_path): - with open(github_event_path) as f: - event_data = json.load(f) - pr_data = event_data.get("pull_request", {}) - if pr_number := pr_data.get("number"): - return str(pr_number) + if event_data := _get_github_event_data(): + pr_data = event_data.get("pull_request", {}) + if pr_number := pr_data.get("number"): + return str(pr_number) + + return None + + +def get_target_branch() -> str | None: + """Get the target branch from GitHub environment variables. + + Returns: + Target branch name (e.g., "dev", "release", "beta"), or None if not in PR context + """ + # First try GITHUB_BASE_REF (set for pull_request events) + if base_ref := os.environ.get("GITHUB_BASE_REF"): + return base_ref + + # Fallback to GitHub event file + if event_data := _get_github_event_data(): + pr_data = event_data.get("pull_request", {}) + base_data = pr_data.get("base", {}) + if ref := base_data.get("ref"): + return ref return None diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index e084e2e398..9f12d7ffcf 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -1240,3 +1240,73 @@ def test_detect_memory_impact_config_filters_incompatible_esp8266_on_esp32( ) assert result["use_merged_config"] == "true" + + +def test_detect_memory_impact_config_skips_release_branch(tmp_path: Path) -> None: + """Test that memory impact analysis is skipped for release* branches.""" + # Create test directory structure with components that have tests + tests_dir = tmp_path / "tests" / "components" + wifi_dir = tests_dir / "wifi" + wifi_dir.mkdir(parents=True) + (wifi_dir / "test.esp32-idf.yaml").write_text("test: wifi") + + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + patch.object(determine_jobs, "get_target_branch", return_value="release"), + ): + mock_changed_files.return_value = ["esphome/components/wifi/wifi.cpp"] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + # Memory impact should be skipped for release branch + assert result["should_run"] == "false" + + +def test_detect_memory_impact_config_skips_beta_branch(tmp_path: Path) -> None: + """Test that memory impact analysis is skipped for beta* branches.""" + # Create test directory structure with components that have tests + tests_dir = tmp_path / "tests" / "components" + wifi_dir = tests_dir / "wifi" + wifi_dir.mkdir(parents=True) + (wifi_dir / "test.esp32-idf.yaml").write_text("test: wifi") + + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + patch.object(determine_jobs, "get_target_branch", return_value="beta"), + ): + mock_changed_files.return_value = ["esphome/components/wifi/wifi.cpp"] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + # Memory impact should be skipped for beta branch + assert result["should_run"] == "false" + + +def test_detect_memory_impact_config_runs_for_dev_branch(tmp_path: Path) -> None: + """Test that memory impact analysis runs for dev branch.""" + # Create test directory structure with components that have tests + tests_dir = tmp_path / "tests" / "components" + wifi_dir = tests_dir / "wifi" + wifi_dir.mkdir(parents=True) + (wifi_dir / "test.esp32-idf.yaml").write_text("test: wifi") + + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + patch.object(determine_jobs, "get_target_branch", return_value="dev"), + ): + mock_changed_files.return_value = ["esphome/components/wifi/wifi.cpp"] + determine_jobs._component_has_tests.cache_clear() + + result = determine_jobs.detect_memory_impact_config() + + # Memory impact should run for dev branch + assert result["should_run"] == "true" + assert result["components"] == ["wifi"] diff --git a/tests/script/test_helpers.py b/tests/script/test_helpers.py index 1bfffef51c..c51273f298 100644 --- a/tests/script/test_helpers.py +++ b/tests/script/test_helpers.py @@ -31,6 +31,13 @@ print_file_list = helpers.print_file_list get_all_dependencies = helpers.get_all_dependencies +@pytest.fixture(autouse=True) +def clear_helpers_cache() -> None: + """Clear cached functions before each test.""" + helpers._get_github_event_data.cache_clear() + helpers._get_changed_files_github_actions.cache_clear() + + @pytest.mark.parametrize( ("github_ref", "expected_pr_number"), [ From 20b6e0d5c239d949a89f61a528733772f5c3166b Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:37:38 +1000 Subject: [PATCH 407/526] [lvgl] Allow text substitution for NaN (#11712) --- esphome/components/lvgl/helpers.py | 39 ++++++++++++++++++++---- esphome/components/lvgl/lv_validation.py | 16 ++++++++-- esphome/components/lvgl/schemas.py | 3 +- tests/components/lvgl/lvgl-package.yaml | 6 ++++ 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/esphome/components/lvgl/helpers.py b/esphome/components/lvgl/helpers.py index 8d5b6354bb..c2bd58f71c 100644 --- a/esphome/components/lvgl/helpers.py +++ b/esphome/components/lvgl/helpers.py @@ -3,6 +3,8 @@ import re from esphome import config_validation as cv from esphome.const import CONF_ARGS, CONF_FORMAT +CONF_IF_NAN = "if_nan" + lv_uses = { "USER_DATA", "LOG", @@ -21,23 +23,48 @@ lv_fonts_used = set() esphome_fonts_used = set() lvgl_components_required = set() - -def validate_printf(value): - cfmt = r""" +# noqa +f_regex = re.compile( + r""" ( # start of capture group 1 % # literal "%" - (?:[-+0 #]{0,5}) # optional flags + [-+0 #]{0,5} # optional flags + (?:\d+|\*)? # width + (?:\.(?:\d+|\*))? # precision + (?:h|l|ll|w|I|I32|I64)? # size + f # type + ) + """, + flags=re.VERBOSE, +) +# noqa +c_regex = re.compile( + r""" + ( # start of capture group 1 + % # literal "%" + [-+0 #]{0,5} # optional flags (?:\d+|\*)? # width (?:\.(?:\d+|\*))? # precision (?:h|l|ll|w|I|I32|I64)? # size [cCdiouxXeEfgGaAnpsSZ] # type ) - """ # noqa - matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.VERBOSE) + """, + flags=re.VERBOSE, +) + + +def validate_printf(value): + format_string = value[CONF_FORMAT] + matches = c_regex.findall(format_string) if len(matches) != len(value[CONF_ARGS]): raise cv.Invalid( f"Found {len(matches)} printf-patterns ({', '.join(matches)}), but {len(value[CONF_ARGS])} args were given!" ) + + if value.get(CONF_IF_NAN) and len(f_regex.findall(format_string)) != 1: + raise cv.Invalid( + "Use of 'if_nan' requires a single valid printf-pattern of type %f" + ) return value diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index 9fe72128ce..045258555c 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -33,7 +33,13 @@ from .defines import ( call_lambda, literal, ) -from .helpers import add_lv_use, esphome_fonts_used, lv_fonts_used, requires_component +from .helpers import ( + CONF_IF_NAN, + add_lv_use, + esphome_fonts_used, + lv_fonts_used, + requires_component, +) from .types import lv_font_t, lv_gradient_t opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER") @@ -412,7 +418,13 @@ class TextValidator(LValidator): str_args = [str(x) for x in value[CONF_ARGS]] arg_expr = cg.RawExpression(",".join(str_args)) format_str = cpp_string_escape(format_str) - return literal(f"str_sprintf({format_str}, {arg_expr}).c_str()") + sprintf_str = f"str_sprintf({format_str}, {arg_expr}).c_str()" + if nanval := value.get(CONF_IF_NAN): + nanval = cpp_string_escape(nanval) + return literal( + f"(std::isfinite({arg_expr}) ? {sprintf_str} : {nanval})" + ) + return literal(sprintf_str) if time_format := value.get(CONF_TIME_FORMAT): source = value[CONF_TIME] if isinstance(source, Lambda): diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index dd248d0b94..0dcf420f24 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -20,7 +20,7 @@ from esphome.core.config import StartupTrigger from . import defines as df, lv_validation as lvalid from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR -from .helpers import requires_component, validate_printf +from .helpers import CONF_IF_NAN, requires_component, validate_printf from .layout import ( FLEX_OBJ_SCHEMA, GRID_CELL_SCHEMA, @@ -54,6 +54,7 @@ PRINTF_TEXT_SCHEMA = cv.All( { cv.Required(CONF_FORMAT): cv.string, cv.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_), + cv.Optional(CONF_IF_NAN): cv.string, }, ), validate_printf, diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 8ac9a60e2d..b122d10f04 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -726,6 +726,12 @@ lvgl: - logger.log: format: "Spinbox value is %f" args: [x] + - lvgl.label.update: + id: hello_label + text: + format: "value is %.1f now" + args: [x] + if_nan: "Value unknown" - button: styles: spin_button id: spin_down From bdfd88441a3666ce6f89773bb9bbfbc384eee4af Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 19:31:23 -0600 Subject: [PATCH 408/526] [ci] Skip memory impact analysis when more than 40 components changed (#11741) --- script/determine-jobs.py | 12 +++++ tests/script/test_determine_jobs.py | 77 ++++++++++++++++++++++------- 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index e9d17d8fe5..39a7571fbe 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -94,6 +94,7 @@ class Platform(StrEnum): # Memory impact analysis constants MEMORY_IMPACT_FALLBACK_COMPONENT = "api" # Representative component for core changes MEMORY_IMPACT_FALLBACK_PLATFORM = Platform.ESP32_IDF # Most representative platform +MEMORY_IMPACT_MAX_COMPONENTS = 40 # Max components before results become nonsensical # Platform-specific components that can only be built on their respective platforms # These components contain platform-specific code and cannot be cross-compiled @@ -555,6 +556,17 @@ def detect_memory_impact_config( if not components_with_tests: return {"should_run": "false"} + # Skip memory impact analysis if too many components changed + # Building 40+ components at once produces nonsensical memory impact results + # This typically happens with large refactorings or batch updates + if len(components_with_tests) > MEMORY_IMPACT_MAX_COMPONENTS: + print( + f"Memory impact: Skipping analysis for {len(components_with_tests)} components " + f"(limit is {MEMORY_IMPACT_MAX_COMPONENTS}, would give nonsensical results)", + file=sys.stderr, + ) + return {"should_run": "false"} + # Find common platforms supported by ALL components # This ensures we can build all components together in a merged config common_platforms = set(MEMORY_IMPACT_PLATFORM_PREFERENCE) diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 9f12d7ffcf..4894a5e28a 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -71,9 +71,10 @@ def mock_changed_files() -> Generator[Mock, None, None]: @pytest.fixture(autouse=True) -def clear_clang_tidy_cache() -> None: - """Clear the clang-tidy full scan cache before each test.""" +def clear_determine_jobs_caches() -> None: + """Clear all cached functions before each test.""" determine_jobs._is_clang_tidy_full_scan.cache_clear() + determine_jobs._component_has_tests.cache_clear() def test_main_all_tests_should_run( @@ -565,7 +566,6 @@ def test_main_filters_components_without_tests( patch.object(determine_jobs, "changed_files", return_value=[]), ): # Clear the cache since we're mocking root_path - determine_jobs._component_has_tests.cache_clear() determine_jobs.main() # Check output @@ -665,7 +665,6 @@ def test_main_detects_components_with_variant_tests( patch.object(determine_jobs, "changed_files", return_value=[]), ): # Clear the cache since we're mocking root_path - determine_jobs._component_has_tests.cache_clear() determine_jobs.main() # Check output @@ -714,7 +713,6 @@ def test_detect_memory_impact_config_with_common_platform(tmp_path: Path) -> Non "esphome/components/wifi/wifi.cpp", "esphome/components/api/api.cpp", ] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -744,7 +742,6 @@ def test_detect_memory_impact_config_core_only_changes(tmp_path: Path) -> None: "esphome/core/application.cpp", "esphome/core/component.h", ] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -775,7 +772,6 @@ def test_detect_memory_impact_config_core_python_only_changes(tmp_path: Path) -> "esphome/config.py", "esphome/core/config.py", ] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -808,7 +804,6 @@ def test_detect_memory_impact_config_no_common_platform(tmp_path: Path) -> None: "esphome/components/wifi/wifi.cpp", "esphome/components/logger/logger.cpp", ] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -830,7 +825,6 @@ def test_detect_memory_impact_config_no_changes(tmp_path: Path) -> None: patch.object(determine_jobs, "changed_files") as mock_changed_files, ): mock_changed_files.return_value = [] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -855,7 +849,6 @@ def test_detect_memory_impact_config_no_components_with_tests(tmp_path: Path) -> mock_changed_files.return_value = [ "esphome/components/my_custom_component/component.cpp", ] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -895,7 +888,6 @@ def test_detect_memory_impact_config_includes_base_bus_components( "esphome/components/uart/automation.h", # Header file with inline code "esphome/components/wifi/wifi.cpp", ] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -938,7 +930,6 @@ def test_detect_memory_impact_config_with_variant_tests(tmp_path: Path) -> None: "esphome/components/improv_serial/improv_serial.cpp", "esphome/components/ethernet/ethernet.cpp", ] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -1168,7 +1159,6 @@ def test_detect_memory_impact_config_filters_incompatible_esp32_on_esp8266( "tests/components/esp8266/test.esp8266-ard.yaml", "esphome/core/helpers_esp8266.h", # ESP8266-specific file to hint platform ] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -1222,7 +1212,6 @@ def test_detect_memory_impact_config_filters_incompatible_esp8266_on_esp32( "esphome/components/wifi/wifi_component_esp_idf.cpp", # ESP-IDF hint "esphome/components/ethernet/ethernet_esp32.cpp", # ESP32 hint ] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -1257,7 +1246,6 @@ def test_detect_memory_impact_config_skips_release_branch(tmp_path: Path) -> Non patch.object(determine_jobs, "get_target_branch", return_value="release"), ): mock_changed_files.return_value = ["esphome/components/wifi/wifi.cpp"] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -1280,7 +1268,6 @@ def test_detect_memory_impact_config_skips_beta_branch(tmp_path: Path) -> None: patch.object(determine_jobs, "get_target_branch", return_value="beta"), ): mock_changed_files.return_value = ["esphome/components/wifi/wifi.cpp"] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() @@ -1303,10 +1290,66 @@ def test_detect_memory_impact_config_runs_for_dev_branch(tmp_path: Path) -> None patch.object(determine_jobs, "get_target_branch", return_value="dev"), ): mock_changed_files.return_value = ["esphome/components/wifi/wifi.cpp"] - determine_jobs._component_has_tests.cache_clear() result = determine_jobs.detect_memory_impact_config() # Memory impact should run for dev branch assert result["should_run"] == "true" assert result["components"] == ["wifi"] + + +def test_detect_memory_impact_config_skips_too_many_components( + tmp_path: Path, +) -> None: + """Test that memory impact analysis is skipped when more than 40 components changed.""" + # Create test directory structure with 41 components + tests_dir = tmp_path / "tests" / "components" + component_names = [f"component_{i}" for i in range(41)] + + for component_name in component_names: + comp_dir = tests_dir / component_name + comp_dir.mkdir(parents=True) + (comp_dir / "test.esp32-idf.yaml").write_text(f"test: {component_name}") + + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + patch.object(determine_jobs, "get_target_branch", return_value="dev"), + ): + mock_changed_files.return_value = [ + f"esphome/components/{name}/{name}.cpp" for name in component_names + ] + + result = determine_jobs.detect_memory_impact_config() + + # Memory impact should be skipped for too many components (41 > 40) + assert result["should_run"] == "false" + + +def test_detect_memory_impact_config_runs_at_component_limit(tmp_path: Path) -> None: + """Test that memory impact analysis runs with exactly 40 components (at limit).""" + # Create test directory structure with exactly 40 components + tests_dir = tmp_path / "tests" / "components" + component_names = [f"component_{i}" for i in range(40)] + + for component_name in component_names: + comp_dir = tests_dir / component_name + comp_dir.mkdir(parents=True) + (comp_dir / "test.esp32-idf.yaml").write_text(f"test: {component_name}") + + with ( + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "changed_files") as mock_changed_files, + patch.object(determine_jobs, "get_target_branch", return_value="dev"), + ): + mock_changed_files.return_value = [ + f"esphome/components/{name}/{name}.cpp" for name in component_names + ] + + result = determine_jobs.detect_memory_impact_config() + + # Memory impact should run at exactly 40 components (at limit but not over) + assert result["should_run"] == "true" + assert len(result["components"]) == 40 From 5eea7bdb44fcd58b99f6738264122e9528ebcbb0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 19:45:48 -0600 Subject: [PATCH 409/526] Update AI instructions with C++ style guidelines from developers docs (#11743) --- .ai/instructions.md | 74 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/.ai/instructions.md b/.ai/instructions.md index 5f314a0dc9..9309c67c65 100644 --- a/.ai/instructions.md +++ b/.ai/instructions.md @@ -51,7 +51,79 @@ This document provides essential context for AI models interacting with this pro * **Naming Conventions:** * **Python:** Follows PEP 8. Use clear, descriptive names following snake_case. - * **C++:** Follows the Google C++ Style Guide. + * **C++:** Follows the Google C++ Style Guide with these specifics (following clang-tidy conventions): + - Function, method, and variable names: `lower_snake_case` + - Class/struct/enum names: `UpperCamelCase` + - Top-level constants (global/namespace scope): `UPPER_SNAKE_CASE` + - Function-local constants: `lower_snake_case` + - Protected/private fields: `lower_snake_case_with_trailing_underscore_` + - Favor descriptive names over abbreviations + +* **C++ Field Visibility:** + * **Prefer `protected`:** Use `protected` for most class fields to enable extensibility and testing. Fields should be `lower_snake_case_with_trailing_underscore_`. + * **Use `private` for safety-critical cases:** Use `private` visibility when direct field access could introduce bugs or violate invariants: + 1. **Pointer lifetime issues:** When setters validate and store pointers from known lists to prevent dangling references. + ```cpp + // Helper to find matching string in vector and return its pointer + inline const char *vector_find(const std::vector &vec, const char *value) { + for (const char *item : vec) { + if (strcmp(item, value) == 0) + return item; + } + return nullptr; + } + + class ClimateDevice { + public: + void set_custom_fan_modes(std::initializer_list modes) { + this->custom_fan_modes_ = modes; + this->active_custom_fan_mode_ = nullptr; // Reset when modes change + } + bool set_custom_fan_mode(const char *mode) { + // Find mode in supported list and store that pointer (not the input pointer) + const char *validated_mode = vector_find(this->custom_fan_modes_, mode); + if (validated_mode != nullptr) { + this->active_custom_fan_mode_ = validated_mode; + return true; + } + return false; + } + private: + std::vector custom_fan_modes_; // Pointers to string literals in flash + const char *active_custom_fan_mode_{nullptr}; // Must point to entry in custom_fan_modes_ + }; + ``` + 2. **Invariant coupling:** When multiple fields must remain synchronized to prevent buffer overflows or data corruption. + ```cpp + class Buffer { + public: + void resize(size_t new_size) { + auto new_data = std::make_unique(new_size); + if (this->data_) { + std::memcpy(new_data.get(), this->data_.get(), std::min(this->size_, new_size)); + } + this->data_ = std::move(new_data); + this->size_ = new_size; // Must stay in sync with data_ + } + private: + std::unique_ptr data_; + size_t size_{0}; // Must match allocated size of data_ + }; + ``` + 3. **Resource management:** When setters perform cleanup or registration operations that derived classes might skip. + * **Provide `protected` accessor methods:** When derived classes need controlled access to `private` members. + +* **C++ Preprocessor Directives:** + * **Avoid `#define` for constants:** Using `#define` for constants is discouraged and should be replaced with `const` variables or enums. + * **Use `#define` only for:** + - Conditional compilation (`#ifdef`, `#ifndef`) + - Compile-time sizes calculated during Python code generation (e.g., configuring `std::array` or `StaticVector` dimensions via `cg.add_define()`) + +* **C++ Additional Conventions:** + * **Member access:** Prefix all class member access with `this->` (e.g., `this->value_` not `value_`) + * **Indentation:** Use spaces (two per indentation level), not tabs + * **Type aliases:** Prefer `using type_t = int;` over `typedef int type_t;` + * **Line length:** Wrap lines at no more than 120 characters * **Component Structure:** * **Standard Files:** From 83f30a64ed54f9285b0d68ba22b8654cdc75c5be Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 20:31:59 -0600 Subject: [PATCH 410/526] [api] Store YAML service names in flash instead of heap (#11744) --- esphome/components/api/custom_api_device.h | 4 +- esphome/components/api/user_services.h | 60 +++++++++++++++++-- .../fixtures/api_custom_services.yaml | 22 +++++++ tests/integration/test_api_custom_services.py | 60 ++++++++++++++++++- 4 files changed, 136 insertions(+), 10 deletions(-) diff --git a/esphome/components/api/custom_api_device.h b/esphome/components/api/custom_api_device.h index d34ccfa0ce..43ea644f0c 100644 --- a/esphome/components/api/custom_api_device.h +++ b/esphome/components/api/custom_api_device.h @@ -9,11 +9,11 @@ namespace esphome::api { #ifdef USE_API_SERVICES -template class CustomAPIDeviceService : public UserServiceBase { +template class CustomAPIDeviceService : public UserServiceDynamic { public: CustomAPIDeviceService(const std::string &name, const std::array &arg_names, T *obj, void (T::*callback)(Ts...)) - : UserServiceBase(name, arg_names), obj_(obj), callback_(callback) {} + : UserServiceDynamic(name, arg_names), obj_(obj), callback_(callback) {} protected: void execute(Ts... x) override { (this->obj_->*this->callback_)(x...); } // NOLINT diff --git a/esphome/components/api/user_services.h b/esphome/components/api/user_services.h index 9ca5e1093e..2a887fc52d 100644 --- a/esphome/components/api/user_services.h +++ b/esphome/components/api/user_services.h @@ -23,11 +23,13 @@ template T get_execute_arg_value(const ExecuteServiceArgument &arg); template enums::ServiceArgType to_service_arg_type(); +// Base class for YAML-defined services (most common case) +// Stores only pointers to string literals in flash - no heap allocation template class UserServiceBase : public UserServiceDescriptor { public: - UserServiceBase(std::string name, const std::array &arg_names) - : name_(std::move(name)), arg_names_(arg_names) { - this->key_ = fnv1_hash(this->name_); + UserServiceBase(const char *name, const std::array &arg_names) + : name_(name), arg_names_(arg_names) { + this->key_ = fnv1_hash(name); } ListEntitiesServicesResponse encode_list_service_response() override { @@ -47,7 +49,7 @@ template class UserServiceBase : public UserServiceDescriptor { bool execute_service(const ExecuteServiceRequest &req) override { if (req.key != this->key_) return false; - if (req.args.size() != this->arg_names_.size()) + if (req.args.size() != sizeof...(Ts)) return false; this->execute_(req.args, typename gens::type()); return true; @@ -59,14 +61,60 @@ template class UserServiceBase : public UserServiceDescriptor { this->execute((get_execute_arg_value(args[S]))...); } - std::string name_; + // Pointers to string literals in flash - no heap allocation + const char *name_; + std::array arg_names_; uint32_t key_{0}; +}; + +// Separate class for custom_api_device services (rare case) +// Stores copies of runtime-generated names +template class UserServiceDynamic : public UserServiceDescriptor { + public: + UserServiceDynamic(std::string name, const std::array &arg_names) + : name_(std::move(name)), arg_names_(arg_names) { + this->key_ = fnv1_hash(this->name_.c_str()); + } + + ListEntitiesServicesResponse encode_list_service_response() override { + ListEntitiesServicesResponse msg; + msg.set_name(StringRef(this->name_)); + msg.key = this->key_; + std::array arg_types = {to_service_arg_type()...}; + msg.args.init(sizeof...(Ts)); + for (size_t i = 0; i < sizeof...(Ts); i++) { + auto &arg = msg.args.emplace_back(); + arg.type = arg_types[i]; + arg.set_name(StringRef(this->arg_names_[i])); + } + return msg; + } + + bool execute_service(const ExecuteServiceRequest &req) override { + if (req.key != this->key_) + return false; + if (req.args.size() != sizeof...(Ts)) + return false; + this->execute_(req.args, typename gens::type()); + return true; + } + + protected: + virtual void execute(Ts... x) = 0; + template void execute_(const ArgsContainer &args, seq type) { + this->execute((get_execute_arg_value(args[S]))...); + } + + // Heap-allocated strings for runtime-generated names + std::string name_; std::array arg_names_; + uint32_t key_{0}; }; template class UserServiceTrigger : public UserServiceBase, public Trigger { public: - UserServiceTrigger(const std::string &name, const std::array &arg_names) + // Constructor for static names (YAML-defined services - used by code generator) + UserServiceTrigger(const char *name, const std::array &arg_names) : UserServiceBase(name, arg_names) {} protected: diff --git a/tests/integration/fixtures/api_custom_services.yaml b/tests/integration/fixtures/api_custom_services.yaml index 41efc95b85..a597c74126 100644 --- a/tests/integration/fixtures/api_custom_services.yaml +++ b/tests/integration/fixtures/api_custom_services.yaml @@ -11,6 +11,28 @@ api: then: - logger.log: "YAML service called" + # Test YAML service with arguments (tests UserServiceBase with const char* array) + - action: test_yaml_service_with_args + variables: + my_int: int + my_string: string + then: + - logger.log: + format: "YAML service with args: %d, %s" + args: [my_int, my_string.c_str()] + + # Test YAML service with multiple arguments + - action: test_yaml_service_many_args + variables: + arg1: int + arg2: float + arg3: bool + arg4: string + then: + - logger.log: + format: "YAML service many args: %d, %.2f, %d, %s" + args: [arg1, arg2, arg3, arg4.c_str()] + logger: level: DEBUG diff --git a/tests/integration/test_api_custom_services.py b/tests/integration/test_api_custom_services.py index 9ae4cdcb5d..967c504112 100644 --- a/tests/integration/test_api_custom_services.py +++ b/tests/integration/test_api_custom_services.py @@ -33,12 +33,16 @@ async def test_api_custom_services( # Track log messages yaml_service_future = loop.create_future() + yaml_args_future = loop.create_future() + yaml_many_args_future = loop.create_future() custom_service_future = loop.create_future() custom_args_future = loop.create_future() custom_arrays_future = loop.create_future() # Patterns to match in logs yaml_service_pattern = re.compile(r"YAML service called") + yaml_args_pattern = re.compile(r"YAML service with args: 123, test_value") + yaml_many_args_pattern = re.compile(r"YAML service many args: 42, 3\.14, 1, hello") custom_service_pattern = re.compile(r"Custom test service called!") custom_args_pattern = re.compile( r"Custom service called with: test_string, 456, 1, 78\.90" @@ -51,6 +55,10 @@ async def test_api_custom_services( """Check log output for expected messages.""" if not yaml_service_future.done() and yaml_service_pattern.search(line): yaml_service_future.set_result(True) + elif not yaml_args_future.done() and yaml_args_pattern.search(line): + yaml_args_future.set_result(True) + elif not yaml_many_args_future.done() and yaml_many_args_pattern.search(line): + yaml_many_args_future.set_result(True) elif not custom_service_future.done() and custom_service_pattern.search(line): custom_service_future.set_result(True) elif not custom_args_future.done() and custom_args_pattern.search(line): @@ -71,11 +79,13 @@ async def test_api_custom_services( # List services _, services = await client.list_entities_services() - # Should have 4 services: 1 YAML + 3 CustomAPIDevice - assert len(services) == 4, f"Expected 4 services, found {len(services)}" + # Should have 6 services: 3 YAML + 3 CustomAPIDevice + assert len(services) == 6, f"Expected 6 services, found {len(services)}" # Find our services yaml_service: UserService | None = None + yaml_args_service: UserService | None = None + yaml_many_args_service: UserService | None = None custom_service: UserService | None = None custom_args_service: UserService | None = None custom_arrays_service: UserService | None = None @@ -83,6 +93,10 @@ async def test_api_custom_services( for service in services: if service.name == "test_yaml_service": yaml_service = service + elif service.name == "test_yaml_service_with_args": + yaml_args_service = service + elif service.name == "test_yaml_service_many_args": + yaml_many_args_service = service elif service.name == "custom_test_service": custom_service = service elif service.name == "custom_service_with_args": @@ -91,6 +105,10 @@ async def test_api_custom_services( custom_arrays_service = service assert yaml_service is not None, "test_yaml_service not found" + assert yaml_args_service is not None, "test_yaml_service_with_args not found" + assert yaml_many_args_service is not None, ( + "test_yaml_service_many_args not found" + ) assert custom_service is not None, "custom_test_service not found" assert custom_args_service is not None, "custom_service_with_args not found" assert custom_arrays_service is not None, "custom_service_with_arrays not found" @@ -99,6 +117,44 @@ async def test_api_custom_services( client.execute_service(yaml_service, {}) await asyncio.wait_for(yaml_service_future, timeout=5.0) + # Verify YAML service with args arguments + assert len(yaml_args_service.args) == 2 + yaml_args_types = {arg.name: arg.type for arg in yaml_args_service.args} + assert yaml_args_types["my_int"] == UserServiceArgType.INT + assert yaml_args_types["my_string"] == UserServiceArgType.STRING + + # Test YAML service with arguments + client.execute_service( + yaml_args_service, + { + "my_int": 123, + "my_string": "test_value", + }, + ) + await asyncio.wait_for(yaml_args_future, timeout=5.0) + + # Verify YAML service with many args arguments + assert len(yaml_many_args_service.args) == 4 + yaml_many_args_types = { + arg.name: arg.type for arg in yaml_many_args_service.args + } + assert yaml_many_args_types["arg1"] == UserServiceArgType.INT + assert yaml_many_args_types["arg2"] == UserServiceArgType.FLOAT + assert yaml_many_args_types["arg3"] == UserServiceArgType.BOOL + assert yaml_many_args_types["arg4"] == UserServiceArgType.STRING + + # Test YAML service with many arguments + client.execute_service( + yaml_many_args_service, + { + "arg1": 42, + "arg2": 3.14, + "arg3": True, + "arg4": "hello", + }, + ) + await asyncio.wait_for(yaml_many_args_future, timeout=5.0) + # Test simple CustomAPIDevice service client.execute_service(custom_service, {}) await asyncio.wait_for(custom_service_future, timeout=5.0) From ab5d8f67aee0c631b3fdf7d6cae94aa71fb4b60c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:48:02 +1000 Subject: [PATCH 411/526] [core] Add helper functions for clamp_at_... (#10387) --- esphome/core/helpers.h | 16 +++++++++++++++- tests/components/esp32/common.yaml | 13 +++++++++++++ tests/components/esp8266/test.esp8266-ard.yaml | 13 +++++++++++++ tests/components/host/common.yaml | 7 +++++++ tests/components/libretiny/test.bk72xx-ard.yaml | 13 +++++++++++++ tests/components/nrf52/test.nrf52-adafruit.yaml | 10 ++++++++++ tests/components/rp2040/test.rp2040-ard.yaml | 13 +++++++++++++ 7 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tests/components/esp32/common.yaml create mode 100644 tests/components/esp8266/test.esp8266-ard.yaml create mode 100644 tests/components/libretiny/test.bk72xx-ard.yaml create mode 100644 tests/components/rp2040/test.rp2040-ard.yaml diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 660874ed1a..48af7f674a 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "esphome/core/optional.h" @@ -1169,7 +1170,20 @@ template class RAMAllocator { template using ExternalRAMAllocator = RAMAllocator; -/// @} +/** + * Functions to constrain the range of arithmetic values. + */ + +template T clamp_at_least(T value, T min) { + if (value < min) + return min; + return value; +} +template T clamp_at_most(T value, T max) { + if (value > max) + return max; + return value; +} /// @name Internal functions ///@{ diff --git a/tests/components/esp32/common.yaml b/tests/components/esp32/common.yaml new file mode 100644 index 0000000000..039a261016 --- /dev/null +++ b/tests/components/esp32/common.yaml @@ -0,0 +1,13 @@ +logger: + level: VERBOSE + +esphome: + on_boot: + - lambda: |- + int x = 100; + x = clamp(x, 50, 90); + assert(x == 90); + x = clamp_at_least(x, 95); + assert(x == 95); + x = clamp_at_most(x, 40); + assert(x == 40); diff --git a/tests/components/esp8266/test.esp8266-ard.yaml b/tests/components/esp8266/test.esp8266-ard.yaml new file mode 100644 index 0000000000..039a261016 --- /dev/null +++ b/tests/components/esp8266/test.esp8266-ard.yaml @@ -0,0 +1,13 @@ +logger: + level: VERBOSE + +esphome: + on_boot: + - lambda: |- + int x = 100; + x = clamp(x, 50, 90); + assert(x == 90); + x = clamp_at_least(x, 95); + assert(x == 95); + x = clamp_at_most(x, 40); + assert(x == 40); diff --git a/tests/components/host/common.yaml b/tests/components/host/common.yaml index 5c329c8245..d5c8446ae8 100644 --- a/tests/components/host/common.yaml +++ b/tests/components/host/common.yaml @@ -15,3 +15,10 @@ esphome: static const uint8_t my_addr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; if (!mac_address_is_valid(my_addr)) ESP_LOGD("test", "Invalid mac address %X", my_addr[0]); // etc. + int x = 100; + x = clamp(x, 50, 90); + assert(x == 90); + x = clamp_at_least(x, 95); + assert(x == 95); + x = clamp_at_most(x, 40); + assert(x == 40); diff --git a/tests/components/libretiny/test.bk72xx-ard.yaml b/tests/components/libretiny/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..039a261016 --- /dev/null +++ b/tests/components/libretiny/test.bk72xx-ard.yaml @@ -0,0 +1,13 @@ +logger: + level: VERBOSE + +esphome: + on_boot: + - lambda: |- + int x = 100; + x = clamp(x, 50, 90); + assert(x == 90); + x = clamp_at_least(x, 95); + assert(x == 95); + x = clamp_at_most(x, 40); + assert(x == 40); diff --git a/tests/components/nrf52/test.nrf52-adafruit.yaml b/tests/components/nrf52/test.nrf52-adafruit.yaml index 3fe80209b6..cf704ecceb 100644 --- a/tests/components/nrf52/test.nrf52-adafruit.yaml +++ b/tests/components/nrf52/test.nrf52-adafruit.yaml @@ -1,3 +1,13 @@ +esphome: + on_boot: + - lambda: |- + int x = 100; + x = clamp(x, 50, 90); + assert(x == 90); + x = clamp_at_least(x, 95); + assert(x == 95); + x = clamp_at_most(x, 40); + assert(x == 40); nrf52: dfu: reset_pin: diff --git a/tests/components/rp2040/test.rp2040-ard.yaml b/tests/components/rp2040/test.rp2040-ard.yaml new file mode 100644 index 0000000000..039a261016 --- /dev/null +++ b/tests/components/rp2040/test.rp2040-ard.yaml @@ -0,0 +1,13 @@ +logger: + level: VERBOSE + +esphome: + on_boot: + - lambda: |- + int x = 100; + x = clamp(x, 50, 90); + assert(x == 90); + x = clamp_at_least(x, 95); + assert(x == 95); + x = clamp_at_most(x, 40); + assert(x == 40); From 822eacfd77878067484601ec2fe58edde7fae9e4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 20:49:24 -0600 Subject: [PATCH 412/526] [core] Fix wait_until and for_condition timing regression in automation chains (#11716) --- esphome/core/base_automation.h | 82 +++++++------ .../fixtures/wait_until_mid_loop_timing.yaml | 109 +++++++++++++++++ .../test_wait_until_mid_loop_timing.py | 112 ++++++++++++++++++ 3 files changed, 269 insertions(+), 34 deletions(-) create mode 100644 tests/integration/fixtures/wait_until_mid_loop_timing.yaml create mode 100644 tests/integration/test_wait_until_mid_loop_timing.py diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index 128a2d4b08..6f392c8959 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -98,22 +98,28 @@ template class ForCondition : public Condition, public Co TEMPLATABLE_VALUE(uint32_t, time); - void loop() override { this->check_internal(); } - float get_setup_priority() const override { return setup_priority::DATA; } - bool check_internal() { - bool cond = this->condition_->check(); - if (!cond) - this->last_inactive_ = App.get_loop_component_start_time(); - return cond; + void loop() override { + // Safe to use cached time - only called from Application::loop() + this->check_internal_(App.get_loop_component_start_time()); } + float get_setup_priority() const override { return setup_priority::DATA; } + bool check(const Ts &...x) override { - if (!this->check_internal()) + auto now = millis(); + if (!this->check_internal_(now)) return false; - return millis() - this->last_inactive_ >= this->time_.value(x...); + return now - this->last_inactive_ >= this->time_.value(x...); } protected: + bool check_internal_(uint32_t now) { + bool cond = this->condition_->check(); + if (!cond) + this->last_inactive_ = now; + return cond; + } + Condition<> *condition_; uint32_t last_inactive_{0}; }; @@ -424,34 +430,17 @@ template class WaitUntilAction : public Action, public Co auto timeout = this->timeout_value_.optional_value(x...); this->var_queue_.emplace_front(now, timeout, std::make_tuple(x...)); - // Enable loop now that we have work to do - this->enable_loop(); - this->loop(); + // Do immediate check with fresh timestamp + if (this->process_queue_(now)) { + // Only enable loop if we still have pending items + this->enable_loop(); + } } void loop() override { - if (this->num_running_ == 0) - return; - - auto now = App.get_loop_component_start_time(); - - this->var_queue_.remove_if([&](auto &queued) { - auto start = std::get(queued); - auto timeout = std::get>(queued); - auto &var = std::get>(queued); - - auto expired = timeout && (now - start) >= *timeout; - - if (!expired && !this->condition_->check_tuple(var)) { - return false; - } - - this->play_next_tuple_(var); - return true; - }); - - // If queue is now empty, disable loop until next play_complex - if (this->var_queue_.empty()) { + // Safe to use cached time - only called from Application::loop() + if (this->num_running_ > 0 && !this->process_queue_(App.get_loop_component_start_time())) { + // If queue is now empty, disable loop until next play_complex this->disable_loop(); } } @@ -467,6 +456,31 @@ template class WaitUntilAction : public Action, public Co } protected: + // Helper: Process queue, triggering completed items and removing them + // Returns true if queue still has pending items + bool process_queue_(uint32_t now) { + // Process each queued wait_until and remove completed ones + this->var_queue_.remove_if([&](auto &queued) { + auto start = std::get(queued); + auto timeout = std::get>(queued); + auto &var = std::get>(queued); + + // Check if timeout has expired + auto expired = timeout && (now - start) >= *timeout; + + // Keep waiting if not expired and condition not met + if (!expired && !this->condition_->check_tuple(var)) { + return false; + } + + // Condition met or timed out - trigger next action + this->play_next_tuple_(var); + return true; + }); + + return !this->var_queue_.empty(); + } + Condition *condition_; std::forward_list, std::tuple>> var_queue_{}; }; diff --git a/tests/integration/fixtures/wait_until_mid_loop_timing.yaml b/tests/integration/fixtures/wait_until_mid_loop_timing.yaml new file mode 100644 index 0000000000..32f59e81a1 --- /dev/null +++ b/tests/integration/fixtures/wait_until_mid_loop_timing.yaml @@ -0,0 +1,109 @@ +# Test for PR #11676 bug: wait_until timeout when triggered mid-component-loop +# This demonstrates that App.get_loop_component_start_time() is stale when +# wait_until is triggered partway through a component's loop execution + +esphome: + name: wait-mid-loop + +host: + +api: + actions: + - action: test_mid_loop_timeout + then: + - logger.log: "=== Test: wait_until triggered mid-loop should timeout correctly ===" + + # Reset test state + - globals.set: + id: test_complete + value: 'false' + + # Trigger the slow script that will call wait_until mid-execution + - script.execute: slow_script + + # Wait for test to complete (should take ~300ms: 100ms delay + 200ms timeout) + - wait_until: + condition: + lambda: return id(test_complete); + timeout: 2s + + - if: + condition: + lambda: return id(test_complete); + then: + - logger.log: "✓ Test PASSED: wait_until timed out correctly" + else: + - logger.log: "✗ Test FAILED: wait_until did not complete properly" + +logger: + level: DEBUG + +globals: + - id: test_complete + type: bool + restore_value: false + initial_value: 'false' + + - id: test_condition + type: bool + restore_value: false + initial_value: 'false' + + - id: timeout_start_time + type: uint32_t + restore_value: false + initial_value: '0' + + - id: timeout_end_time + type: uint32_t + restore_value: false + initial_value: '0' + +script: + # This script simulates a component that takes time during its execution + # When wait_until is triggered mid-script, the loop_component_start_time + # will be stale (from when the script's component loop started) + - id: slow_script + then: + - logger.log: "Script: Starting, about to do some work..." + + # Simulate component doing work for 100ms + # This represents time spent in a component's loop() before triggering wait_until + - delay: 100ms + + - logger.log: "Script: 100ms elapsed, now starting wait_until with 200ms timeout" + - lambda: |- + // Record when timeout starts + id(timeout_start_time) = millis(); + id(test_condition) = false; + + # At this point: + # - Script component's loop started 100ms ago + # - App.loop_component_start_time_ = time from 100ms ago (stale!) + # - wait_until will capture millis() NOW (fresh) + # - BUG: loop() will use stale loop_component_start_time, causing immediate timeout + + - wait_until: + condition: + lambda: return id(test_condition); + timeout: 200ms + + - lambda: |- + // Record when timeout completes + id(timeout_end_time) = millis(); + uint32_t elapsed = id(timeout_end_time) - id(timeout_start_time); + + ESP_LOGD("TEST", "wait_until completed after %u ms (expected ~200ms)", elapsed); + + // Check if timeout took approximately correct time + // Should be ~200ms, not <50ms (immediate timeout) + if (elapsed >= 150 && elapsed <= 250) { + ESP_LOGD("TEST", "✓ Timeout duration correct: %u ms", elapsed); + id(test_complete) = true; + } else { + ESP_LOGE("TEST", "✗ Timeout duration WRONG: %u ms (expected 150-250ms)", elapsed); + if (elapsed < 50) { + ESP_LOGE("TEST", " → Likely BUG: Immediate timeout due to stale loop_component_start_time"); + } + id(test_complete) = false; + } diff --git a/tests/integration/test_wait_until_mid_loop_timing.py b/tests/integration/test_wait_until_mid_loop_timing.py new file mode 100644 index 0000000000..01cad747ae --- /dev/null +++ b/tests/integration/test_wait_until_mid_loop_timing.py @@ -0,0 +1,112 @@ +"""Integration test for PR #11676 mid-loop timing bug. + +This test validates that wait_until timeouts work correctly when triggered +mid-component-loop, where App.get_loop_component_start_time() is stale. + +The bug: When wait_until is triggered partway through a component's loop execution +(e.g., from a script or automation), the cached loop_component_start_time_ is stale +relative to when the action was actually triggered. This causes timeout calculations +to underflow and timeout immediately instead of waiting the specified duration. +""" + +from __future__ import annotations + +import asyncio +import re + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_wait_until_mid_loop_timing( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that wait_until timeout works when triggered mid-component-loop. + + This test: + 1. Executes a script that delays 100ms (simulating component work) + 2. Then starts wait_until with 200ms timeout + 3. Verifies timeout takes ~200ms, not <50ms (immediate timeout bug) + """ + loop = asyncio.get_running_loop() + + # Track test results + test_results = { + "timeout_duration": None, + "passed": False, + "failed": False, + "bug_detected": False, + } + + # Patterns for log messages + timeout_duration = re.compile(r"wait_until completed after (\d+) ms") + test_pass = re.compile(r"✓ Timeout duration correct") + test_fail = re.compile(r"✗ Timeout duration WRONG") + bug_pattern = re.compile(r"Likely BUG: Immediate timeout") + test_passed = re.compile(r"✓ Test PASSED") + test_failed = re.compile(r"✗ Test FAILED") + + test_complete = loop.create_future() + + def check_output(line: str) -> None: + """Check log output for test results.""" + # Extract timeout duration + match = timeout_duration.search(line) + if match: + test_results["timeout_duration"] = int(match.group(1)) + + if test_pass.search(line): + test_results["passed"] = True + if test_fail.search(line): + test_results["failed"] = True + if bug_pattern.search(line): + test_results["bug_detected"] = True + + # Final test result + if ( + test_passed.search(line) + or test_failed.search(line) + and not test_complete.done() + ): + test_complete.set_result(True) + + async with ( + run_compiled(yaml_config, line_callback=check_output), + api_client_connected() as client, + ): + # Get the test service + _, services = await client.list_entities_services() + test_service = next( + (s for s in services if s.name == "test_mid_loop_timeout"), None + ) + assert test_service is not None, "test_mid_loop_timeout service not found" + + # Execute the test + client.execute_service(test_service, {}) + + # Wait for test to complete (100ms delay + 200ms timeout + margins = ~500ms) + await asyncio.wait_for(test_complete, timeout=5.0) + + # Verify results + assert test_results["timeout_duration"] is not None, ( + "Timeout duration not reported" + ) + assert test_results["passed"], ( + f"Test failed: wait_until took {test_results['timeout_duration']}ms, expected ~200ms. " + f"Bug detected: {test_results['bug_detected']}" + ) + assert not test_results["bug_detected"], ( + f"BUG DETECTED: wait_until timed out immediately ({test_results['timeout_duration']}ms) " + "instead of waiting 200ms. This indicates stale loop_component_start_time." + ) + + # Additional validation: timeout should be ~200ms (150-250ms range) + duration = test_results["timeout_duration"] + assert 150 <= duration <= 250, ( + f"Timeout duration {duration}ms outside expected range (150-250ms). " + f"This suggests timing regression from PR #11676." + ) From 74187845b7625efba82b1adf753335eb1713022a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 20:55:26 -0600 Subject: [PATCH 413/526] [select] Convert remaining components to use index-based control() (#11693) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/demo/demo_select.h | 2 +- .../es8388/select/adc_input_mic_select.cpp | 6 ++--- .../es8388/select/adc_input_mic_select.h | 2 +- .../es8388/select/dac_output_select.cpp | 6 ++--- .../es8388/select/dac_output_select.h | 2 +- esphome/components/lvgl/select/lvgl_select.h | 8 +++---- .../select/modbus_select.cpp | 22 +++++++++---------- .../modbus_controller/select/modbus_select.h | 2 +- .../select/existence_boundary_select.cpp | 9 +++----- .../select/existence_boundary_select.h | 2 +- .../select/motion_boundary_select.cpp | 9 +++----- .../select/motion_boundary_select.h | 2 +- .../select/scene_mode_select.cpp | 9 +++----- .../seeed_mr24hpc1/select/scene_mode_select.h | 2 +- .../select/unman_time_select.cpp | 9 +++----- .../seeed_mr24hpc1/select/unman_time_select.h | 2 +- .../select/height_threshold_select.cpp | 9 +++----- .../select/height_threshold_select.h | 2 +- .../select/install_height_select.cpp | 9 +++----- .../select/install_height_select.h | 2 +- .../select/sensitivity_select.cpp | 9 +++----- .../select/sensitivity_select.h | 2 +- .../components/tuya/select/tuya_select.cpp | 22 +++++++------------ esphome/components/tuya/select/tuya_select.h | 2 +- 24 files changed, 61 insertions(+), 90 deletions(-) diff --git a/esphome/components/demo/demo_select.h b/esphome/components/demo/demo_select.h index 1951a684a2..1a5df13eda 100644 --- a/esphome/components/demo/demo_select.h +++ b/esphome/components/demo/demo_select.h @@ -8,7 +8,7 @@ namespace demo { class DemoSelect : public select::Select, public Component { protected: - void control(const std::string &value) override { this->publish_state(value); } + void control(size_t index) override { this->publish_state(index); } }; } // namespace demo diff --git a/esphome/components/es8388/select/adc_input_mic_select.cpp b/esphome/components/es8388/select/adc_input_mic_select.cpp index 5fab5b8a92..2e47534296 100644 --- a/esphome/components/es8388/select/adc_input_mic_select.cpp +++ b/esphome/components/es8388/select/adc_input_mic_select.cpp @@ -3,9 +3,9 @@ namespace esphome { namespace es8388 { -void ADCInputMicSelect::control(const std::string &value) { - this->publish_state(value); - this->parent_->set_adc_input_mic(static_cast(this->index_of(value).value())); +void ADCInputMicSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_adc_input_mic(static_cast(index)); } } // namespace es8388 diff --git a/esphome/components/es8388/select/adc_input_mic_select.h b/esphome/components/es8388/select/adc_input_mic_select.h index 8d035525ef..f0fa840d00 100644 --- a/esphome/components/es8388/select/adc_input_mic_select.h +++ b/esphome/components/es8388/select/adc_input_mic_select.h @@ -8,7 +8,7 @@ namespace es8388 { class ADCInputMicSelect : public select::Select, public Parented { protected: - void control(const std::string &value) override; + void control(size_t index) override; }; } // namespace es8388 diff --git a/esphome/components/es8388/select/dac_output_select.cpp b/esphome/components/es8388/select/dac_output_select.cpp index 268b5f290c..9af288a721 100644 --- a/esphome/components/es8388/select/dac_output_select.cpp +++ b/esphome/components/es8388/select/dac_output_select.cpp @@ -3,9 +3,9 @@ namespace esphome { namespace es8388 { -void DacOutputSelect::control(const std::string &value) { - this->publish_state(value); - this->parent_->set_dac_output(static_cast(this->index_of(value).value())); +void DacOutputSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_dac_output(static_cast(index)); } } // namespace es8388 diff --git a/esphome/components/es8388/select/dac_output_select.h b/esphome/components/es8388/select/dac_output_select.h index fccae9fc19..40d8a66553 100644 --- a/esphome/components/es8388/select/dac_output_select.h +++ b/esphome/components/es8388/select/dac_output_select.h @@ -8,7 +8,7 @@ namespace es8388 { class DacOutputSelect : public select::Select, public Parented { protected: - void control(const std::string &value) override; + void control(size_t index) override; }; } // namespace es8388 diff --git a/esphome/components/lvgl/select/lvgl_select.h b/esphome/components/lvgl/select/lvgl_select.h index d4c9631073..70bb3e7bcb 100644 --- a/esphome/components/lvgl/select/lvgl_select.h +++ b/esphome/components/lvgl/select/lvgl_select.h @@ -41,16 +41,16 @@ class LVGLSelect : public select::Select, public Component { } void publish() { - this->publish_state(this->widget_->get_selected_text()); + auto index = this->widget_->get_selected_index(); + this->publish_state(index); if (this->restore_) { - auto index = this->widget_->get_selected_index(); this->pref_.save(&index); } } protected: - void control(const std::string &value) override { - this->widget_->set_selected_text(value, this->anim_); + void control(size_t index) override { + this->widget_->set_selected_index(index, this->anim_); this->publish(); } void set_options_() { diff --git a/esphome/components/modbus_controller/select/modbus_select.cpp b/esphome/components/modbus_controller/select/modbus_select.cpp index 48bf2835f2..853f4215c3 100644 --- a/esphome/components/modbus_controller/select/modbus_select.cpp +++ b/esphome/components/modbus_controller/select/modbus_select.cpp @@ -28,8 +28,9 @@ void ModbusSelect::parse_and_publish(const std::vector &data) { if (map_it != this->mapping_.cend()) { size_t idx = std::distance(this->mapping_.cbegin(), map_it); - new_state = std::string(this->option_at(idx)); - ESP_LOGV(TAG, "Found option %s for value %lld", new_state->c_str(), value); + ESP_LOGV(TAG, "Found option %s for value %lld", this->option_at(idx), value); + this->publish_state(idx); + return; } else { ESP_LOGE(TAG, "No option found for mapping %lld", value); } @@ -40,19 +41,16 @@ void ModbusSelect::parse_and_publish(const std::vector &data) { } } -void ModbusSelect::control(const std::string &value) { - auto idx = this->index_of(value); - if (!idx.has_value()) { - ESP_LOGW(TAG, "Invalid option '%s'", value.c_str()); - return; - } - optional mapval = this->mapping_[idx.value()]; - ESP_LOGD(TAG, "Found value %lld for option '%s'", *mapval, value.c_str()); +void ModbusSelect::control(size_t index) { + optional mapval = this->mapping_[index]; + const char *option = this->option_at(index); + ESP_LOGD(TAG, "Found value %lld for option '%s'", *mapval, option); std::vector data; if (this->write_transform_func_.has_value()) { - auto val = (*this->write_transform_func_)(this, value, *mapval, data); + // Transform func requires string parameter for backward compatibility + auto val = (*this->write_transform_func_)(this, std::string(option), *mapval, data); if (val.has_value()) { mapval = *val; ESP_LOGV(TAG, "write_lambda returned mapping value %lld", *mapval); @@ -85,7 +83,7 @@ void ModbusSelect::control(const std::string &value) { this->parent_->queue_command(write_cmd); if (this->optimistic_) - this->publish_state(value); + this->publish_state(index); } } // namespace modbus_controller diff --git a/esphome/components/modbus_controller/select/modbus_select.h b/esphome/components/modbus_controller/select/modbus_select.h index e6b98aead2..fde441f2bc 100644 --- a/esphome/components/modbus_controller/select/modbus_select.h +++ b/esphome/components/modbus_controller/select/modbus_select.h @@ -38,7 +38,7 @@ class ModbusSelect : public Component, public select::Select, public SensorItem void dump_config() override; void parse_and_publish(const std::vector &data) override; - void control(const std::string &value) override; + void control(size_t index) override; protected: std::vector mapping_{}; diff --git a/esphome/components/seeed_mr24hpc1/select/existence_boundary_select.cpp b/esphome/components/seeed_mr24hpc1/select/existence_boundary_select.cpp index 03c2ec4745..81543055a4 100644 --- a/esphome/components/seeed_mr24hpc1/select/existence_boundary_select.cpp +++ b/esphome/components/seeed_mr24hpc1/select/existence_boundary_select.cpp @@ -3,12 +3,9 @@ namespace esphome { namespace seeed_mr24hpc1 { -void ExistenceBoundarySelect::control(const std::string &value) { - this->publish_state(value); - auto index = this->index_of(value); - if (index.has_value()) { - this->parent_->set_existence_boundary(index.value()); - } +void ExistenceBoundarySelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_existence_boundary(index); } } // namespace seeed_mr24hpc1 diff --git a/esphome/components/seeed_mr24hpc1/select/existence_boundary_select.h b/esphome/components/seeed_mr24hpc1/select/existence_boundary_select.h index ad770a7296..933279dd13 100644 --- a/esphome/components/seeed_mr24hpc1/select/existence_boundary_select.h +++ b/esphome/components/seeed_mr24hpc1/select/existence_boundary_select.h @@ -11,7 +11,7 @@ class ExistenceBoundarySelect : public select::Select, public Parentedpublish_state(value); - auto index = this->index_of(value); - if (index.has_value()) { - this->parent_->set_motion_boundary(index.value()); - } +void MotionBoundarySelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_motion_boundary(index); } } // namespace seeed_mr24hpc1 diff --git a/esphome/components/seeed_mr24hpc1/select/motion_boundary_select.h b/esphome/components/seeed_mr24hpc1/select/motion_boundary_select.h index 9058e3130b..b0051ae6b1 100644 --- a/esphome/components/seeed_mr24hpc1/select/motion_boundary_select.h +++ b/esphome/components/seeed_mr24hpc1/select/motion_boundary_select.h @@ -11,7 +11,7 @@ class MotionBoundarySelect : public select::Select, public Parentedpublish_state(value); - auto index = this->index_of(value); - if (index.has_value()) { - this->parent_->set_scene_mode(index.value()); - } +void SceneModeSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_scene_mode(index); } } // namespace seeed_mr24hpc1 diff --git a/esphome/components/seeed_mr24hpc1/select/scene_mode_select.h b/esphome/components/seeed_mr24hpc1/select/scene_mode_select.h index 95508d49b0..f478ea5b66 100644 --- a/esphome/components/seeed_mr24hpc1/select/scene_mode_select.h +++ b/esphome/components/seeed_mr24hpc1/select/scene_mode_select.h @@ -11,7 +11,7 @@ class SceneModeSelect : public select::Select, public Parentedpublish_state(value); - auto index = this->index_of(value); - if (index.has_value()) { - this->parent_->set_unman_time(index.value()); - } +void UnmanTimeSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_unman_time(index); } } // namespace seeed_mr24hpc1 diff --git a/esphome/components/seeed_mr24hpc1/select/unman_time_select.h b/esphome/components/seeed_mr24hpc1/select/unman_time_select.h index 7131988cda..a64ff4b840 100644 --- a/esphome/components/seeed_mr24hpc1/select/unman_time_select.h +++ b/esphome/components/seeed_mr24hpc1/select/unman_time_select.h @@ -11,7 +11,7 @@ class UnmanTimeSelect : public select::Select, public Parentedpublish_state(value); - auto index = this->index_of(value); - if (index.has_value()) { - this->parent_->set_height_threshold(index.value()); - } +void HeightThresholdSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_height_threshold(index); } } // namespace seeed_mr60fda2 diff --git a/esphome/components/seeed_mr60fda2/select/height_threshold_select.h b/esphome/components/seeed_mr60fda2/select/height_threshold_select.h index b856dbc89a..f5707c7a88 100644 --- a/esphome/components/seeed_mr60fda2/select/height_threshold_select.h +++ b/esphome/components/seeed_mr60fda2/select/height_threshold_select.h @@ -11,7 +11,7 @@ class HeightThresholdSelect : public select::Select, public Parentedpublish_state(value); - auto index = this->index_of(value); - if (index.has_value()) { - this->parent_->set_install_height(index.value()); - } +void InstallHeightSelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_install_height(index); } } // namespace seeed_mr60fda2 diff --git a/esphome/components/seeed_mr60fda2/select/install_height_select.h b/esphome/components/seeed_mr60fda2/select/install_height_select.h index 7430da3493..470d96c50c 100644 --- a/esphome/components/seeed_mr60fda2/select/install_height_select.h +++ b/esphome/components/seeed_mr60fda2/select/install_height_select.h @@ -11,7 +11,7 @@ class InstallHeightSelect : public select::Select, public Parentedpublish_state(value); - auto index = this->index_of(value); - if (index.has_value()) { - this->parent_->set_sensitivity(index.value()); - } +void SensitivitySelect::control(size_t index) { + this->publish_state(index); + this->parent_->set_sensitivity(index); } } // namespace seeed_mr60fda2 diff --git a/esphome/components/seeed_mr60fda2/select/sensitivity_select.h b/esphome/components/seeed_mr60fda2/select/sensitivity_select.h index d1accc1b5b..82ed4c5d79 100644 --- a/esphome/components/seeed_mr60fda2/select/sensitivity_select.h +++ b/esphome/components/seeed_mr60fda2/select/sensitivity_select.h @@ -11,7 +11,7 @@ class SensitivitySelect : public select::Select, public Parentedoptimistic_) - this->publish_state(value); + this->publish_state(index); - auto idx = this->index_of(value); - if (idx.has_value()) { - uint8_t mapping = this->mappings_.at(idx.value()); - ESP_LOGV(TAG, "Setting %u datapoint value to %u:%s", this->select_id_, mapping, value.c_str()); - if (this->is_int_) { - this->parent_->set_integer_datapoint_value(this->select_id_, mapping); - } else { - this->parent_->set_enum_datapoint_value(this->select_id_, mapping); - } - return; + uint8_t mapping = this->mappings_.at(index); + ESP_LOGV(TAG, "Setting %u datapoint value to %u:%s", this->select_id_, mapping, this->option_at(index)); + if (this->is_int_) { + this->parent_->set_integer_datapoint_value(this->select_id_, mapping); + } else { + this->parent_->set_enum_datapoint_value(this->select_id_, mapping); } - - ESP_LOGW(TAG, "Invalid value %s", value.c_str()); } void TuyaSelect::dump_config() { diff --git a/esphome/components/tuya/select/tuya_select.h b/esphome/components/tuya/select/tuya_select.h index 12d7b507d4..24505c9910 100644 --- a/esphome/components/tuya/select/tuya_select.h +++ b/esphome/components/tuya/select/tuya_select.h @@ -23,7 +23,7 @@ class TuyaSelect : public select::Select, public Component { void set_select_mappings(std::vector mappings) { this->mappings_ = std::move(mappings); } protected: - void control(const std::string &value) override; + void control(size_t index) override; Tuya *parent_; bool optimistic_ = false; From 895d76ca030927210f5736161e19e1d7cfd23a54 Mon Sep 17 00:00:00 2001 From: Szewcson Date: Thu, 6 Nov 2025 04:19:29 +0100 Subject: [PATCH 414/526] [gdk101] Fix fw version reporting (#11029) Signed-off-by: szewcu Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/gdk101/gdk101.cpp | 15 +++++++++------ esphome/components/gdk101/gdk101.h | 7 ++++++- esphome/components/gdk101/sensor.py | 9 ++------- esphome/components/gdk101/text_sensor.py | 23 +++++++++++++++++++++++ tests/components/gdk101/common.yaml | 7 +++++-- 5 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 esphome/components/gdk101/text_sensor.py diff --git a/esphome/components/gdk101/gdk101.cpp b/esphome/components/gdk101/gdk101.cpp index 4c156ab24b..6c218f03d9 100644 --- a/esphome/components/gdk101/gdk101.cpp +++ b/esphome/components/gdk101/gdk101.cpp @@ -62,7 +62,6 @@ void GDK101Component::dump_config() { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } #ifdef USE_SENSOR - LOG_SENSOR(" ", "Firmware Version", this->fw_version_sensor_); LOG_SENSOR(" ", "Average Radaition Dose per 1 minute", this->rad_1m_sensor_); LOG_SENSOR(" ", "Average Radaition Dose per 10 minutes", this->rad_10m_sensor_); LOG_SENSOR(" ", "Status", this->status_sensor_); @@ -72,6 +71,10 @@ void GDK101Component::dump_config() { #ifdef USE_BINARY_SENSOR LOG_BINARY_SENSOR(" ", "Vibration Status", this->vibration_binary_sensor_); #endif // USE_BINARY_SENSOR + +#ifdef USE_TEXT_SENSOR + LOG_TEXT_SENSOR(" ", "Firmware Version", this->fw_version_text_sensor_); +#endif // USE_TEXT_SENSOR } float GDK101Component::get_setup_priority() const { return setup_priority::DATA; } @@ -153,18 +156,18 @@ bool GDK101Component::read_status_(uint8_t *data) { } bool GDK101Component::read_fw_version_(uint8_t *data) { -#ifdef USE_SENSOR - if (this->fw_version_sensor_ != nullptr) { +#ifdef USE_TEXT_SENSOR + if (this->fw_version_text_sensor_ != nullptr) { if (!this->read_bytes(GDK101_REG_READ_FIRMWARE, data, 2)) { ESP_LOGE(TAG, "Updating GDK101 failed!"); return false; } - const float fw_version = data[0] + (data[1] / 10.0f); + const std::string fw_version_str = str_sprintf("%d.%d", data[0], data[1]); - this->fw_version_sensor_->publish_state(fw_version); + this->fw_version_text_sensor_->publish_state(fw_version_str); } -#endif // USE_SENSOR +#endif // USE_TEXT_SENSOR return true; } diff --git a/esphome/components/gdk101/gdk101.h b/esphome/components/gdk101/gdk101.h index 460e72ac89..f250a42a54 100644 --- a/esphome/components/gdk101/gdk101.h +++ b/esphome/components/gdk101/gdk101.h @@ -8,6 +8,9 @@ #ifdef USE_BINARY_SENSOR #include "esphome/components/binary_sensor/binary_sensor.h" #endif // USE_BINARY_SENSOR +#ifdef USE_TEXT_SENSOR +#include "esphome/components/text_sensor/text_sensor.h" +#endif // USE_TEXT_SENSOR #include "esphome/components/i2c/i2c.h" namespace esphome { @@ -25,12 +28,14 @@ class GDK101Component : public PollingComponent, public i2c::I2CDevice { SUB_SENSOR(rad_1m) SUB_SENSOR(rad_10m) SUB_SENSOR(status) - SUB_SENSOR(fw_version) SUB_SENSOR(measurement_duration) #endif // USE_SENSOR #ifdef USE_BINARY_SENSOR SUB_BINARY_SENSOR(vibration) #endif // USE_BINARY_SENSOR +#ifdef USE_TEXT_SENSOR + SUB_TEXT_SENSOR(fw_version) +#endif // USE_TEXT_SENSOR public: void setup() override; diff --git a/esphome/components/gdk101/sensor.py b/esphome/components/gdk101/sensor.py index d04e0b8367..6cf89e0fd4 100644 --- a/esphome/components/gdk101/sensor.py +++ b/esphome/components/gdk101/sensor.py @@ -40,9 +40,8 @@ CONFIG_SCHEMA = cv.Schema( device_class=DEVICE_CLASS_EMPTY, state_class=STATE_CLASS_MEASUREMENT, ), - cv.Optional(CONF_VERSION): sensor.sensor_schema( - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - accuracy_decimals=1, + cv.Optional(CONF_VERSION): cv.invalid( + "The 'version' option has been moved to the `text_sensor` component." ), cv.Optional(CONF_STATUS): sensor.sensor_schema( entity_category=ENTITY_CATEGORY_DIAGNOSTIC, @@ -71,10 +70,6 @@ async def to_code(config): sens = await sensor.new_sensor(radiation_dose_per_10m) cg.add(hub.set_rad_10m_sensor(sens)) - if version_config := config.get(CONF_VERSION): - sens = await sensor.new_sensor(version_config) - cg.add(hub.set_fw_version_sensor(sens)) - if status_config := config.get(CONF_STATUS): sens = await sensor.new_sensor(status_config) cg.add(hub.set_status_sensor(sens)) diff --git a/esphome/components/gdk101/text_sensor.py b/esphome/components/gdk101/text_sensor.py new file mode 100644 index 0000000000..703e68493a --- /dev/null +++ b/esphome/components/gdk101/text_sensor.py @@ -0,0 +1,23 @@ +import esphome.codegen as cg +from esphome.components import text_sensor +import esphome.config_validation as cv +from esphome.const import CONF_VERSION, ENTITY_CATEGORY_DIAGNOSTIC, ICON_CHIP + +from . import CONF_GDK101_ID, GDK101Component + +DEPENDENCIES = ["gdk101"] + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_GDK101_ID): cv.use_id(GDK101Component), + cv.Required(CONF_VERSION): text_sensor.text_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, icon=ICON_CHIP + ), + } +) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_GDK101_ID]) + var = await text_sensor.new_text_sensor(config[CONF_VERSION]) + cg.add(hub.set_fw_version_text_sensor(var)) diff --git a/tests/components/gdk101/common.yaml b/tests/components/gdk101/common.yaml index 4eb5586ade..b9b93d760f 100644 --- a/tests/components/gdk101/common.yaml +++ b/tests/components/gdk101/common.yaml @@ -11,8 +11,6 @@ sensor: name: Radiation Dose @ 10 min status: name: Status - version: - name: FW Version measurement_duration: name: Measuring Time @@ -21,3 +19,8 @@ binary_sensor: gdk101_id: my_gdk101 vibrations: name: Vibrations + +text_sensor: + - platform: gdk101 + version: + name: FW Version From 26607713bb1ba0848100384b9ddecd6d4345ba4d Mon Sep 17 00:00:00 2001 From: rwrozelle Date: Wed, 5 Nov 2025 22:57:31 -0500 Subject: [PATCH 415/526] [openthread] add poll period for mtd devices (#11374) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/openthread/__init__.py | 19 ++++++++++++-- esphome/components/openthread/const.py | 1 + esphome/components/openthread/openthread.cpp | 17 ++++++++++++ esphome/components/openthread/openthread.h | 7 +++++ .../components/openthread/openthread_esp.cpp | 26 +++++++++++++++++++ .../openthread/test.esp32-c6-idf.yaml | 4 ++- 6 files changed, 71 insertions(+), 3 deletions(-) diff --git a/esphome/components/openthread/__init__.py b/esphome/components/openthread/__init__.py index 572ec144d4..e3ad3ed76c 100644 --- a/esphome/components/openthread/__init__.py +++ b/esphome/components/openthread/__init__.py @@ -9,7 +9,7 @@ from esphome.components.esp32 import ( from esphome.components.mdns import MDNSComponent, enable_mdns_storage import esphome.config_validation as cv from esphome.const import CONF_CHANNEL, CONF_ENABLE_IPV6, CONF_ID, CONF_USE_ADDRESS -from esphome.core import CORE +from esphome.core import CORE, TimePeriodMilliseconds import esphome.final_validate as fv from esphome.types import ConfigType @@ -22,6 +22,7 @@ from .const import ( CONF_NETWORK_KEY, CONF_NETWORK_NAME, CONF_PAN_ID, + CONF_POLL_PERIOD, CONF_PSKC, CONF_SRP_ID, CONF_TLV, @@ -89,7 +90,7 @@ def set_sdkconfig_options(config): add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT", True) add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT_MAX_SERVICES", 5) - # TODO: Add suport for sleepy end devices + # TODO: Add suport for synchronized sleepy end devices (SSED) add_idf_sdkconfig_option(f"CONFIG_OPENTHREAD_{config.get(CONF_DEVICE_TYPE)}", True) @@ -113,6 +114,17 @@ _CONNECTION_SCHEMA = cv.Schema( def _validate(config: ConfigType) -> ConfigType: if CONF_USE_ADDRESS not in config: config[CONF_USE_ADDRESS] = f"{CORE.name}.local" + device_type = config.get(CONF_DEVICE_TYPE) + poll_period = config.get(CONF_POLL_PERIOD) + if ( + device_type == "FTD" + and poll_period + and poll_period > TimePeriodMilliseconds(milliseconds=0) + ): + raise cv.Invalid( + f"{CONF_POLL_PERIOD} can only be used with {CONF_DEVICE_TYPE}: MTD" + ) + return config @@ -135,6 +147,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_FORCE_DATASET): cv.boolean, cv.Optional(CONF_TLV): cv.string_strict, cv.Optional(CONF_USE_ADDRESS): cv.string_strict, + cv.Optional(CONF_POLL_PERIOD): cv.positive_time_period_milliseconds, } ).extend(_CONNECTION_SCHEMA), cv.has_exactly_one_key(CONF_NETWORK_KEY, CONF_TLV), @@ -167,6 +180,8 @@ async def to_code(config): ot = cg.new_Pvariable(config[CONF_ID]) cg.add(ot.set_use_address(config[CONF_USE_ADDRESS])) await cg.register_component(ot, config) + if (poll_period := config.get(CONF_POLL_PERIOD)) is not None: + cg.add(ot.set_poll_period(poll_period)) srp = cg.new_Pvariable(config[CONF_SRP_ID]) mdns_component = await cg.get_variable(config[CONF_MDNS_ID]) diff --git a/esphome/components/openthread/const.py b/esphome/components/openthread/const.py index 7a6ffb2df4..f0274a8c9e 100644 --- a/esphome/components/openthread/const.py +++ b/esphome/components/openthread/const.py @@ -6,6 +6,7 @@ CONF_MESH_LOCAL_PREFIX = "mesh_local_prefix" CONF_NETWORK_NAME = "network_name" CONF_NETWORK_KEY = "network_key" CONF_PAN_ID = "pan_id" +CONF_POLL_PERIOD = "poll_period" CONF_PSKC = "pskc" CONF_SRP_ID = "srp_id" CONF_TLV = "tlv" diff --git a/esphome/components/openthread/openthread.cpp b/esphome/components/openthread/openthread.cpp index d7fb1e1d42..721ab89326 100644 --- a/esphome/components/openthread/openthread.cpp +++ b/esphome/components/openthread/openthread.cpp @@ -29,6 +29,23 @@ OpenThreadComponent *global_openthread_component = // NOLINT(cppcoreguidelines- OpenThreadComponent::OpenThreadComponent() { global_openthread_component = this; } +void OpenThreadComponent::dump_config() { + ESP_LOGCONFIG(TAG, "Open Thread:"); +#if CONFIG_OPENTHREAD_FTD + ESP_LOGCONFIG(TAG, " Device Type: FTD"); +#elif CONFIG_OPENTHREAD_MTD + ESP_LOGCONFIG(TAG, " Device Type: MTD"); + // TBD: Synchronized Sleepy End Device + if (this->poll_period > 0) { + ESP_LOGCONFIG(TAG, " Device is configured as Sleepy End Device (SED)"); + uint32_t duration = this->poll_period / 1000; + ESP_LOGCONFIG(TAG, " Poll Period: %" PRIu32 "s", duration); + } else { + ESP_LOGCONFIG(TAG, " Device is configured as Minimal End Device (MED)"); + } +#endif +} + bool OpenThreadComponent::is_connected() { auto lock = InstanceLock::try_acquire(100); if (!lock) { diff --git a/esphome/components/openthread/openthread.h b/esphome/components/openthread/openthread.h index 3132e41696..546128b366 100644 --- a/esphome/components/openthread/openthread.h +++ b/esphome/components/openthread/openthread.h @@ -22,6 +22,7 @@ class OpenThreadComponent : public Component { public: OpenThreadComponent(); ~OpenThreadComponent(); + void dump_config() override; void setup() override; bool teardown() override; float get_setup_priority() const override { return setup_priority::WIFI; } @@ -35,6 +36,9 @@ class OpenThreadComponent : public Component { const char *get_use_address() const; void set_use_address(const char *use_address); +#if CONFIG_OPENTHREAD_MTD + void set_poll_period(uint32_t poll_period) { this->poll_period = poll_period; } +#endif protected: std::optional get_omr_address_(InstanceLock &lock); @@ -46,6 +50,9 @@ class OpenThreadComponent : public Component { // Stores a pointer to a string literal (static storage duration). // ONLY set from Python-generated code with string literals - never dynamic strings. const char *use_address_{""}; +#if CONFIG_OPENTHREAD_MTD + uint32_t poll_period{0}; +#endif }; extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/openthread/openthread_esp.cpp b/esphome/components/openthread/openthread_esp.cpp index b11b7ad34a..72dc521091 100644 --- a/esphome/components/openthread/openthread_esp.cpp +++ b/esphome/components/openthread/openthread_esp.cpp @@ -105,6 +105,32 @@ void OpenThreadComponent::ot_main() { esp_cli_custom_command_init(); #endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION + otLinkModeConfig link_mode_config = {0}; +#if CONFIG_OPENTHREAD_FTD + link_mode_config.mRxOnWhenIdle = true; + link_mode_config.mDeviceType = true; + link_mode_config.mNetworkData = true; +#elif CONFIG_OPENTHREAD_MTD + if (this->poll_period > 0) { + if (otLinkSetPollPeriod(esp_openthread_get_instance(), this->poll_period) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to set OpenThread pollperiod."); + } + uint32_t link_polling_period = otLinkGetPollPeriod(esp_openthread_get_instance()); + ESP_LOGD(TAG, "Link Polling Period: %d", link_polling_period); + } + link_mode_config.mRxOnWhenIdle = this->poll_period == 0; + link_mode_config.mDeviceType = false; + link_mode_config.mNetworkData = false; +#endif + + if (otThreadSetLinkMode(esp_openthread_get_instance(), link_mode_config) != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to set OpenThread linkmode."); + } + link_mode_config = otThreadGetLinkMode(esp_openthread_get_instance()); + ESP_LOGD(TAG, "Link Mode Device Type: %s", link_mode_config.mDeviceType ? "true" : "false"); + ESP_LOGD(TAG, "Link Mode Network Data: %s", link_mode_config.mNetworkData ? "true" : "false"); + ESP_LOGD(TAG, "Link Mode RX On When Idle: %s", link_mode_config.mRxOnWhenIdle ? "true" : "false"); + // Run the main loop #if CONFIG_OPENTHREAD_CLI esp_openthread_cli_create_task(); diff --git a/tests/components/openthread/test.esp32-c6-idf.yaml b/tests/components/openthread/test.esp32-c6-idf.yaml index da5339fb39..9df63b2f29 100644 --- a/tests/components/openthread/test.esp32-c6-idf.yaml +++ b/tests/components/openthread/test.esp32-c6-idf.yaml @@ -2,7 +2,7 @@ network: enable_ipv6: true openthread: - device_type: FTD + device_type: MTD channel: 13 network_name: OpenThread-8f28 network_key: 0xdfd34f0f05cad978ec4e32b0413038ff @@ -11,3 +11,5 @@ openthread: pskc: 0xc23a76e98f1a6483639b1ac1271e2e27 mesh_local_prefix: fd53:145f:ed22:ad81::/64 force_dataset: true + use_address: open-thread-test.local + poll_period: 20sec From 5cdb891b580882842c420b458ddc141775162031 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 6 Nov 2025 19:21:58 -0600 Subject: [PATCH 416/526] [socket] Deduplicate IP formatting in LWIP raw TCP implementation (#11747) --- .../components/socket/lwip_raw_tcp_impl.cpp | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 4dedeffb6a..e0d93d8e2f 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -172,16 +172,7 @@ class LWIPRawImpl : public Socket { errno = ECONNRESET; return ""; } - char buffer[50] = {}; - if (IP_IS_V4_VAL(pcb_->remote_ip)) { - inet_ntoa_r(pcb_->remote_ip, buffer, sizeof(buffer)); - } -#if LWIP_IPV6 - else if (IP_IS_V6_VAL(pcb_->remote_ip)) { - inet6_ntoa_r(pcb_->remote_ip, buffer, sizeof(buffer)); - } -#endif - return std::string(buffer); + return this->format_ip_address_(pcb_->remote_ip); } int getsockname(struct sockaddr *name, socklen_t *addrlen) override { if (pcb_ == nullptr) { @@ -199,16 +190,7 @@ class LWIPRawImpl : public Socket { errno = ECONNRESET; return ""; } - char buffer[50] = {}; - if (IP_IS_V4_VAL(pcb_->local_ip)) { - inet_ntoa_r(pcb_->local_ip, buffer, sizeof(buffer)); - } -#if LWIP_IPV6 - else if (IP_IS_V6_VAL(pcb_->local_ip)) { - inet6_ntoa_r(pcb_->local_ip, buffer, sizeof(buffer)); - } -#endif - return std::string(buffer); + return this->format_ip_address_(pcb_->local_ip); } int getsockopt(int level, int optname, void *optval, socklen_t *optlen) override { if (pcb_ == nullptr) { @@ -499,6 +481,19 @@ class LWIPRawImpl : public Socket { } protected: + std::string format_ip_address_(const ip_addr_t &ip) { + char buffer[50] = {}; + if (IP_IS_V4_VAL(ip)) { + inet_ntoa_r(ip, buffer, sizeof(buffer)); + } +#if LWIP_IPV6 + else if (IP_IS_V6_VAL(ip)) { + inet6_ntoa_r(ip, buffer, sizeof(buffer)); + } +#endif + return std::string(buffer); + } + int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen) { if (family_ == AF_INET) { if (*addrlen < sizeof(struct sockaddr_in)) { From ba5fa7c10a97c0c62f4d6126f254e165d71cccc6 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Thu, 6 Nov 2025 20:22:50 -0500 Subject: [PATCH 417/526] [psram] Add option to disable ignore not found sdkconfig setting (#11411) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/const/__init__.py | 1 + esphome/components/psram/__init__.py | 6 +++++- tests/components/psram/test.esp32-s3-idf.yaml | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/esphome/components/const/__init__.py b/esphome/components/const/__init__.py index 0c22b2d27e..12a69551f5 100644 --- a/esphome/components/const/__init__.py +++ b/esphome/components/const/__init__.py @@ -9,6 +9,7 @@ BYTE_ORDER_BIG = "big_endian" CONF_COLOR_DEPTH = "color_depth" CONF_DRAW_ROUNDING = "draw_rounding" CONF_ENABLED = "enabled" +CONF_IGNORE_NOT_FOUND = "ignore_not_found" CONF_ON_RECEIVE = "on_receive" CONF_ON_STATE_CHANGE = "on_state_change" CONF_REQUEST_HEADERS = "request_headers" diff --git a/esphome/components/psram/__init__.py b/esphome/components/psram/__init__.py index df49e08879..11c238c1bf 100644 --- a/esphome/components/psram/__init__.py +++ b/esphome/components/psram/__init__.py @@ -2,6 +2,7 @@ import logging import textwrap import esphome.codegen as cg +from esphome.components.const import CONF_IGNORE_NOT_FOUND from esphome.components.esp32 import ( CONF_CPU_FREQUENCY, CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES, @@ -123,6 +124,7 @@ def get_config_schema(config): cv.Optional(CONF_ENABLE_ECC, default=False): cv.boolean, cv.Optional(CONF_SPEED, default=speeds[0]): cv.one_of(*speeds, upper=True), cv.Optional(CONF_DISABLED, default=False): cv.boolean, + cv.Optional(CONF_IGNORE_NOT_FOUND, default=True): cv.boolean, } )(config) @@ -147,7 +149,9 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_SPIRAM", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_USE", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_USE_CAPS_ALLOC", True) - add_idf_sdkconfig_option("CONFIG_SPIRAM_IGNORE_NOTFOUND", True) + add_idf_sdkconfig_option( + "CONFIG_SPIRAM_IGNORE_NOTFOUND", config[CONF_IGNORE_NOT_FOUND] + ) add_idf_sdkconfig_option(f"CONFIG_SPIRAM_MODE_{SDK_MODES[config[CONF_MODE]]}", True) diff --git a/tests/components/psram/test.esp32-s3-idf.yaml b/tests/components/psram/test.esp32-s3-idf.yaml index 75d4ee539c..548b8324d0 100644 --- a/tests/components/psram/test.esp32-s3-idf.yaml +++ b/tests/components/psram/test.esp32-s3-idf.yaml @@ -9,3 +9,4 @@ psram: mode: octal speed: 120MHz enable_ecc: true + ignore_not_found: false From 5d20e3a3b4ab7981d0d17a822493532eef267b75 Mon Sep 17 00:00:00 2001 From: philippderdiedas <56478008+philippderdiedas@users.noreply.github.com> Date: Fri, 7 Nov 2025 02:25:14 +0100 Subject: [PATCH 418/526] Add MCP3221 i2c A-D-Converter (#7764) --- CODEOWNERS | 1 + esphome/components/mcp3221/__init__.py | 1 + esphome/components/mcp3221/mcp3221_sensor.cpp | 31 ++++++++++++ esphome/components/mcp3221/mcp3221_sensor.h | 28 +++++++++++ esphome/components/mcp3221/sensor.py | 49 +++++++++++++++++++ tests/components/mcp3221/common.yaml | 6 +++ .../components/mcp3221/test.esp32-c3-idf.yaml | 4 ++ tests/components/mcp3221/test.esp32-idf.yaml | 4 ++ .../components/mcp3221/test.esp32-s3-idf.yaml | 4 ++ .../components/mcp3221/test.esp8266-ard.yaml | 4 ++ tests/components/mcp3221/test.rp2040-ard.yaml | 4 ++ 11 files changed, 136 insertions(+) create mode 100644 esphome/components/mcp3221/__init__.py create mode 100644 esphome/components/mcp3221/mcp3221_sensor.cpp create mode 100644 esphome/components/mcp3221/mcp3221_sensor.h create mode 100644 esphome/components/mcp3221/sensor.py create mode 100644 tests/components/mcp3221/common.yaml create mode 100644 tests/components/mcp3221/test.esp32-c3-idf.yaml create mode 100644 tests/components/mcp3221/test.esp32-idf.yaml create mode 100644 tests/components/mcp3221/test.esp32-s3-idf.yaml create mode 100644 tests/components/mcp3221/test.esp8266-ard.yaml create mode 100644 tests/components/mcp3221/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 4d458eceb8..7e785db451 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -290,6 +290,7 @@ esphome/components/mcp23x17_base/* @jesserockz esphome/components/mcp23xxx_base/* @jesserockz esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp3204/* @rsumner +esphome/components/mcp3221/* @philippderdiedas esphome/components/mcp4461/* @p1ngb4ck esphome/components/mcp4728/* @berfenger esphome/components/mcp47a1/* @jesserockz diff --git a/esphome/components/mcp3221/__init__.py b/esphome/components/mcp3221/__init__.py new file mode 100644 index 0000000000..677bb78c35 --- /dev/null +++ b/esphome/components/mcp3221/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@philippderdiedas"] diff --git a/esphome/components/mcp3221/mcp3221_sensor.cpp b/esphome/components/mcp3221/mcp3221_sensor.cpp new file mode 100644 index 0000000000..c04b1c0b93 --- /dev/null +++ b/esphome/components/mcp3221/mcp3221_sensor.cpp @@ -0,0 +1,31 @@ +#include "mcp3221_sensor.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mcp3221 { + +static const char *const TAG = "mcp3221"; + +float MCP3221Sensor::sample() { + uint8_t data[2]; + if (this->read(data, 2) != i2c::ERROR_OK) { + ESP_LOGW(TAG, "Read failed"); + this->status_set_warning(); + return NAN; + } + this->status_clear_warning(); + + uint16_t value = encode_uint16(data[0], data[1]); + float voltage = value * this->reference_voltage_ / 4096.0f; + + return voltage; +} + +void MCP3221Sensor::update() { + float v = this->sample(); + this->publish_state(v); +} + +} // namespace mcp3221 +} // namespace esphome diff --git a/esphome/components/mcp3221/mcp3221_sensor.h b/esphome/components/mcp3221/mcp3221_sensor.h new file mode 100644 index 0000000000..c83caccabf --- /dev/null +++ b/esphome/components/mcp3221/mcp3221_sensor.h @@ -0,0 +1,28 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/voltage_sampler/voltage_sampler.h" + +#include + +namespace esphome { +namespace mcp3221 { + +class MCP3221Sensor : public sensor::Sensor, + public PollingComponent, + public voltage_sampler::VoltageSampler, + public i2c::I2CDevice { + public: + void set_reference_voltage(float reference_voltage) { this->reference_voltage_ = reference_voltage; } + void update() override; + float sample() override; + + protected: + float reference_voltage_; +}; + +} // namespace mcp3221 +} // namespace esphome diff --git a/esphome/components/mcp3221/sensor.py b/esphome/components/mcp3221/sensor.py new file mode 100644 index 0000000000..993876c2c8 --- /dev/null +++ b/esphome/components/mcp3221/sensor.py @@ -0,0 +1,49 @@ +import esphome.codegen as cg +from esphome.components import i2c, sensor, voltage_sampler +import esphome.config_validation as cv +from esphome.const import ( + CONF_REFERENCE_VOLTAGE, + DEVICE_CLASS_VOLTAGE, + ICON_SCALE, + STATE_CLASS_MEASUREMENT, + UNIT_VOLT, +) + +AUTO_LOAD = ["voltage_sampler"] +DEPENDENCIES = ["i2c"] + + +mcp3221_ns = cg.esphome_ns.namespace("mcp3221") +MCP3221Sensor = mcp3221_ns.class_( + "MCP3221Sensor", + sensor.Sensor, + voltage_sampler.VoltageSampler, + cg.PollingComponent, + i2c.I2CDevice, +) + + +CONFIG_SCHEMA = ( + sensor.sensor_schema( + MCP3221Sensor, + icon=ICON_SCALE, + accuracy_decimals=2, + state_class=STATE_CLASS_MEASUREMENT, + device_class=DEVICE_CLASS_VOLTAGE, + unit_of_measurement=UNIT_VOLT, + ) + .extend( + { + cv.Optional(CONF_REFERENCE_VOLTAGE, default="3.3V"): cv.voltage, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x48)) +) + + +async def to_code(config): + var = await sensor.new_sensor(config) + cg.add(var.set_reference_voltage(config[CONF_REFERENCE_VOLTAGE])) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) diff --git a/tests/components/mcp3221/common.yaml b/tests/components/mcp3221/common.yaml new file mode 100644 index 0000000000..cc3eadbf4f --- /dev/null +++ b/tests/components/mcp3221/common.yaml @@ -0,0 +1,6 @@ +sensor: + - platform: mcp3221 + id: test_id + name: voltage + i2c_id: i2c_bus + reference_voltage: 3.3V diff --git a/tests/components/mcp3221/test.esp32-c3-idf.yaml b/tests/components/mcp3221/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..9990d96d29 --- /dev/null +++ b/tests/components/mcp3221/test.esp32-c3-idf.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/mcp3221/test.esp32-idf.yaml b/tests/components/mcp3221/test.esp32-idf.yaml new file mode 100644 index 0000000000..b47e39c389 --- /dev/null +++ b/tests/components/mcp3221/test.esp32-idf.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/mcp3221/test.esp32-s3-idf.yaml b/tests/components/mcp3221/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..0fd8684a2c --- /dev/null +++ b/tests/components/mcp3221/test.esp32-s3-idf.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/mcp3221/test.esp8266-ard.yaml b/tests/components/mcp3221/test.esp8266-ard.yaml new file mode 100644 index 0000000000..4a98b9388a --- /dev/null +++ b/tests/components/mcp3221/test.esp8266-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + +<<: !include common.yaml diff --git a/tests/components/mcp3221/test.rp2040-ard.yaml b/tests/components/mcp3221/test.rp2040-ard.yaml new file mode 100644 index 0000000000..319a7c71a6 --- /dev/null +++ b/tests/components/mcp3221/test.rp2040-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + +<<: !include common.yaml From d0b399d77167d5ab48e5aeb8fd275eef151ccfb5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 6 Nov 2025 20:44:01 -0600 Subject: [PATCH 419/526] [ci] Reduce release time by removing 468 redundant ESP32-C3 IDF tests (#11737) --- .../components/a01nyub/test.esp32-c3-idf.yaml | 8 ------- .../components/a02yyuw/test.esp32-c3-idf.yaml | 8 ------- tests/components/a4988/test.esp32-c3-idf.yaml | 6 ------ .../absolute_humidity/test.esp32-c3-idf.yaml | 1 - .../adc128s102/test.esp32-c3-idf.yaml | 7 ------- .../components/ade7880/test.esp32-c3-idf.yaml | 9 -------- .../ade7953_i2c/test.esp32-c3-idf.yaml | 7 ------- .../ade7953_spi/test.esp32-c3-idf.yaml | 7 ------- .../components/ads1115/test.esp32-c3-idf.yaml | 4 ---- tests/components/ags10/test.esp32-c3-idf.yaml | 4 ---- tests/components/aht10/test.esp32-c3-idf.yaml | 4 ---- .../components/aic3204/test.esp32-c3-idf.yaml | 4 ---- .../test.esp32-c3-idf.yaml | 4 ---- .../test.esp32-c3-idf.yaml | 4 ---- .../test.esp32-c3-idf.yaml | 1 - .../components/alpha3/test.esp32-c3-idf.yaml | 4 ---- .../components/am2315c/test.esp32-c3-idf.yaml | 4 ---- .../components/am2320/test.esp32-c3-idf.yaml | 4 ---- tests/components/am43/test.esp32-c3-idf.yaml | 4 ---- .../analog_threshold/test.esp32-c3-idf.yaml | 1 - .../animation/test.esp32-c3-idf.yaml | 13 ------------ tests/components/anova/test.esp32-c3-idf.yaml | 4 ---- .../apds9306/test.esp32-c3-idf.yaml | 4 ---- .../apds9960/test.esp32-c3-idf.yaml | 4 ---- tests/components/api/test.esp32-c3-idf.yaml | 5 ----- .../as3935_i2c/test.esp32-c3-idf.yaml | 7 ------- .../as3935_spi/test.esp32-c3-idf.yaml | 7 ------- .../components/as5600/test.esp32-c3-idf.yaml | 7 ------- .../components/as7341/test.esp32-c3-idf.yaml | 4 ---- .../components/at581x/test.esp32-c3-idf.yaml | 4 ---- .../atc_mithermometer/test.esp32-c3-idf.yaml | 4 ---- .../atm90e26/test.esp32-c3-idf.yaml | 6 ------ .../atm90e32/test.esp32-c3-idf.yaml | 6 ------ .../axs15231/test.esp32-c3-idf.yaml | 4 ---- .../b_parasite/test.esp32-c3-idf.yaml | 4 ---- .../bang_bang/test.esp32-c3-idf.yaml | 1 - .../components/bedjet/test.esp32-c3-idf.yaml | 4 ---- .../components/bh1750/test.esp32-c3-idf.yaml | 4 ---- .../bh1900nux/test.esp32-c3-idf.yaml | 4 ---- .../binary_sensor/test.esp32-c3-idf.yaml | 2 -- .../binary_sensor_map/test.esp32-c3-idf.yaml | 1 - .../components/bl0906/test.esp32-c3-idf.yaml | 4 ---- .../components/bl0939/test.esp32-c3-idf.yaml | 5 ----- .../components/bl0940/test.esp32-c3-idf.yaml | 5 ----- .../components/bl0942/test.esp32-c3-idf.yaml | 5 ----- .../ble_client/test.esp32-c3-idf.yaml | 4 ---- .../ble_presence/test.esp32-c3-idf.yaml | 4 ---- .../ble_rssi/test.esp32-c3-idf.yaml | 4 ---- .../ble_scanner/test.esp32-c3-idf.yaml | 4 ---- .../bme280_i2c/test.esp32-c3-idf.yaml | 4 ---- .../bme280_spi/test.esp32-c3-idf.yaml | 6 ------ .../components/bme680/test.esp32-c3-idf.yaml | 4 ---- .../bme68x_bsec2_i2c/test.esp32-c3-idf.yaml | 4 ---- .../components/bmi160/test.esp32-c3-idf.yaml | 4 ---- .../components/bmp085/test.esp32-c3-idf.yaml | 4 ---- .../bmp280_i2c/test.esp32-c3-idf.yaml | 4 ---- .../bmp280_spi/test.esp32-c3-idf.yaml | 6 ------ .../bmp3xx_i2c/test.esp32-c3-idf.yaml | 4 ---- .../bmp3xx_spi/test.esp32-c3-idf.yaml | 6 ------ .../components/bmp581/test.esp32-c3-idf.yaml | 4 ---- .../bp1658cj/test.esp32-c3-idf.yaml | 5 ----- .../components/bp5758d/test.esp32-c3-idf.yaml | 5 ----- .../components/button/test.esp32-c3-idf.yaml | 1 - .../bytebuffer/test.esp32-c3-idf.yaml | 1 - .../components/cap1188/test.esp32-c3-idf.yaml | 7 ------- .../captive_portal/test.esp32-c3-idf.yaml | 1 - .../components/ccs811/test.esp32-c3-idf.yaml | 4 ---- .../cd74hc4067/test.esp32-c3-idf.yaml | 8 ------- .../components/ch422g/test.esp32-c3-idf.yaml | 4 ---- .../components/chsc6x/test.esp32-c3-idf.yaml | 20 ------------------ .../climate_ir_lg/test.esp32-c3-idf.yaml | 4 ---- .../components/cm1106/test.esp32-c3-idf.yaml | 5 ----- tests/components/color/test.esp32-c3-idf.yaml | 1 - .../color_temperature/test.esp32-c3-idf.yaml | 6 ------ .../combination/test.esp32-c3-idf.yaml | 1 - .../components/coolix/test.esp32-c3-idf.yaml | 4 ---- tests/components/copy/test.esp32-c3-idf.yaml | 5 ----- .../components/cs5460a/test.esp32-c3-idf.yaml | 6 ------ .../components/cse7761/test.esp32-c3-idf.yaml | 5 ----- .../components/cse7766/test.esp32-c3-idf.yaml | 5 ----- .../components/cst226/test.esp32-c3-idf.yaml | 11 ---------- .../components/cst816/test.esp32-c3-idf.yaml | 11 ---------- .../ct_clamp/test.esp32-c3-idf.yaml | 4 ---- .../current_based/test.esp32-c3-idf.yaml | 7 ------- tests/components/cwww/test.esp32-c3-idf.yaml | 17 --------------- .../components/dac7678/test.esp32-c3-idf.yaml | 4 ---- .../daikin_brc/test.esp32-c3-idf.yaml | 4 ---- .../dallas_temp/test.esp32-c3-idf.yaml | 7 ------- .../daly_bms/test.esp32-c3-idf.yaml | 5 ----- tests/components/debug/test.esp32-c3-idf.yaml | 1 - .../delonghi/test.esp32-c3-idf.yaml | 4 ---- .../dfplayer/test.esp32-c3-idf.yaml | 5 ----- .../dfrobot_sen0395/test.esp32-c3-idf.yaml | 5 ----- tests/components/dht/test.esp32-c3-idf.yaml | 1 - tests/components/dht12/test.esp32-c3-idf.yaml | 4 ---- .../components/dps310/test.esp32-c3-idf.yaml | 4 ---- .../components/ds1307/test.esp32-c3-idf.yaml | 4 ---- .../components/ds2484/test.esp32-c3-idf.yaml | 4 ---- .../duty_cycle/test.esp32-c3-idf.yaml | 1 - .../duty_time/test.esp32-c3-idf.yaml | 1 - tests/components/e131/test.esp32-c3-idf.yaml | 5 ----- tests/components/ee895/test.esp32-c3-idf.yaml | 4 ---- .../ektf2232/test.esp32-c3-idf.yaml | 9 -------- .../components/emc2101/test.esp32-c3-idf.yaml | 4 ---- .../components/endstop/test.esp32-c3-idf.yaml | 1 - .../ens160_i2c/test.esp32-c3-idf.yaml | 4 ---- .../ens160_spi/test.esp32-c3-idf.yaml | 6 ------ .../components/ens210/test.esp32-c3-idf.yaml | 4 ---- .../components/es7210/test.esp32-c3-idf.yaml | 4 ---- .../components/es7243e/test.esp32-c3-idf.yaml | 4 ---- .../components/es8156/test.esp32-c3-idf.yaml | 4 ---- .../components/es8311/test.esp32-c3-idf.yaml | 4 ---- .../components/es8388/test.esp32-c3-idf.yaml | 4 ---- .../components/esphome/test.esp32-c3-idf.yaml | 1 - tests/components/event/test.esp32-c3-idf.yaml | 1 - .../test.esp32-c3-idf.yaml | 4 ---- .../test.esp32-c3-idf.yaml | 1 - tests/components/ezo/test.esp32-c3-idf.yaml | 4 ---- .../components/ezo_pmp/test.esp32-c3-idf.yaml | 4 ---- .../factory_reset/test.esp32-c3-idf.yaml | 1 - .../feedback/test.esp32-c3-idf.yaml | 1 - .../fingerprint_grow/test.esp32-c3-idf.yaml | 6 ------ tests/components/font/test.esp32-c3-idf.yaml | 7 ------- .../components/fs3000/test.esp32-c3-idf.yaml | 4 ---- .../components/ft5x06/test.esp32-c3-idf.yaml | 7 ------- .../components/ft63x6/test.esp32-c3-idf.yaml | 8 ------- .../fujitsu_general/test.esp32-c3-idf.yaml | 4 ---- tests/components/gcja5/test.esp32-c3-idf.yaml | 4 ---- .../gl_r01_i2c/test.esp32-c3-idf.yaml | 4 ---- .../components/globals/test.esp32-c3-idf.yaml | 1 - .../gp2y1010au0f/test.esp32-c3-idf.yaml | 5 ----- .../components/gp8403/test.esp32-c3-idf.yaml | 4 ---- tests/components/gps/test.esp32-c3-idf.yaml | 5 ----- tests/components/graph/test.esp32-c3-idf.yaml | 7 ------- .../test.esp32-c3-idf.yaml | 7 ------- tests/components/gree/test.esp32-c3-idf.yaml | 4 ---- .../grove_gas_mc_v2/test.esp32-c3-idf.yaml | 4 ---- .../grove_tb6612fng/test.esp32-c3-idf.yaml | 4 ---- .../growatt_solar/test.esp32-c3-idf.yaml | 6 ------ tests/components/gt911/test.esp32-c3-idf.yaml | 9 -------- tests/components/haier/test.esp32-c3-idf.yaml | 5 ----- .../havells_solar/test.esp32-c3-idf.yaml | 6 ------ .../components/hbridge/test.esp32-c3-idf.yaml | 17 --------------- .../components/hdc1080/test.esp32-c3-idf.yaml | 4 ---- .../components/hdc2010/test.esp32-c3-idf.yaml | 4 ---- tests/components/he60r/test.esp32-c3-idf.yaml | 4 ---- .../hitachi_ac344/test.esp32-c3-idf.yaml | 4 ---- .../hitachi_ac424/test.esp32-c3-idf.yaml | 4 ---- .../components/hlw8012/test.esp32-c3-idf.yaml | 6 ------ .../components/hm3301/test.esp32-c3-idf.yaml | 4 ---- .../hmc5883l/test.esp32-c3-idf.yaml | 4 ---- .../homeassistant/test.esp32-c3-idf.yaml | 2 -- .../honeywell_hih_i2c/test.esp32-c3-idf.yaml | 4 ---- .../honeywellabp/test.esp32-c3-idf.yaml | 6 ------ .../honeywellabp2_i2c/test.esp32-c3-idf.yaml | 4 ---- .../hrxl_maxsonar_wr/test.esp32-c3-idf.yaml | 5 ----- .../components/hte501/test.esp32-c3-idf.yaml | 4 ---- .../http_request/test.esp32-c3-idf.yaml | 4 ---- .../components/htu21d/test.esp32-c3-idf.yaml | 4 ---- .../components/htu31d/test.esp32-c3-idf.yaml | 4 ---- tests/components/hx711/test.esp32-c3-idf.yaml | 5 ----- .../hydreon_rgxx/test.esp32-c3-idf.yaml | 5 ----- .../components/hyt271/test.esp32-c3-idf.yaml | 4 ---- tests/components/i2c/test.esp32-c3-idf.yaml | 4 ---- .../i2c_device/test.esp32-c3-idf.yaml | 4 ---- .../components/iaqcore/test.esp32-c3-idf.yaml | 4 ---- .../components/ili9xxx/test.esp32-c3-idf.yaml | 11 ---------- .../components/ina219/test.esp32-c3-idf.yaml | 4 ---- .../components/ina226/test.esp32-c3-idf.yaml | 4 ---- .../components/ina260/test.esp32-c3-idf.yaml | 4 ---- .../ina2xx_i2c/test.esp32-c3-idf.yaml | 4 ---- .../ina2xx_spi/test.esp32-c3-idf.yaml | 6 ------ .../components/ina3221/test.esp32-c3-idf.yaml | 4 ---- .../test.esp32-c3-idf.yaml | 4 ---- .../integration/test.esp32-c3-idf.yaml | 4 ---- .../interval/test.esp32-c3-idf.yaml | 1 - .../jsn_sr04t/test.esp32-c3-idf.yaml | 5 ----- .../key_collector/test.esp32-c3-idf.yaml | 7 ------- .../kmeteriso/test.esp32-c3-idf.yaml | 4 ---- .../components/kuntze/test.esp32-c3-idf.yaml | 6 ------ .../lc709203f/test.esp32-c3-idf.yaml | 4 ---- .../lcd_gpio/test.esp32-c3-idf.yaml | 9 -------- .../lcd_menu/test.esp32-c3-idf.yaml | 9 -------- .../lcd_pcf8574/test.esp32-c3-idf.yaml | 4 ---- .../components/ld2410/test.esp32-c3-idf.yaml | 8 ------- .../components/ld2412/test.esp32-c3-idf.yaml | 8 ------- .../components/ld2420/test.esp32-c3-idf.yaml | 5 ----- .../components/ld2450/test.esp32-c3-idf.yaml | 8 ------- tests/components/light/test.esp32-c3-idf.yaml | 21 ------------------- .../lilygo_t5_47/test.esp32-c3-idf.yaml | 8 ------- tests/components/lm75b/test.esp32-c3-idf.yaml | 4 ---- tests/components/lock/test.esp32-c3-idf.yaml | 1 - tests/components/lps22/test.esp32-c3-idf.yaml | 4 ---- .../components/ltr390/test.esp32-c3-idf.yaml | 4 ---- .../components/ltr501/test.esp32-c3-idf.yaml | 4 ---- .../ltr_als_ps/test.esp32-c3-idf.yaml | 4 ---- .../m5stack_8angle/test.esp32-c3-idf.yaml | 4 ---- .../components/mapping/test.esp32-c3-idf.yaml | 13 ------------ .../matrix_keypad/test.esp32-c3-idf.yaml | 15 ------------- .../max17043/test.esp32-c3-idf.yaml | 4 ---- .../max31855/test.esp32-c3-idf.yaml | 6 ------ .../max31856/test.esp32-c3-idf.yaml | 6 ------ .../max31865/test.esp32-c3-idf.yaml | 6 ------ .../max44009/test.esp32-c3-idf.yaml | 4 ---- .../components/max6675/test.esp32-c3-idf.yaml | 6 ------ .../components/max6956/test.esp32-c3-idf.yaml | 4 ---- .../components/max7219/test.esp32-c3-idf.yaml | 6 ------ .../max7219digit/test.esp32-c3-idf.yaml | 6 ------ .../components/max9611/test.esp32-c3-idf.yaml | 4 ---- .../mcp23008/test.esp32-c3-idf.yaml | 4 ---- .../mcp23016/test.esp32-c3-idf.yaml | 4 ---- .../mcp23017/test.esp32-c3-idf.yaml | 4 ---- .../mcp23s08/test.esp32-c3-idf.yaml | 7 ------- .../mcp23s17/test.esp32-c3-idf.yaml | 7 ------- .../components/mcp2515/test.esp32-c3-idf.yaml | 6 ------ .../components/mcp3008/test.esp32-c3-idf.yaml | 6 ------ .../components/mcp3204/test.esp32-c3-idf.yaml | 6 ------ .../components/mcp4461/test.esp32-c3-idf.yaml | 4 ---- .../components/mcp4725/test.esp32-c3-idf.yaml | 4 ---- .../components/mcp4728/test.esp32-c3-idf.yaml | 4 ---- .../components/mcp47a1/test.esp32-c3-idf.yaml | 4 ---- .../components/mcp9600/test.esp32-c3-idf.yaml | 4 ---- .../components/mcp9808/test.esp32-c3-idf.yaml | 4 ---- .../mdns/test-enabled.esp32-c3-idf.yaml | 1 - tests/components/mhz19/test.esp32-c3-idf.yaml | 5 ----- .../micronova/test.esp32-c3-idf.yaml | 7 ------- .../microphone/test.esp32-c3-idf.yaml | 7 ------- .../mics_4514/test.esp32-c3-idf.yaml | 4 ---- .../midea_ir/test.esp32-c3-idf.yaml | 4 ---- .../mitsubishi/test.esp32-c3-idf.yaml | 4 ---- tests/components/mixer/test.esp32-c3-idf.yaml | 7 ------- .../mlx90393/test.esp32-c3-idf.yaml | 4 ---- .../mlx90614/test.esp32-c3-idf.yaml | 4 ---- .../components/mmc5603/test.esp32-c3-idf.yaml | 4 ---- .../components/mmc5983/test.esp32-c3-idf.yaml | 4 ---- .../components/modbus/test.esp32-c3-idf.yaml | 6 ------ .../modbus_controller/test.esp32-c3-idf.yaml | 4 ---- .../monochromatic/test.esp32-c3-idf.yaml | 5 ----- .../mopeka_ble/test.esp32-c3-idf.yaml | 4 ---- .../mopeka_pro_check/test.esp32-c3-idf.yaml | 4 ---- .../mopeka_std_check/test.esp32-c3-idf.yaml | 4 ---- .../mpl3115a2/test.esp32-c3-idf.yaml | 4 ---- .../components/mpr121/test.esp32-c3-idf.yaml | 8 ------- .../components/mpu6050/test.esp32-c3-idf.yaml | 4 ---- .../components/mpu6886/test.esp32-c3-idf.yaml | 4 ---- tests/components/mqtt/test.esp32-c3-idf.yaml | 3 --- .../mqtt_subscribe/test.esp32-c3-idf.yaml | 1 - .../components/ms5611/test.esp32-c3-idf.yaml | 8 ------- .../components/msa3xx/test.esp32-c3-idf.yaml | 4 ---- .../components/my9231/test.esp32-c3-idf.yaml | 1 - .../components/nau7802/test.esp32-c3-idf.yaml | 4 ---- .../network/test-ipv6.esp32-c3-idf.yaml | 4 ---- .../components/network/test.esp32-c3-idf.yaml | 1 - .../components/nextion/test.esp32-c3-idf.yaml | 7 ------- .../components/noblex/test.esp32-c3-idf.yaml | 5 ----- tests/components/ntc/test.esp32-c3-idf.yaml | 4 ---- .../components/opt3001/test.esp32-c3-idf.yaml | 4 ---- tests/components/ota/test.esp32-c3-idf.yaml | 1 - .../components/output/test.esp32-c3-idf.yaml | 5 ----- .../packet_transport/test.esp32-c3-idf.yaml | 4 ---- .../pca6416a/test.esp32-c3-idf.yaml | 4 ---- .../components/pca9554/test.esp32-c3-idf.yaml | 4 ---- .../components/pca9685/test.esp32-c3-idf.yaml | 4 ---- .../components/pcd8544/test.esp32-c3-idf.yaml | 8 ------- .../pcf85063/test.esp32-c3-idf.yaml | 4 ---- .../components/pcf8563/test.esp32-c3-idf.yaml | 4 ---- .../components/pcf8574/test.esp32-c3-idf.yaml | 4 ---- tests/components/pid/test.esp32-c3-idf.yaml | 1 - .../pipsolar/test.esp32-c3-idf.yaml | 5 ----- .../components/pm1006/test.esp32-c3-idf.yaml | 5 ----- .../components/pm2005/test.esp32-c3-idf.yaml | 4 ---- .../pmsa003i/test.esp32-c3-idf.yaml | 4 ---- .../components/pmsx003/test.esp32-c3-idf.yaml | 5 ----- .../components/pmwcs3/test.esp32-c3-idf.yaml | 4 ---- .../pn532_i2c/test.esp32-c3-idf.yaml | 4 ---- .../pn532_spi/test.esp32-c3-idf.yaml | 6 ------ .../pn7150_i2c/test.esp32-c3-idf.yaml | 8 ------- .../pn7160_i2c/test.esp32-c3-idf.yaml | 8 ------- .../pn7160_spi/test.esp32-c3-idf.yaml | 8 ------- .../power_supply/test.esp32-c3-idf.yaml | 1 - .../prometheus/test.esp32-c3-idf.yaml | 7 ------- .../pulse_meter/test.esp32-c3-idf.yaml | 1 - .../pulse_width/test.esp32-c3-idf.yaml | 1 - .../pvvx_mithermometer/test.esp32-c3-idf.yaml | 4 ---- .../pylontech/test.esp32-c3-idf.yaml | 5 ----- .../pzem004t/test.esp32-c3-idf.yaml | 5 ----- .../components/pzemac/test.esp32-c3-idf.yaml | 5 ----- .../components/pzemdc/test.esp32-c3-idf.yaml | 5 ----- .../qmc5883l/test.esp32-c3-idf.yaml | 7 ------- .../components/qmp6988/test.esp32-c3-idf.yaml | 4 ---- .../components/qr_code/test.esp32-c3-idf.yaml | 8 ------- .../qwiic_pir/test.esp32-c3-idf.yaml | 4 ---- .../radon_eye_ble/test.esp32-c3-idf.yaml | 4 ---- .../radon_eye_rd200/test.esp32-c3-idf.yaml | 4 ---- .../rc522_i2c/test.esp32-c3-idf.yaml | 4 ---- .../rc522_spi/test.esp32-c3-idf.yaml | 6 ------ .../components/rdm6300/test.esp32-c3-idf.yaml | 5 ----- .../resampler/test.esp32-c3-idf.yaml | 7 ------- .../resistance/test.esp32-c3-idf.yaml | 4 ---- .../components/restart/test.esp32-c3-idf.yaml | 1 - .../rf_bridge/test.esp32-c3-idf.yaml | 4 ---- tests/components/rgb/test.esp32-c3-idf.yaml | 7 ------- tests/components/rgbct/test.esp32-c3-idf.yaml | 9 -------- tests/components/rgbw/test.esp32-c3-idf.yaml | 8 ------- tests/components/rgbww/test.esp32-c3-idf.yaml | 9 -------- .../rotary_encoder/test.esp32-c3-idf.yaml | 6 ------ tests/components/rtttl/test.esp32-c3-idf.yaml | 5 ----- .../ruuvi_ble/test.esp32-c3-idf.yaml | 4 ---- .../ruuvitag/test.esp32-c3-idf.yaml | 4 ---- .../safe_mode/test-enabled.esp32-c3-idf.yaml | 1 - tests/components/scd30/test.esp32-c3-idf.yaml | 4 ---- tests/components/scd4x/test.esp32-c3-idf.yaml | 4 ---- .../components/script/test.esp32-c3-idf.yaml | 1 - .../sdm_meter/test.esp32-c3-idf.yaml | 5 ----- tests/components/sdp3x/test.esp32-c3-idf.yaml | 4 ---- .../components/sds011/test.esp32-c3-idf.yaml | 5 ----- .../seeed_mr24hpc1/test.esp32-c3-idf.yaml | 4 ---- .../seeed_mr60bha2/test.esp32-c3-idf.yaml | 4 ---- .../seeed_mr60fda2/test.esp32-c3-idf.yaml | 4 ---- .../selec_meter/test.esp32-c3-idf.yaml | 7 ------- .../components/sen0321/test.esp32-c3-idf.yaml | 4 ---- .../sen21231/test.esp32-c3-idf.yaml | 4 ---- tests/components/sen5x/test.esp32-c3-idf.yaml | 4 ---- .../senseair/test.esp32-c3-idf.yaml | 5 ----- tests/components/servo/test.esp32-c3-idf.yaml | 5 ----- tests/components/sfa30/test.esp32-c3-idf.yaml | 4 ---- tests/components/sgp30/test.esp32-c3-idf.yaml | 4 ---- tests/components/sgp4x/test.esp32-c3-idf.yaml | 4 ---- .../components/sht3xd/test.esp32-c3-idf.yaml | 4 ---- tests/components/sht4x/test.esp32-c3-idf.yaml | 4 ---- tests/components/shtcx/test.esp32-c3-idf.yaml | 4 ---- .../shutdown/test.esp32-c3-idf.yaml | 1 - .../components/sim800l/test.esp32-c3-idf.yaml | 5 ----- .../slow_pwm/test.esp32-c3-idf.yaml | 1 - .../components/sm16716/test.esp32-c3-idf.yaml | 5 ----- .../components/sm2135/test.esp32-c3-idf.yaml | 5 ----- .../components/sm2235/test.esp32-c3-idf.yaml | 5 ----- .../components/sm2335/test.esp32-c3-idf.yaml | 5 ----- .../components/sm300d2/test.esp32-c3-idf.yaml | 5 ----- tests/components/sml/test.esp32-c3-idf.yaml | 5 ----- .../components/smt100/test.esp32-c3-idf.yaml | 5 ----- .../sn74hc165/test.esp32-c3-idf.yaml | 7 ------- .../sn74hc595/test.esp32-c3-idf.yaml | 12 ----------- tests/components/sntp/test.esp32-c3-idf.yaml | 1 - .../sonoff_d1/test.esp32-c3-idf.yaml | 4 ---- .../sound_level/test.esp32-c3-idf.yaml | 6 ------ .../speaker/audio_dac.esp32-c3-idf.yaml | 10 --------- .../components/speaker/test.esp32-c3-idf.yaml | 12 ----------- tests/components/speed/test.esp32-c3-idf.yaml | 5 ----- .../spi_device/test.esp32-c3-idf.yaml | 4 ---- .../spi_led_strip/test.esp32-c3-idf.yaml | 4 ---- .../sprinkler/test.esp32-c3-idf.yaml | 1 - tests/components/sps30/test.esp32-c3-idf.yaml | 4 ---- .../ssd1306_i2c/test.esp32-c3-idf.yaml | 7 ------- .../ssd1306_spi/test.esp32-c3-idf.yaml | 8 ------- .../ssd1322_spi/test.esp32-c3-idf.yaml | 8 ------- .../ssd1325_spi/test.esp32-c3-idf.yaml | 8 ------- .../ssd1327_i2c/test.esp32-c3-idf.yaml | 7 ------- .../ssd1327_spi/test.esp32-c3-idf.yaml | 8 ------- .../ssd1331_spi/test.esp32-c3-idf.yaml | 8 ------- .../ssd1351_spi/test.esp32-c3-idf.yaml | 8 ------- .../st7567_i2c/test.esp32-c3-idf.yaml | 7 ------- .../st7567_spi/test.esp32-c3-idf.yaml | 8 ------- .../components/st7735/test.esp32-c3-idf.yaml | 8 ------- .../components/st7789v/test.esp32-c3-idf.yaml | 10 --------- .../components/st7920/test.esp32-c3-idf.yaml | 6 ------ .../components/statsD/test.esp32-c3-idf.yaml | 2 -- .../components/status/test.esp32-c3-idf.yaml | 1 - .../status_led/test.esp32-c3-idf.yaml | 1 - .../components/stepper/test.esp32-c3-idf.yaml | 1 - tests/components/sts3x/test.esp32-c3-idf.yaml | 4 ---- tests/components/sun/test.esp32-c3-idf.yaml | 1 - .../sun_gtil2/test.esp32-c3-idf.yaml | 5 ----- .../components/switch/test.esp32-c3-idf.yaml | 2 -- .../components/sx126x/test.esp32-c3-idf.yaml | 10 --------- .../components/sx127x/test.esp32-c3-idf.yaml | 8 ------- .../components/sx1509/test.esp32-c3-idf.yaml | 4 ---- .../components/syslog/test.esp32-c3-idf.yaml | 1 - tests/components/t6615/test.esp32-c3-idf.yaml | 4 ---- tests/components/tc74/test.esp32-c3-idf.yaml | 4 ---- .../tca9548a/test.esp32-c3-idf.yaml | 4 ---- .../components/tca9555/test.esp32-c3-idf.yaml | 4 ---- .../components/tcl112/test.esp32-c3-idf.yaml | 4 ---- .../tcs34725/test.esp32-c3-idf.yaml | 4 ---- .../components/tee501/test.esp32-c3-idf.yaml | 4 ---- .../teleinfo/test.esp32-c3-idf.yaml | 5 ----- .../template/test.esp32-c3-idf.yaml | 2 -- .../thermostat/test.esp32-c3-idf.yaml | 1 - tests/components/time/test.esp32-c3-idf.yaml | 1 - .../time_based/test.esp32-c3-idf.yaml | 1 - .../tlc59208f/test.esp32-c3-idf.yaml | 4 ---- .../components/tlc5947/test.esp32-c3-idf.yaml | 7 ------- .../components/tlc5971/test.esp32-c3-idf.yaml | 6 ------ .../components/tm1621/test.esp32-c3-idf.yaml | 7 ------- .../components/tm1637/test.esp32-c3-idf.yaml | 5 ----- .../components/tm1638/test.esp32-c3-idf.yaml | 1 - .../components/tm1651/test.esp32-c3-idf.yaml | 1 - .../components/tmp102/test.esp32-c3-idf.yaml | 4 ---- .../components/tmp1075/test.esp32-c3-idf.yaml | 4 ---- .../components/tmp117/test.esp32-c3-idf.yaml | 4 ---- .../tof10120/test.esp32-c3-idf.yaml | 4 ---- .../tormatic/test.esp32-c3-idf.yaml | 5 ----- .../components/toshiba/test.esp32-c3-idf.yaml | 4 ---- .../toshiba/test_ras2819t.esp32-c3-idf.yaml | 5 ----- .../total_daily_energy/test.esp32-c3-idf.yaml | 6 ------ .../components/tsl2561/test.esp32-c3-idf.yaml | 4 ---- .../components/tsl2591/test.esp32-c3-idf.yaml | 4 ---- .../components/tt21100/test.esp32-c3-idf.yaml | 9 -------- .../ttp229_bsf/test.esp32-c3-idf.yaml | 8 ------- .../ttp229_lsf/test.esp32-c3-idf.yaml | 4 ---- tests/components/tuya/test.esp32-c3-idf.yaml | 6 ------ tests/components/tx20/test.esp32-c3-idf.yaml | 1 - tests/components/udp/test.esp32-c3-idf.yaml | 4 ---- .../ufire_ec/test.esp32-c3-idf.yaml | 4 ---- .../ufire_ise/test.esp32-c3-idf.yaml | 4 ---- .../components/uln2003/test.esp32-c3-idf.yaml | 7 ------- .../ultrasonic/test.esp32-c3-idf.yaml | 1 - .../uponor_smatrix/test.esp32-c3-idf.yaml | 5 ----- .../components/uptime/test.esp32-c3-idf.yaml | 1 - tests/components/vbus/test.esp32-c3-idf.yaml | 4 ---- .../veml3235/test.esp32-c3-idf.yaml | 4 ---- .../veml7700/test.esp32-c3-idf.yaml | 4 ---- .../components/version/test.esp32-c3-idf.yaml | 1 - .../components/vl53l0x/test.esp32-c3-idf.yaml | 4 ---- .../voice_assistant/test.esp32-c3-idf.yaml | 8 ------- .../wake_on_lan/test.esp32-c3-idf.yaml | 4 ---- .../waveshare_epaper/test.esp32-c3-idf.yaml | 9 -------- .../web_server/test.esp32-c3-idf.yaml | 1 - .../whirlpool/test.esp32-c3-idf.yaml | 4 ---- .../components/whynter/test.esp32-c3-idf.yaml | 4 ---- .../components/wiegand/test.esp32-c3-idf.yaml | 1 - tests/components/wifi/test.esp32-c3-idf.yaml | 1 - .../wifi_info/test.esp32-c3-idf.yaml | 4 ---- .../wifi_signal/test.esp32-c3-idf.yaml | 1 - .../wireguard/test.esp32-c3-idf.yaml | 4 ---- .../components/wl_134/test.esp32-c3-idf.yaml | 5 ----- tests/components/wts01/test.esp32-c3-idf.yaml | 5 ----- tests/components/x9c/test.esp32-c3-idf.yaml | 6 ------ .../xgzp68xx/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_ble/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_cgd1/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_cgdk2/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_cgg1/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_cgpr1/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_gcls002/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_hhccjcy01/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_hhccpot002/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_jqjcy01ym/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_lywsd02/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_lywsd03mmc/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_lywsdcgq/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_mhoc303/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_mhoc401/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_miscale/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_mjyd02yla/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_mue4094rt/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_rtcgq02lm/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_wx08zm/test.esp32-c3-idf.yaml | 4 ---- .../xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml | 4 ---- .../components/xl9535/test.esp32-c3-idf.yaml | 4 ---- .../components/xpt2046/test.esp32-c3-idf.yaml | 10 --------- .../components/yashima/test.esp32-c3-idf.yaml | 4 ---- .../components/zhlt01/test.esp32-c3-idf.yaml | 4 ---- .../zio_ultrasonic/test.esp32-c3-idf.yaml | 4 ---- .../zwave_proxy/test.esp32-c3-idf.yaml | 5 ----- .../components/zyaura/test.esp32-c3-idf.yaml | 5 ----- 467 files changed, 2212 deletions(-) delete mode 100644 tests/components/a01nyub/test.esp32-c3-idf.yaml delete mode 100644 tests/components/a02yyuw/test.esp32-c3-idf.yaml delete mode 100644 tests/components/a4988/test.esp32-c3-idf.yaml delete mode 100644 tests/components/absolute_humidity/test.esp32-c3-idf.yaml delete mode 100644 tests/components/adc128s102/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ade7880/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ade7953_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ade7953_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ads1115/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ags10/test.esp32-c3-idf.yaml delete mode 100644 tests/components/aht10/test.esp32-c3-idf.yaml delete mode 100644 tests/components/aic3204/test.esp32-c3-idf.yaml delete mode 100644 tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml delete mode 100644 tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml delete mode 100644 tests/components/alarm_control_panel/test.esp32-c3-idf.yaml delete mode 100644 tests/components/alpha3/test.esp32-c3-idf.yaml delete mode 100644 tests/components/am2315c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/am2320/test.esp32-c3-idf.yaml delete mode 100644 tests/components/am43/test.esp32-c3-idf.yaml delete mode 100644 tests/components/analog_threshold/test.esp32-c3-idf.yaml delete mode 100644 tests/components/animation/test.esp32-c3-idf.yaml delete mode 100644 tests/components/anova/test.esp32-c3-idf.yaml delete mode 100644 tests/components/apds9306/test.esp32-c3-idf.yaml delete mode 100644 tests/components/apds9960/test.esp32-c3-idf.yaml delete mode 100644 tests/components/api/test.esp32-c3-idf.yaml delete mode 100644 tests/components/as3935_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/as3935_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/as5600/test.esp32-c3-idf.yaml delete mode 100644 tests/components/as7341/test.esp32-c3-idf.yaml delete mode 100644 tests/components/at581x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/atc_mithermometer/test.esp32-c3-idf.yaml delete mode 100644 tests/components/atm90e26/test.esp32-c3-idf.yaml delete mode 100644 tests/components/atm90e32/test.esp32-c3-idf.yaml delete mode 100644 tests/components/axs15231/test.esp32-c3-idf.yaml delete mode 100644 tests/components/b_parasite/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bang_bang/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bedjet/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bh1750/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bh1900nux/test.esp32-c3-idf.yaml delete mode 100644 tests/components/binary_sensor/test.esp32-c3-idf.yaml delete mode 100644 tests/components/binary_sensor_map/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bl0906/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bl0939/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bl0940/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bl0942/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ble_client/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ble_presence/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ble_rssi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ble_scanner/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bme280_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bme280_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bme680/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bmi160/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bmp085/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bmp280_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bmp280_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bmp3xx_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bmp3xx_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bmp581/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bp1658cj/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bp5758d/test.esp32-c3-idf.yaml delete mode 100644 tests/components/button/test.esp32-c3-idf.yaml delete mode 100644 tests/components/bytebuffer/test.esp32-c3-idf.yaml delete mode 100644 tests/components/cap1188/test.esp32-c3-idf.yaml delete mode 100644 tests/components/captive_portal/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ccs811/test.esp32-c3-idf.yaml delete mode 100644 tests/components/cd74hc4067/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ch422g/test.esp32-c3-idf.yaml delete mode 100644 tests/components/chsc6x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/climate_ir_lg/test.esp32-c3-idf.yaml delete mode 100644 tests/components/cm1106/test.esp32-c3-idf.yaml delete mode 100644 tests/components/color/test.esp32-c3-idf.yaml delete mode 100644 tests/components/color_temperature/test.esp32-c3-idf.yaml delete mode 100644 tests/components/combination/test.esp32-c3-idf.yaml delete mode 100644 tests/components/coolix/test.esp32-c3-idf.yaml delete mode 100644 tests/components/copy/test.esp32-c3-idf.yaml delete mode 100644 tests/components/cs5460a/test.esp32-c3-idf.yaml delete mode 100644 tests/components/cse7761/test.esp32-c3-idf.yaml delete mode 100644 tests/components/cse7766/test.esp32-c3-idf.yaml delete mode 100644 tests/components/cst226/test.esp32-c3-idf.yaml delete mode 100644 tests/components/cst816/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ct_clamp/test.esp32-c3-idf.yaml delete mode 100644 tests/components/current_based/test.esp32-c3-idf.yaml delete mode 100644 tests/components/cwww/test.esp32-c3-idf.yaml delete mode 100644 tests/components/dac7678/test.esp32-c3-idf.yaml delete mode 100644 tests/components/daikin_brc/test.esp32-c3-idf.yaml delete mode 100644 tests/components/dallas_temp/test.esp32-c3-idf.yaml delete mode 100644 tests/components/daly_bms/test.esp32-c3-idf.yaml delete mode 100644 tests/components/debug/test.esp32-c3-idf.yaml delete mode 100644 tests/components/delonghi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/dfplayer/test.esp32-c3-idf.yaml delete mode 100644 tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml delete mode 100644 tests/components/dht/test.esp32-c3-idf.yaml delete mode 100644 tests/components/dht12/test.esp32-c3-idf.yaml delete mode 100644 tests/components/dps310/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ds1307/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ds2484/test.esp32-c3-idf.yaml delete mode 100644 tests/components/duty_cycle/test.esp32-c3-idf.yaml delete mode 100644 tests/components/duty_time/test.esp32-c3-idf.yaml delete mode 100644 tests/components/e131/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ee895/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ektf2232/test.esp32-c3-idf.yaml delete mode 100644 tests/components/emc2101/test.esp32-c3-idf.yaml delete mode 100644 tests/components/endstop/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ens160_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ens160_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ens210/test.esp32-c3-idf.yaml delete mode 100644 tests/components/es7210/test.esp32-c3-idf.yaml delete mode 100644 tests/components/es7243e/test.esp32-c3-idf.yaml delete mode 100644 tests/components/es8156/test.esp32-c3-idf.yaml delete mode 100644 tests/components/es8311/test.esp32-c3-idf.yaml delete mode 100644 tests/components/es8388/test.esp32-c3-idf.yaml delete mode 100644 tests/components/esphome/test.esp32-c3-idf.yaml delete mode 100644 tests/components/event/test.esp32-c3-idf.yaml delete mode 100644 tests/components/exposure_notifications/test.esp32-c3-idf.yaml delete mode 100644 tests/components/external_components/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ezo/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ezo_pmp/test.esp32-c3-idf.yaml delete mode 100644 tests/components/factory_reset/test.esp32-c3-idf.yaml delete mode 100644 tests/components/feedback/test.esp32-c3-idf.yaml delete mode 100644 tests/components/fingerprint_grow/test.esp32-c3-idf.yaml delete mode 100644 tests/components/font/test.esp32-c3-idf.yaml delete mode 100644 tests/components/fs3000/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ft5x06/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ft63x6/test.esp32-c3-idf.yaml delete mode 100644 tests/components/fujitsu_general/test.esp32-c3-idf.yaml delete mode 100644 tests/components/gcja5/test.esp32-c3-idf.yaml delete mode 100644 tests/components/gl_r01_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/globals/test.esp32-c3-idf.yaml delete mode 100644 tests/components/gp2y1010au0f/test.esp32-c3-idf.yaml delete mode 100644 tests/components/gp8403/test.esp32-c3-idf.yaml delete mode 100644 tests/components/gps/test.esp32-c3-idf.yaml delete mode 100644 tests/components/graph/test.esp32-c3-idf.yaml delete mode 100644 tests/components/graphical_display_menu/test.esp32-c3-idf.yaml delete mode 100644 tests/components/gree/test.esp32-c3-idf.yaml delete mode 100644 tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml delete mode 100644 tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml delete mode 100644 tests/components/growatt_solar/test.esp32-c3-idf.yaml delete mode 100644 tests/components/gt911/test.esp32-c3-idf.yaml delete mode 100644 tests/components/haier/test.esp32-c3-idf.yaml delete mode 100644 tests/components/havells_solar/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hbridge/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hdc1080/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hdc2010/test.esp32-c3-idf.yaml delete mode 100644 tests/components/he60r/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hitachi_ac344/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hitachi_ac424/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hlw8012/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hm3301/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hmc5883l/test.esp32-c3-idf.yaml delete mode 100644 tests/components/homeassistant/test.esp32-c3-idf.yaml delete mode 100644 tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/honeywellabp/test.esp32-c3-idf.yaml delete mode 100644 tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hte501/test.esp32-c3-idf.yaml delete mode 100644 tests/components/http_request/test.esp32-c3-idf.yaml delete mode 100644 tests/components/htu21d/test.esp32-c3-idf.yaml delete mode 100644 tests/components/htu31d/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hx711/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml delete mode 100644 tests/components/hyt271/test.esp32-c3-idf.yaml delete mode 100644 tests/components/i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/i2c_device/test.esp32-c3-idf.yaml delete mode 100644 tests/components/iaqcore/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ili9xxx/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ina219/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ina226/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ina260/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ina2xx_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ina2xx_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ina3221/test.esp32-c3-idf.yaml delete mode 100644 tests/components/inkbird_ibsth1_mini/test.esp32-c3-idf.yaml delete mode 100644 tests/components/integration/test.esp32-c3-idf.yaml delete mode 100644 tests/components/interval/test.esp32-c3-idf.yaml delete mode 100644 tests/components/jsn_sr04t/test.esp32-c3-idf.yaml delete mode 100644 tests/components/key_collector/test.esp32-c3-idf.yaml delete mode 100644 tests/components/kmeteriso/test.esp32-c3-idf.yaml delete mode 100644 tests/components/kuntze/test.esp32-c3-idf.yaml delete mode 100644 tests/components/lc709203f/test.esp32-c3-idf.yaml delete mode 100644 tests/components/lcd_gpio/test.esp32-c3-idf.yaml delete mode 100644 tests/components/lcd_menu/test.esp32-c3-idf.yaml delete mode 100644 tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ld2410/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ld2412/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ld2420/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ld2450/test.esp32-c3-idf.yaml delete mode 100644 tests/components/light/test.esp32-c3-idf.yaml delete mode 100644 tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml delete mode 100644 tests/components/lm75b/test.esp32-c3-idf.yaml delete mode 100644 tests/components/lock/test.esp32-c3-idf.yaml delete mode 100644 tests/components/lps22/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ltr390/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ltr501/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ltr_als_ps/test.esp32-c3-idf.yaml delete mode 100644 tests/components/m5stack_8angle/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mapping/test.esp32-c3-idf.yaml delete mode 100644 tests/components/matrix_keypad/test.esp32-c3-idf.yaml delete mode 100644 tests/components/max17043/test.esp32-c3-idf.yaml delete mode 100644 tests/components/max31855/test.esp32-c3-idf.yaml delete mode 100644 tests/components/max31856/test.esp32-c3-idf.yaml delete mode 100644 tests/components/max31865/test.esp32-c3-idf.yaml delete mode 100644 tests/components/max44009/test.esp32-c3-idf.yaml delete mode 100644 tests/components/max6675/test.esp32-c3-idf.yaml delete mode 100644 tests/components/max6956/test.esp32-c3-idf.yaml delete mode 100644 tests/components/max7219/test.esp32-c3-idf.yaml delete mode 100644 tests/components/max7219digit/test.esp32-c3-idf.yaml delete mode 100644 tests/components/max9611/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp23008/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp23016/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp23017/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp23s08/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp23s17/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp2515/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp3008/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp3204/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp4461/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp4725/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp4728/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp47a1/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp9600/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mcp9808/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mdns/test-enabled.esp32-c3-idf.yaml delete mode 100644 tests/components/mhz19/test.esp32-c3-idf.yaml delete mode 100644 tests/components/micronova/test.esp32-c3-idf.yaml delete mode 100644 tests/components/microphone/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mics_4514/test.esp32-c3-idf.yaml delete mode 100644 tests/components/midea_ir/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mitsubishi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mixer/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mlx90393/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mlx90614/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mmc5603/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mmc5983/test.esp32-c3-idf.yaml delete mode 100644 tests/components/modbus/test.esp32-c3-idf.yaml delete mode 100644 tests/components/modbus_controller/test.esp32-c3-idf.yaml delete mode 100644 tests/components/monochromatic/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mopeka_ble/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mopeka_pro_check/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mopeka_std_check/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mpl3115a2/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mpr121/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mpu6050/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mpu6886/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mqtt/test.esp32-c3-idf.yaml delete mode 100644 tests/components/mqtt_subscribe/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ms5611/test.esp32-c3-idf.yaml delete mode 100644 tests/components/msa3xx/test.esp32-c3-idf.yaml delete mode 100644 tests/components/my9231/test.esp32-c3-idf.yaml delete mode 100644 tests/components/nau7802/test.esp32-c3-idf.yaml delete mode 100644 tests/components/network/test-ipv6.esp32-c3-idf.yaml delete mode 100644 tests/components/network/test.esp32-c3-idf.yaml delete mode 100644 tests/components/nextion/test.esp32-c3-idf.yaml delete mode 100644 tests/components/noblex/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ntc/test.esp32-c3-idf.yaml delete mode 100644 tests/components/opt3001/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ota/test.esp32-c3-idf.yaml delete mode 100644 tests/components/output/test.esp32-c3-idf.yaml delete mode 100644 tests/components/packet_transport/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pca6416a/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pca9554/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pca9685/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pcd8544/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pcf85063/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pcf8563/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pcf8574/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pid/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pipsolar/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pm1006/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pm2005/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pmsa003i/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pmsx003/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pmwcs3/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pn532_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pn532_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pn7150_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pn7160_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pn7160_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/power_supply/test.esp32-c3-idf.yaml delete mode 100644 tests/components/prometheus/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pulse_meter/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pulse_width/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pvvx_mithermometer/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pylontech/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pzem004t/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pzemac/test.esp32-c3-idf.yaml delete mode 100644 tests/components/pzemdc/test.esp32-c3-idf.yaml delete mode 100644 tests/components/qmc5883l/test.esp32-c3-idf.yaml delete mode 100644 tests/components/qmp6988/test.esp32-c3-idf.yaml delete mode 100644 tests/components/qr_code/test.esp32-c3-idf.yaml delete mode 100644 tests/components/qwiic_pir/test.esp32-c3-idf.yaml delete mode 100644 tests/components/radon_eye_ble/test.esp32-c3-idf.yaml delete mode 100644 tests/components/radon_eye_rd200/test.esp32-c3-idf.yaml delete mode 100644 tests/components/rc522_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/rc522_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/rdm6300/test.esp32-c3-idf.yaml delete mode 100644 tests/components/resampler/test.esp32-c3-idf.yaml delete mode 100644 tests/components/resistance/test.esp32-c3-idf.yaml delete mode 100644 tests/components/restart/test.esp32-c3-idf.yaml delete mode 100644 tests/components/rf_bridge/test.esp32-c3-idf.yaml delete mode 100644 tests/components/rgb/test.esp32-c3-idf.yaml delete mode 100644 tests/components/rgbct/test.esp32-c3-idf.yaml delete mode 100644 tests/components/rgbw/test.esp32-c3-idf.yaml delete mode 100644 tests/components/rgbww/test.esp32-c3-idf.yaml delete mode 100644 tests/components/rotary_encoder/test.esp32-c3-idf.yaml delete mode 100644 tests/components/rtttl/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ruuvi_ble/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ruuvitag/test.esp32-c3-idf.yaml delete mode 100644 tests/components/safe_mode/test-enabled.esp32-c3-idf.yaml delete mode 100644 tests/components/scd30/test.esp32-c3-idf.yaml delete mode 100644 tests/components/scd4x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/script/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sdm_meter/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sdp3x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sds011/test.esp32-c3-idf.yaml delete mode 100644 tests/components/seeed_mr24hpc1/test.esp32-c3-idf.yaml delete mode 100644 tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml delete mode 100644 tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml delete mode 100644 tests/components/selec_meter/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sen0321/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sen21231/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sen5x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/senseair/test.esp32-c3-idf.yaml delete mode 100644 tests/components/servo/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sfa30/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sgp30/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sgp4x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sht3xd/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sht4x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/shtcx/test.esp32-c3-idf.yaml delete mode 100644 tests/components/shutdown/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sim800l/test.esp32-c3-idf.yaml delete mode 100644 tests/components/slow_pwm/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sm16716/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sm2135/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sm2235/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sm2335/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sm300d2/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sml/test.esp32-c3-idf.yaml delete mode 100644 tests/components/smt100/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sn74hc165/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sn74hc595/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sntp/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sonoff_d1/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sound_level/test.esp32-c3-idf.yaml delete mode 100644 tests/components/speaker/audio_dac.esp32-c3-idf.yaml delete mode 100644 tests/components/speaker/test.esp32-c3-idf.yaml delete mode 100644 tests/components/speed/test.esp32-c3-idf.yaml delete mode 100644 tests/components/spi_device/test.esp32-c3-idf.yaml delete mode 100644 tests/components/spi_led_strip/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sprinkler/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sps30/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ssd1306_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ssd1322_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ssd1325_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ssd1327_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ssd1331_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ssd1351_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/st7567_i2c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/st7567_spi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/st7735/test.esp32-c3-idf.yaml delete mode 100644 tests/components/st7789v/test.esp32-c3-idf.yaml delete mode 100644 tests/components/st7920/test.esp32-c3-idf.yaml delete mode 100644 tests/components/statsD/test.esp32-c3-idf.yaml delete mode 100644 tests/components/status/test.esp32-c3-idf.yaml delete mode 100644 tests/components/status_led/test.esp32-c3-idf.yaml delete mode 100644 tests/components/stepper/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sts3x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sun/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sun_gtil2/test.esp32-c3-idf.yaml delete mode 100644 tests/components/switch/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sx126x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sx127x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/sx1509/test.esp32-c3-idf.yaml delete mode 100644 tests/components/syslog/test.esp32-c3-idf.yaml delete mode 100644 tests/components/t6615/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tc74/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tca9548a/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tca9555/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tcl112/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tcs34725/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tee501/test.esp32-c3-idf.yaml delete mode 100644 tests/components/teleinfo/test.esp32-c3-idf.yaml delete mode 100644 tests/components/template/test.esp32-c3-idf.yaml delete mode 100644 tests/components/thermostat/test.esp32-c3-idf.yaml delete mode 100644 tests/components/time/test.esp32-c3-idf.yaml delete mode 100644 tests/components/time_based/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tlc59208f/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tlc5947/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tlc5971/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tm1621/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tm1637/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tm1638/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tm1651/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tmp102/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tmp1075/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tmp117/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tof10120/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tormatic/test.esp32-c3-idf.yaml delete mode 100644 tests/components/toshiba/test.esp32-c3-idf.yaml delete mode 100644 tests/components/toshiba/test_ras2819t.esp32-c3-idf.yaml delete mode 100644 tests/components/total_daily_energy/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tsl2561/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tsl2591/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tt21100/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ttp229_bsf/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ttp229_lsf/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tuya/test.esp32-c3-idf.yaml delete mode 100644 tests/components/tx20/test.esp32-c3-idf.yaml delete mode 100644 tests/components/udp/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ufire_ec/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ufire_ise/test.esp32-c3-idf.yaml delete mode 100644 tests/components/uln2003/test.esp32-c3-idf.yaml delete mode 100644 tests/components/ultrasonic/test.esp32-c3-idf.yaml delete mode 100644 tests/components/uponor_smatrix/test.esp32-c3-idf.yaml delete mode 100644 tests/components/uptime/test.esp32-c3-idf.yaml delete mode 100644 tests/components/vbus/test.esp32-c3-idf.yaml delete mode 100644 tests/components/veml3235/test.esp32-c3-idf.yaml delete mode 100644 tests/components/veml7700/test.esp32-c3-idf.yaml delete mode 100644 tests/components/version/test.esp32-c3-idf.yaml delete mode 100644 tests/components/vl53l0x/test.esp32-c3-idf.yaml delete mode 100644 tests/components/voice_assistant/test.esp32-c3-idf.yaml delete mode 100644 tests/components/wake_on_lan/test.esp32-c3-idf.yaml delete mode 100644 tests/components/waveshare_epaper/test.esp32-c3-idf.yaml delete mode 100644 tests/components/web_server/test.esp32-c3-idf.yaml delete mode 100644 tests/components/whirlpool/test.esp32-c3-idf.yaml delete mode 100644 tests/components/whynter/test.esp32-c3-idf.yaml delete mode 100644 tests/components/wiegand/test.esp32-c3-idf.yaml delete mode 100644 tests/components/wifi/test.esp32-c3-idf.yaml delete mode 100644 tests/components/wifi_info/test.esp32-c3-idf.yaml delete mode 100644 tests/components/wifi_signal/test.esp32-c3-idf.yaml delete mode 100644 tests/components/wireguard/test.esp32-c3-idf.yaml delete mode 100644 tests/components/wl_134/test.esp32-c3-idf.yaml delete mode 100644 tests/components/wts01/test.esp32-c3-idf.yaml delete mode 100644 tests/components/x9c/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xgzp68xx/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_ble/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_cgd1/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_cgdk2/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_cgg1/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_cgpr1/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_gcls002/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_hhccjcy01/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_hhccpot002/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_jqjcy01ym/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_lywsd02/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_lywsd03mmc/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_lywsdcgq/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_mhoc303/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_mhoc401/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_miscale/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_mjyd02yla/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_mue4094rt/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_rtcgq02lm/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_wx08zm/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xl9535/test.esp32-c3-idf.yaml delete mode 100644 tests/components/xpt2046/test.esp32-c3-idf.yaml delete mode 100644 tests/components/yashima/test.esp32-c3-idf.yaml delete mode 100644 tests/components/zhlt01/test.esp32-c3-idf.yaml delete mode 100644 tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml delete mode 100644 tests/components/zwave_proxy/test.esp32-c3-idf.yaml delete mode 100644 tests/components/zyaura/test.esp32-c3-idf.yaml diff --git a/tests/components/a01nyub/test.esp32-c3-idf.yaml b/tests/components/a01nyub/test.esp32-c3-idf.yaml deleted file mode 100644 index 2cda8deaf9..0000000000 --- a/tests/components/a01nyub/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/a02yyuw/test.esp32-c3-idf.yaml b/tests/components/a02yyuw/test.esp32-c3-idf.yaml deleted file mode 100644 index 2cda8deaf9..0000000000 --- a/tests/components/a02yyuw/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/a4988/test.esp32-c3-idf.yaml b/tests/components/a4988/test.esp32-c3-idf.yaml deleted file mode 100644 index 25caba75b5..0000000000 --- a/tests/components/a4988/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - step_pin: GPIO2 - dir_pin: GPIO3 - sleep_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/absolute_humidity/test.esp32-c3-idf.yaml b/tests/components/absolute_humidity/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/absolute_humidity/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/adc128s102/test.esp32-c3-idf.yaml b/tests/components/adc128s102/test.esp32-c3-idf.yaml deleted file mode 100644 index a60568a736..0000000000 --- a/tests/components/adc128s102/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - cs_pin: GPIO2 - -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ade7880/test.esp32-c3-idf.yaml b/tests/components/ade7880/test.esp32-c3-idf.yaml deleted file mode 100644 index 7d5b41fc5a..0000000000 --- a/tests/components/ade7880/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - irq0_pin: GPIO6 - irq1_pin: GPIO7 - reset_pin: GPIO9 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml b/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 59296a1e6e..0000000000 --- a/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - irq_pin: GPIO6 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.esp32-c3-idf.yaml b/tests/components/ade7953_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index 5e7e2dc82c..0000000000 --- a/tests/components/ade7953_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - irq_pin: GPIO9 - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ads1115/test.esp32-c3-idf.yaml b/tests/components/ads1115/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ads1115/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ags10/test.esp32-c3-idf.yaml b/tests/components/ags10/test.esp32-c3-idf.yaml deleted file mode 100644 index 72703301a1..0000000000 --- a/tests/components/ags10/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/aht10/test.esp32-c3-idf.yaml b/tests/components/aht10/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/aht10/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/aic3204/test.esp32-c3-idf.yaml b/tests/components/aic3204/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/aic3204/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml b/tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/airthings_wave_mini/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml b/tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/airthings_wave_plus/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/alarm_control_panel/test.esp32-c3-idf.yaml b/tests/components/alarm_control_panel/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/alarm_control_panel/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/alpha3/test.esp32-c3-idf.yaml b/tests/components/alpha3/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/alpha3/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/am2315c/test.esp32-c3-idf.yaml b/tests/components/am2315c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/am2315c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/am2320/test.esp32-c3-idf.yaml b/tests/components/am2320/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/am2320/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/am43/test.esp32-c3-idf.yaml b/tests/components/am43/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/am43/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/analog_threshold/test.esp32-c3-idf.yaml b/tests/components/analog_threshold/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/analog_threshold/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/animation/test.esp32-c3-idf.yaml b/tests/components/animation/test.esp32-c3-idf.yaml deleted file mode 100644 index a08a683333..0000000000 --- a/tests/components/animation/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,13 +0,0 @@ -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - animation: !include common.yaml - -display: - - platform: ili9xxx - id: main_lcd - spi_id: spi_bus - model: ili9342 - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - invert_colors: false diff --git a/tests/components/anova/test.esp32-c3-idf.yaml b/tests/components/anova/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/anova/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/apds9306/test.esp32-c3-idf.yaml b/tests/components/apds9306/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/apds9306/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/apds9960/test.esp32-c3-idf.yaml b/tests/components/apds9960/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/apds9960/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/api/test.esp32-c3-idf.yaml b/tests/components/api/test.esp32-c3-idf.yaml deleted file mode 100644 index 46c01d926f..0000000000 --- a/tests/components/api/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -<<: !include common.yaml - -wifi: - ssid: MySSID - password: password1 diff --git a/tests/components/as3935_i2c/test.esp32-c3-idf.yaml b/tests/components/as3935_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 59296a1e6e..0000000000 --- a/tests/components/as3935_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - irq_pin: GPIO6 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/as3935_spi/test.esp32-c3-idf.yaml b/tests/components/as3935_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index 5e7e2dc82c..0000000000 --- a/tests/components/as3935_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - irq_pin: GPIO9 - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/as5600/test.esp32-c3-idf.yaml b/tests/components/as5600/test.esp32-c3-idf.yaml deleted file mode 100644 index 03a87ed6c4..0000000000 --- a/tests/components/as5600/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - dir_pin: GPIO6 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/as7341/test.esp32-c3-idf.yaml b/tests/components/as7341/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/as7341/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/at581x/test.esp32-c3-idf.yaml b/tests/components/at581x/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/at581x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/atc_mithermometer/test.esp32-c3-idf.yaml b/tests/components/atc_mithermometer/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/atc_mithermometer/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/atm90e26/test.esp32-c3-idf.yaml b/tests/components/atm90e26/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/atm90e26/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/atm90e32/test.esp32-c3-idf.yaml b/tests/components/atm90e32/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/atm90e32/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/axs15231/test.esp32-c3-idf.yaml b/tests/components/axs15231/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/axs15231/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/b_parasite/test.esp32-c3-idf.yaml b/tests/components/b_parasite/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/b_parasite/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bang_bang/test.esp32-c3-idf.yaml b/tests/components/bang_bang/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/bang_bang/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/bedjet/test.esp32-c3-idf.yaml b/tests/components/bedjet/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/bedjet/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bh1750/test.esp32-c3-idf.yaml b/tests/components/bh1750/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/bh1750/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bh1900nux/test.esp32-c3-idf.yaml b/tests/components/bh1900nux/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/bh1900nux/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/binary_sensor/test.esp32-c3-idf.yaml b/tests/components/binary_sensor/test.esp32-c3-idf.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/binary_sensor/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/binary_sensor_map/test.esp32-c3-idf.yaml b/tests/components/binary_sensor_map/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/binary_sensor_map/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/bl0906/test.esp32-c3-idf.yaml b/tests/components/bl0906/test.esp32-c3-idf.yaml deleted file mode 100644 index 147d967dd4..0000000000 --- a/tests/components/bl0906/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - uart_19200: !include ../../test_build_components/common/uart_19200/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bl0939/test.esp32-c3-idf.yaml b/tests/components/bl0939/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/bl0939/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bl0940/test.esp32-c3-idf.yaml b/tests/components/bl0940/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/bl0940/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bl0942/test.esp32-c3-idf.yaml b/tests/components/bl0942/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/bl0942/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ble_client/test.esp32-c3-idf.yaml b/tests/components/ble_client/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/ble_client/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ble_presence/test.esp32-c3-idf.yaml b/tests/components/ble_presence/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/ble_presence/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ble_rssi/test.esp32-c3-idf.yaml b/tests/components/ble_rssi/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/ble_rssi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ble_scanner/test.esp32-c3-idf.yaml b/tests/components/ble_scanner/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/ble_scanner/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bme280_i2c/test.esp32-c3-idf.yaml b/tests/components/bme280_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/bme280_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bme280_spi/test.esp32-c3-idf.yaml b/tests/components/bme280_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/bme280_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bme680/test.esp32-c3-idf.yaml b/tests/components/bme680/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/bme680/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bmi160/test.esp32-c3-idf.yaml b/tests/components/bmi160/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/bmi160/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bmp085/test.esp32-c3-idf.yaml b/tests/components/bmp085/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/bmp085/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.esp32-c3-idf.yaml b/tests/components/bmp280_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/bmp280_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.esp32-c3-idf.yaml b/tests/components/bmp280_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/bmp280_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bmp3xx_i2c/test.esp32-c3-idf.yaml b/tests/components/bmp3xx_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/bmp3xx_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bmp3xx_spi/test.esp32-c3-idf.yaml b/tests/components/bmp3xx_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/bmp3xx_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bmp581/test.esp32-c3-idf.yaml b/tests/components/bmp581/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/bmp581/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/bp1658cj/test.esp32-c3-idf.yaml b/tests/components/bp1658cj/test.esp32-c3-idf.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/bp1658cj/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/bp5758d/test.esp32-c3-idf.yaml b/tests/components/bp5758d/test.esp32-c3-idf.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/bp5758d/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/button/test.esp32-c3-idf.yaml b/tests/components/button/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/button/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/bytebuffer/test.esp32-c3-idf.yaml b/tests/components/bytebuffer/test.esp32-c3-idf.yaml deleted file mode 100644 index 380ca87628..0000000000 --- a/tests/components/bytebuffer/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -!include common.yaml diff --git a/tests/components/cap1188/test.esp32-c3-idf.yaml b/tests/components/cap1188/test.esp32-c3-idf.yaml deleted file mode 100644 index c97f30d52c..0000000000 --- a/tests/components/cap1188/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - reset_pin: GPIO6 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/captive_portal/test.esp32-c3-idf.yaml b/tests/components/captive_portal/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/captive_portal/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ccs811/test.esp32-c3-idf.yaml b/tests/components/ccs811/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ccs811/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/cd74hc4067/test.esp32-c3-idf.yaml b/tests/components/cd74hc4067/test.esp32-c3-idf.yaml deleted file mode 100644 index 5e8784c1fc..0000000000 --- a/tests/components/cd74hc4067/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - pin_s0: GPIO2 - pin_s1: GPIO3 - pin_s2: GPIO4 - pin_s3: GPIO5 - pin: GPIO0 - -<<: !include common.yaml diff --git a/tests/components/ch422g/test.esp32-c3-idf.yaml b/tests/components/ch422g/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ch422g/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/chsc6x/test.esp32-c3-idf.yaml b/tests/components/chsc6x/test.esp32-c3-idf.yaml deleted file mode 100644 index f0de4107d7..0000000000 --- a/tests/components/chsc6x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,20 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -display: - - platform: ili9xxx - id: ili9xxx_display - model: GC9A01A - invert_colors: True - cs_pin: 11 - dc_pin: 7 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: chsc6x - display: ili9xxx_display - interrupt_pin: 20 diff --git a/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml b/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/cm1106/test.esp32-c3-idf.yaml b/tests/components/cm1106/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/cm1106/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/color/test.esp32-c3-idf.yaml b/tests/components/color/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/color/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/color_temperature/test.esp32-c3-idf.yaml b/tests/components/color_temperature/test.esp32-c3-idf.yaml deleted file mode 100644 index 016f315d9f..0000000000 --- a/tests/components/color_temperature/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - light_platform: ledc - pin_o1: GPIO6 - pin_o2: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/combination/test.esp32-c3-idf.yaml b/tests/components/combination/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/combination/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/coolix/test.esp32-c3-idf.yaml b/tests/components/coolix/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/coolix/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/copy/test.esp32-c3-idf.yaml b/tests/components/copy/test.esp32-c3-idf.yaml deleted file mode 100644 index 76272beb77..0000000000 --- a/tests/components/copy/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - pwm_platform: ledc - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/cs5460a/test.esp32-c3-idf.yaml b/tests/components/cs5460a/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/cs5460a/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/cse7761/test.esp32-c3-idf.yaml b/tests/components/cse7761/test.esp32-c3-idf.yaml deleted file mode 100644 index 4e11c6e7cb..0000000000 --- a/tests/components/cse7761/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart_38400: !include ../../test_build_components/common/uart_38400/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/cse7766/test.esp32-c3-idf.yaml b/tests/components/cse7766/test.esp32-c3-idf.yaml deleted file mode 100644 index dc95c985c7..0000000000 --- a/tests/components/cse7766/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart_4800_even: !include ../../test_build_components/common/uart_4800_even/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/cst226/test.esp32-c3-idf.yaml b/tests/components/cst226/test.esp32-c3-idf.yaml deleted file mode 100644 index ffc12867d0..0000000000 --- a/tests/components/cst226/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - cs_pin: GPIO7 - dc_pin: GPIO9 - disp_reset_pin: GPIO18 - interrupt_pin: GPIO2 - reset_pin: GPIO3 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/cst816/test.esp32-c3-idf.yaml b/tests/components/cst816/test.esp32-c3-idf.yaml deleted file mode 100644 index ffc12867d0..0000000000 --- a/tests/components/cst816/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - cs_pin: GPIO7 - dc_pin: GPIO9 - disp_reset_pin: GPIO18 - interrupt_pin: GPIO2 - reset_pin: GPIO3 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ct_clamp/test.esp32-c3-idf.yaml b/tests/components/ct_clamp/test.esp32-c3-idf.yaml deleted file mode 100644 index a8f29c98ae..0000000000 --- a/tests/components/ct_clamp/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO0 - -<<: !include common.yaml diff --git a/tests/components/current_based/test.esp32-c3-idf.yaml b/tests/components/current_based/test.esp32-c3-idf.yaml deleted file mode 100644 index 59296a1e6e..0000000000 --- a/tests/components/current_based/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - irq_pin: GPIO6 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/cwww/test.esp32-c3-idf.yaml b/tests/components/cwww/test.esp32-c3-idf.yaml deleted file mode 100644 index 51571b34cf..0000000000 --- a/tests/components/cwww/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,17 +0,0 @@ -substitutions: - light_platform: ledc - pin_o1: GPIO6 - pin_o2: GPIO7 - -output: - - platform: ${light_platform} - id: light_output_1 - pin: ${pin_o1} - channel: 0 - - platform: ${light_platform} - id: light_output_2 - pin: ${pin_o2} - channel: 1 - phase_angle: 180° - -<<: !include common.yaml diff --git a/tests/components/dac7678/test.esp32-c3-idf.yaml b/tests/components/dac7678/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/dac7678/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/daikin_brc/test.esp32-c3-idf.yaml b/tests/components/daikin_brc/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/daikin_brc/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/dallas_temp/test.esp32-c3-idf.yaml b/tests/components/dallas_temp/test.esp32-c3-idf.yaml deleted file mode 100644 index 49bf988eb4..0000000000 --- a/tests/components/dallas_temp/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - one_wire_pin: "4" - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/daly_bms/test.esp32-c3-idf.yaml b/tests/components/daly_bms/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/daly_bms/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/debug/test.esp32-c3-idf.yaml b/tests/components/debug/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/debug/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/delonghi/test.esp32-c3-idf.yaml b/tests/components/delonghi/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/delonghi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/dfplayer/test.esp32-c3-idf.yaml b/tests/components/dfplayer/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/dfplayer/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml b/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/dht/test.esp32-c3-idf.yaml b/tests/components/dht/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/dht/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/dht12/test.esp32-c3-idf.yaml b/tests/components/dht12/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/dht12/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/dps310/test.esp32-c3-idf.yaml b/tests/components/dps310/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/dps310/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ds1307/test.esp32-c3-idf.yaml b/tests/components/ds1307/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ds1307/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ds2484/test.esp32-c3-idf.yaml b/tests/components/ds2484/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ds2484/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/duty_cycle/test.esp32-c3-idf.yaml b/tests/components/duty_cycle/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/duty_cycle/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/duty_time/test.esp32-c3-idf.yaml b/tests/components/duty_time/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/duty_time/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/e131/test.esp32-c3-idf.yaml b/tests/components/e131/test.esp32-c3-idf.yaml deleted file mode 100644 index d9dc4f6804..0000000000 --- a/tests/components/e131/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - light_platform: esp32_rmt_led_strip - pin: GPIO2 - -<<: !include common-idf.yaml diff --git a/tests/components/ee895/test.esp32-c3-idf.yaml b/tests/components/ee895/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ee895/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ektf2232/test.esp32-c3-idf.yaml b/tests/components/ektf2232/test.esp32-c3-idf.yaml deleted file mode 100644 index 708d352a59..0000000000 --- a/tests/components/ektf2232/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - display_reset_pin: GPIO3 - interrupt_pin: GPIO6 - touch_reset_pin: GPIO7 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/emc2101/test.esp32-c3-idf.yaml b/tests/components/emc2101/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/emc2101/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/endstop/test.esp32-c3-idf.yaml b/tests/components/endstop/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/endstop/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ens160_i2c/test.esp32-c3-idf.yaml b/tests/components/ens160_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ens160_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ens160_spi/test.esp32-c3-idf.yaml b/tests/components/ens160_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/ens160_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ens210/test.esp32-c3-idf.yaml b/tests/components/ens210/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ens210/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/es7210/test.esp32-c3-idf.yaml b/tests/components/es7210/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/es7210/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/es7243e/test.esp32-c3-idf.yaml b/tests/components/es7243e/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/es7243e/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/es8156/test.esp32-c3-idf.yaml b/tests/components/es8156/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/es8156/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/es8311/test.esp32-c3-idf.yaml b/tests/components/es8311/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/es8311/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/es8388/test.esp32-c3-idf.yaml b/tests/components/es8388/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/es8388/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/esphome/test.esp32-c3-idf.yaml b/tests/components/esphome/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/esphome/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/event/test.esp32-c3-idf.yaml b/tests/components/event/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/event/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/exposure_notifications/test.esp32-c3-idf.yaml b/tests/components/exposure_notifications/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/exposure_notifications/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/external_components/test.esp32-c3-idf.yaml b/tests/components/external_components/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/external_components/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/ezo/test.esp32-c3-idf.yaml b/tests/components/ezo/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ezo/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.esp32-c3-idf.yaml b/tests/components/ezo_pmp/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ezo_pmp/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/factory_reset/test.esp32-c3-idf.yaml b/tests/components/factory_reset/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/factory_reset/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/feedback/test.esp32-c3-idf.yaml b/tests/components/feedback/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/feedback/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml b/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml deleted file mode 100644 index dff4f7fd92..0000000000 --- a/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - sensing_pin: GPIO6 -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/font/test.esp32-c3-idf.yaml b/tests/components/font/test.esp32-c3-idf.yaml deleted file mode 100644 index 2090db7e6d..0000000000 --- a/tests/components/font/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - display_reset_pin: GPIO3 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/fs3000/test.esp32-c3-idf.yaml b/tests/components/fs3000/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/fs3000/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ft5x06/test.esp32-c3-idf.yaml b/tests/components/ft5x06/test.esp32-c3-idf.yaml deleted file mode 100644 index c97f30d52c..0000000000 --- a/tests/components/ft5x06/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - reset_pin: GPIO6 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ft63x6/test.esp32-c3-idf.yaml b/tests/components/ft63x6/test.esp32-c3-idf.yaml deleted file mode 100644 index febf38d8e1..0000000000 --- a/tests/components/ft63x6/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - interrupt_pin: GPIO2 - reset_pin: GPIO3 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/fujitsu_general/test.esp32-c3-idf.yaml b/tests/components/fujitsu_general/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/fujitsu_general/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/gcja5/test.esp32-c3-idf.yaml b/tests/components/gcja5/test.esp32-c3-idf.yaml deleted file mode 100644 index 236529042f..0000000000 --- a/tests/components/gcja5/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - uart_9600_even: !include ../../test_build_components/common/uart_9600_even/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/gl_r01_i2c/test.esp32-c3-idf.yaml b/tests/components/gl_r01_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/gl_r01_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/globals/test.esp32-c3-idf.yaml b/tests/components/globals/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/globals/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/gp2y1010au0f/test.esp32-c3-idf.yaml b/tests/components/gp2y1010au0f/test.esp32-c3-idf.yaml deleted file mode 100644 index 0e331c893c..0000000000 --- a/tests/components/gp2y1010au0f/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - adc_pin: GPIO0 - output_pin: GPIO1 - -<<: !include common.yaml diff --git a/tests/components/gp8403/test.esp32-c3-idf.yaml b/tests/components/gp8403/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/gp8403/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/gps/test.esp32-c3-idf.yaml b/tests/components/gps/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/gps/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/graph/test.esp32-c3-idf.yaml b/tests/components/graph/test.esp32-c3-idf.yaml deleted file mode 100644 index c97f30d52c..0000000000 --- a/tests/components/graph/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - reset_pin: GPIO6 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml b/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml deleted file mode 100644 index c97f30d52c..0000000000 --- a/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - reset_pin: GPIO6 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/gree/test.esp32-c3-idf.yaml b/tests/components/gree/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/gree/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml b/tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml b/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/growatt_solar/test.esp32-c3-idf.yaml b/tests/components/growatt_solar/test.esp32-c3-idf.yaml deleted file mode 100644 index 17940aafcf..0000000000 --- a/tests/components/growatt_solar/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - flow_control_pin: GPIO3 -packages: - modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/gt911/test.esp32-c3-idf.yaml b/tests/components/gt911/test.esp32-c3-idf.yaml deleted file mode 100644 index 5e15963b7e..0000000000 --- a/tests/components/gt911/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - display_reset_pin: "18" - interrupt_pin: "20" - reset_pin: "21" - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/haier/test.esp32-c3-idf.yaml b/tests/components/haier/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/haier/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/havells_solar/test.esp32-c3-idf.yaml b/tests/components/havells_solar/test.esp32-c3-idf.yaml deleted file mode 100644 index 17940aafcf..0000000000 --- a/tests/components/havells_solar/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - flow_control_pin: GPIO3 -packages: - modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/hbridge/test.esp32-c3-idf.yaml b/tests/components/hbridge/test.esp32-c3-idf.yaml deleted file mode 100644 index 93a6cb5818..0000000000 --- a/tests/components/hbridge/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,17 +0,0 @@ -substitutions: - pwm_platform: "ledc" - output1_pin: "4" - output2_pin: "5" - output3_pin: "6" - output4_pin: "7" - hbridge_on_pin: "2" - hbridge_off_pin: "3" - -<<: !include common.yaml - -switch: - - platform: hbridge - id: switch_hbridge - on_pin: ${hbridge_on_pin} - off_pin: ${hbridge_off_pin} - pulse_length: 60ms diff --git a/tests/components/hdc1080/test.esp32-c3-idf.yaml b/tests/components/hdc1080/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/hdc1080/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/hdc2010/test.esp32-c3-idf.yaml b/tests/components/hdc2010/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/hdc2010/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/he60r/test.esp32-c3-idf.yaml b/tests/components/he60r/test.esp32-c3-idf.yaml deleted file mode 100644 index b0c8c5de3d..0000000000 --- a/tests/components/he60r/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - uart_1200_even: !include ../../test_build_components/common/uart_1200_even/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml b/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml b/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/hlw8012/test.esp32-c3-idf.yaml b/tests/components/hlw8012/test.esp32-c3-idf.yaml deleted file mode 100644 index 8b0d069ce2..0000000000 --- a/tests/components/hlw8012/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - sel_pin: GPIO2 - cf_pin: GPIO3 - cf1_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/hm3301/test.esp32-c3-idf.yaml b/tests/components/hm3301/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/hm3301/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/hmc5883l/test.esp32-c3-idf.yaml b/tests/components/hmc5883l/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/hmc5883l/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/homeassistant/test.esp32-c3-idf.yaml b/tests/components/homeassistant/test.esp32-c3-idf.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/homeassistant/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml b/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/honeywellabp/test.esp32-c3-idf.yaml b/tests/components/honeywellabp/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/honeywellabp/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml b/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/hte501/test.esp32-c3-idf.yaml b/tests/components/hte501/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/hte501/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/http_request/test.esp32-c3-idf.yaml b/tests/components/http_request/test.esp32-c3-idf.yaml deleted file mode 100644 index ee2f5aa59b..0000000000 --- a/tests/components/http_request/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - verify_ssl: "true" - -<<: !include common.yaml diff --git a/tests/components/htu21d/test.esp32-c3-idf.yaml b/tests/components/htu21d/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/htu21d/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/htu31d/test.esp32-c3-idf.yaml b/tests/components/htu31d/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/htu31d/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/hx711/test.esp32-c3-idf.yaml b/tests/components/hx711/test.esp32-c3-idf.yaml deleted file mode 100644 index defef165e3..0000000000 --- a/tests/components/hx711/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clk_pin: GPIO4 - dout_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml b/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/hyt271/test.esp32-c3-idf.yaml b/tests/components/hyt271/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/hyt271/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/i2c/test.esp32-c3-idf.yaml b/tests/components/i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/i2c_device/test.esp32-c3-idf.yaml b/tests/components/i2c_device/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/i2c_device/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/iaqcore/test.esp32-c3-idf.yaml b/tests/components/iaqcore/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/iaqcore/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ili9xxx/test.esp32-c3-idf.yaml b/tests/components/ili9xxx/test.esp32-c3-idf.yaml deleted file mode 100644 index 1eea1e85f7..0000000000 --- a/tests/components/ili9xxx/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - cs_pin1: GPIO8 - dc_pin1: GPIO9 - reset_pin1: GPIO10 - cs_pin2: GPIO2 - dc_pin2: GPIO3 - reset_pin2: GPIO7 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ina219/test.esp32-c3-idf.yaml b/tests/components/ina219/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ina219/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ina226/test.esp32-c3-idf.yaml b/tests/components/ina226/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ina226/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ina260/test.esp32-c3-idf.yaml b/tests/components/ina260/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ina260/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ina2xx_i2c/test.esp32-c3-idf.yaml b/tests/components/ina2xx_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ina2xx_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ina2xx_spi/test.esp32-c3-idf.yaml b/tests/components/ina2xx_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/ina2xx_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ina3221/test.esp32-c3-idf.yaml b/tests/components/ina3221/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ina3221/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/inkbird_ibsth1_mini/test.esp32-c3-idf.yaml b/tests/components/inkbird_ibsth1_mini/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/inkbird_ibsth1_mini/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/integration/test.esp32-c3-idf.yaml b/tests/components/integration/test.esp32-c3-idf.yaml deleted file mode 100644 index 5105e645f3..0000000000 --- a/tests/components/integration/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO1 - -<<: !include common-esp32.yaml diff --git a/tests/components/interval/test.esp32-c3-idf.yaml b/tests/components/interval/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/interval/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml b/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/key_collector/test.esp32-c3-idf.yaml b/tests/components/key_collector/test.esp32-c3-idf.yaml deleted file mode 100644 index b580ab7843..0000000000 --- a/tests/components/key_collector/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - pin_r0: GPIO2 - pin_r1: GPIO3 - pin_c0: GPIO4 - pin_c1: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/kmeteriso/test.esp32-c3-idf.yaml b/tests/components/kmeteriso/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/kmeteriso/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/kuntze/test.esp32-c3-idf.yaml b/tests/components/kuntze/test.esp32-c3-idf.yaml deleted file mode 100644 index 17940aafcf..0000000000 --- a/tests/components/kuntze/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - flow_control_pin: GPIO3 -packages: - modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp32-c3-idf.yaml b/tests/components/lc709203f/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/lc709203f/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/lcd_gpio/test.esp32-c3-idf.yaml b/tests/components/lcd_gpio/test.esp32-c3-idf.yaml deleted file mode 100644 index b6b05f3ab4..0000000000 --- a/tests/components/lcd_gpio/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - d0_pin: GPIO1 - d1_pin: GPIO2 - d2_pin: GPIO3 - d3_pin: GPIO4 - enable_pin: GPIO5 - rs_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/lcd_menu/test.esp32-c3-idf.yaml b/tests/components/lcd_menu/test.esp32-c3-idf.yaml deleted file mode 100644 index b6b05f3ab4..0000000000 --- a/tests/components/lcd_menu/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - d0_pin: GPIO1 - d1_pin: GPIO2 - d2_pin: GPIO3 - d3_pin: GPIO4 - enable_pin: GPIO5 - rs_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml b/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ld2410/test.esp32-c3-idf.yaml b/tests/components/ld2410/test.esp32-c3-idf.yaml deleted file mode 100644 index 7a8f790ed8..0000000000 --- a/tests/components/ld2410/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ld2412/test.esp32-c3-idf.yaml b/tests/components/ld2412/test.esp32-c3-idf.yaml deleted file mode 100644 index 7a8f790ed8..0000000000 --- a/tests/components/ld2412/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ld2420/test.esp32-c3-idf.yaml b/tests/components/ld2420/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/ld2420/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ld2450/test.esp32-c3-idf.yaml b/tests/components/ld2450/test.esp32-c3-idf.yaml deleted file mode 100644 index 7a8f790ed8..0000000000 --- a/tests/components/ld2450/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - tx_pin: GPIO4 - rx_pin: GPIO5 - -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/light/test.esp32-c3-idf.yaml b/tests/components/light/test.esp32-c3-idf.yaml deleted file mode 100644 index 317d5748a3..0000000000 --- a/tests/components/light/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,21 +0,0 @@ -output: - - platform: gpio - id: test_binary - pin: 0 - - platform: ledc - id: test_ledc_1 - pin: 1 - - platform: ledc - id: test_ledc_2 - pin: 2 - - platform: ledc - id: test_ledc_3 - pin: 3 - - platform: ledc - id: test_ledc_4 - pin: 4 - - platform: ledc - id: test_ledc_5 - pin: 5 - -<<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml b/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml deleted file mode 100644 index febf38d8e1..0000000000 --- a/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - interrupt_pin: GPIO2 - reset_pin: GPIO3 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/lm75b/test.esp32-c3-idf.yaml b/tests/components/lm75b/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/lm75b/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/lock/test.esp32-c3-idf.yaml b/tests/components/lock/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/lock/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/lps22/test.esp32-c3-idf.yaml b/tests/components/lps22/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/lps22/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ltr390/test.esp32-c3-idf.yaml b/tests/components/ltr390/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ltr390/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ltr501/test.esp32-c3-idf.yaml b/tests/components/ltr501/test.esp32-c3-idf.yaml deleted file mode 100644 index 72703301a1..0000000000 --- a/tests/components/ltr501/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ltr_als_ps/test.esp32-c3-idf.yaml b/tests/components/ltr_als_ps/test.esp32-c3-idf.yaml deleted file mode 100644 index 72703301a1..0000000000 --- a/tests/components/ltr_als_ps/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c_low_freq: !include ../../test_build_components/common/i2c_low_freq/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.esp32-c3-idf.yaml b/tests/components/m5stack_8angle/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/m5stack_8angle/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mapping/test.esp32-c3-idf.yaml b/tests/components/mapping/test.esp32-c3-idf.yaml deleted file mode 100644 index 7911eb7edc..0000000000 --- a/tests/components/mapping/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,13 +0,0 @@ -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - map: !include common.yaml - -display: - spi_id: spi_bus - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - invert_colors: false diff --git a/tests/components/matrix_keypad/test.esp32-c3-idf.yaml b/tests/components/matrix_keypad/test.esp32-c3-idf.yaml deleted file mode 100644 index 75d9c0b263..0000000000 --- a/tests/components/matrix_keypad/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,15 +0,0 @@ -packages: - common: !include common.yaml - -matrix_keypad: - id: keypad - rows: - - pin: 1 - - pin: 2 - columns: - - pin: 3 - - pin: 4 - keys: "1234" - has_pulldowns: true - on_key: - - lambda: ESP_LOGI("KEY", "key %d pressed", x); diff --git a/tests/components/max17043/test.esp32-c3-idf.yaml b/tests/components/max17043/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/max17043/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/max31855/test.esp32-c3-idf.yaml b/tests/components/max31855/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/max31855/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/max31856/test.esp32-c3-idf.yaml b/tests/components/max31856/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/max31856/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/max31865/test.esp32-c3-idf.yaml b/tests/components/max31865/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/max31865/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/max44009/test.esp32-c3-idf.yaml b/tests/components/max44009/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/max44009/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/max6675/test.esp32-c3-idf.yaml b/tests/components/max6675/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/max6675/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/max6956/test.esp32-c3-idf.yaml b/tests/components/max6956/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/max6956/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/max7219/test.esp32-c3-idf.yaml b/tests/components/max7219/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/max7219/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/max7219digit/test.esp32-c3-idf.yaml b/tests/components/max7219digit/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/max7219digit/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/max9611/test.esp32-c3-idf.yaml b/tests/components/max9611/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/max9611/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp23008/test.esp32-c3-idf.yaml b/tests/components/mcp23008/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mcp23008/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp23016/test.esp32-c3-idf.yaml b/tests/components/mcp23016/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mcp23016/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp23017/test.esp32-c3-idf.yaml b/tests/components/mcp23017/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mcp23017/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp23s08/test.esp32-c3-idf.yaml b/tests/components/mcp23s08/test.esp32-c3-idf.yaml deleted file mode 100644 index b11ec9cdc6..0000000000 --- a/tests/components/mcp23s08/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - cs_pin: GPIO8 - -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp23s17/test.esp32-c3-idf.yaml b/tests/components/mcp23s17/test.esp32-c3-idf.yaml deleted file mode 100644 index b11ec9cdc6..0000000000 --- a/tests/components/mcp23s17/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - cs_pin: GPIO8 - -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp2515/test.esp32-c3-idf.yaml b/tests/components/mcp2515/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/mcp2515/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp3008/test.esp32-c3-idf.yaml b/tests/components/mcp3008/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/mcp3008/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp3204/test.esp32-c3-idf.yaml b/tests/components/mcp3204/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/mcp3204/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp4461/test.esp32-c3-idf.yaml b/tests/components/mcp4461/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mcp4461/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp4725/test.esp32-c3-idf.yaml b/tests/components/mcp4725/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mcp4725/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp4728/test.esp32-c3-idf.yaml b/tests/components/mcp4728/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mcp4728/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp47a1/test.esp32-c3-idf.yaml b/tests/components/mcp47a1/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mcp47a1/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp9600/test.esp32-c3-idf.yaml b/tests/components/mcp9600/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mcp9600/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mcp9808/test.esp32-c3-idf.yaml b/tests/components/mcp9808/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mcp9808/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mdns/test-enabled.esp32-c3-idf.yaml b/tests/components/mdns/test-enabled.esp32-c3-idf.yaml deleted file mode 100644 index 97fd63d70e..0000000000 --- a/tests/components/mdns/test-enabled.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-enabled.yaml diff --git a/tests/components/mhz19/test.esp32-c3-idf.yaml b/tests/components/mhz19/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/mhz19/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/micronova/test.esp32-c3-idf.yaml b/tests/components/micronova/test.esp32-c3-idf.yaml deleted file mode 100644 index eed876ae74..0000000000 --- a/tests/components/micronova/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - enable_rx_pin: GPIO3 - -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/microphone/test.esp32-c3-idf.yaml b/tests/components/microphone/test.esp32-c3-idf.yaml deleted file mode 100644 index c28dc553f5..0000000000 --- a/tests/components/microphone/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - i2s_bclk_pin: GPIO6 - i2s_lrclk_pin: GPIO7 - i2s_mclk_pin: GPIO8 - i2s_din_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/mics_4514/test.esp32-c3-idf.yaml b/tests/components/mics_4514/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mics_4514/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/midea_ir/test.esp32-c3-idf.yaml b/tests/components/midea_ir/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/midea_ir/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mitsubishi/test.esp32-c3-idf.yaml b/tests/components/mitsubishi/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/mitsubishi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mixer/test.esp32-c3-idf.yaml b/tests/components/mixer/test.esp32-c3-idf.yaml deleted file mode 100644 index f1721f0862..0000000000 --- a/tests/components/mixer/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - lrclk_pin: GPIO4 - bclk_pin: GPIO5 - mclk_pin: GPIO6 - dout_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp32-c3-idf.yaml b/tests/components/mlx90393/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mlx90393/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mlx90614/test.esp32-c3-idf.yaml b/tests/components/mlx90614/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mlx90614/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mmc5603/test.esp32-c3-idf.yaml b/tests/components/mmc5603/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mmc5603/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mmc5983/test.esp32-c3-idf.yaml b/tests/components/mmc5983/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mmc5983/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/modbus/test.esp32-c3-idf.yaml b/tests/components/modbus/test.esp32-c3-idf.yaml deleted file mode 100644 index 430c6818cb..0000000000 --- a/tests/components/modbus/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - flow_control_pin: GPIO3 -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/modbus_controller/test.esp32-c3-idf.yaml b/tests/components/modbus_controller/test.esp32-c3-idf.yaml deleted file mode 100644 index db826676ee..0000000000 --- a/tests/components/modbus_controller/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/monochromatic/test.esp32-c3-idf.yaml b/tests/components/monochromatic/test.esp32-c3-idf.yaml deleted file mode 100644 index feabf013fd..0000000000 --- a/tests/components/monochromatic/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - light_platform: ledc - pin: GPIO2 - -<<: !include common.yaml diff --git a/tests/components/mopeka_ble/test.esp32-c3-idf.yaml b/tests/components/mopeka_ble/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/mopeka_ble/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mopeka_pro_check/test.esp32-c3-idf.yaml b/tests/components/mopeka_pro_check/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/mopeka_pro_check/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mopeka_std_check/test.esp32-c3-idf.yaml b/tests/components/mopeka_std_check/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/mopeka_std_check/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.esp32-c3-idf.yaml b/tests/components/mpl3115a2/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mpl3115a2/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mpr121/test.esp32-c3-idf.yaml b/tests/components/mpr121/test.esp32-c3-idf.yaml deleted file mode 100644 index d1abb03369..0000000000 --- a/tests/components/mpr121/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - i2c_scl: GPIO5 - i2c_sda: GPIO4 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mpu6050/test.esp32-c3-idf.yaml b/tests/components/mpu6050/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mpu6050/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mpu6886/test.esp32-c3-idf.yaml b/tests/components/mpu6886/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/mpu6886/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mqtt/test.esp32-c3-idf.yaml b/tests/components/mqtt/test.esp32-c3-idf.yaml deleted file mode 100644 index d19609b55e..0000000000 --- a/tests/components/mqtt/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,3 +0,0 @@ -packages: - common: !include common.yaml - update: !include common-update.yaml diff --git a/tests/components/mqtt_subscribe/test.esp32-c3-idf.yaml b/tests/components/mqtt_subscribe/test.esp32-c3-idf.yaml deleted file mode 100644 index 2cb6d82536..0000000000 --- a/tests/components/mqtt_subscribe/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-idf.yaml diff --git a/tests/components/ms5611/test.esp32-c3-idf.yaml b/tests/components/ms5611/test.esp32-c3-idf.yaml deleted file mode 100644 index d1abb03369..0000000000 --- a/tests/components/ms5611/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - i2c_scl: GPIO5 - i2c_sda: GPIO4 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/msa3xx/test.esp32-c3-idf.yaml b/tests/components/msa3xx/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/msa3xx/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/my9231/test.esp32-c3-idf.yaml b/tests/components/my9231/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/my9231/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/nau7802/test.esp32-c3-idf.yaml b/tests/components/nau7802/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/nau7802/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/network/test-ipv6.esp32-c3-idf.yaml b/tests/components/network/test-ipv6.esp32-c3-idf.yaml deleted file mode 100644 index da1324b17e..0000000000 --- a/tests/components/network/test-ipv6.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - network_enable_ipv6: "true" - -<<: !include common.yaml diff --git a/tests/components/network/test.esp32-c3-idf.yaml b/tests/components/network/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/network/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/nextion/test.esp32-c3-idf.yaml b/tests/components/nextion/test.esp32-c3-idf.yaml deleted file mode 100644 index 888693f909..0000000000 --- a/tests/components/nextion/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - base: !include common.yaml - -display: - - id: !extend main_lcd - tft_url: http://esphome.io/default35.tft diff --git a/tests/components/noblex/test.esp32-c3-idf.yaml b/tests/components/noblex/test.esp32-c3-idf.yaml deleted file mode 100644 index fe77c44eed..0000000000 --- a/tests/components/noblex/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - remote_receiver: !include ../../test_build_components/common/remote_receiver/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-c3-idf.yaml b/tests/components/ntc/test.esp32-c3-idf.yaml deleted file mode 100644 index 37fb325f4a..0000000000 --- a/tests/components/ntc/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/opt3001/test.esp32-c3-idf.yaml b/tests/components/opt3001/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/opt3001/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ota/test.esp32-c3-idf.yaml b/tests/components/ota/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ota/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/output/test.esp32-c3-idf.yaml b/tests/components/output/test.esp32-c3-idf.yaml deleted file mode 100644 index 2227643703..0000000000 --- a/tests/components/output/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO1 - -<<: !include common.yaml diff --git a/tests/components/packet_transport/test.esp32-c3-idf.yaml b/tests/components/packet_transport/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/packet_transport/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pca6416a/test.esp32-c3-idf.yaml b/tests/components/pca6416a/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/pca6416a/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pca9554/test.esp32-c3-idf.yaml b/tests/components/pca9554/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/pca9554/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pca9685/test.esp32-c3-idf.yaml b/tests/components/pca9685/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/pca9685/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pcd8544/test.esp32-c3-idf.yaml b/tests/components/pcd8544/test.esp32-c3-idf.yaml deleted file mode 100644 index b112cf4c31..0000000000 --- a/tests/components/pcd8544/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pcf85063/test.esp32-c3-idf.yaml b/tests/components/pcf85063/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/pcf85063/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pcf8563/test.esp32-c3-idf.yaml b/tests/components/pcf8563/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/pcf8563/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pcf8574/test.esp32-c3-idf.yaml b/tests/components/pcf8574/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/pcf8574/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pid/test.esp32-c3-idf.yaml b/tests/components/pid/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pid/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pipsolar/test.esp32-c3-idf.yaml b/tests/components/pipsolar/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/pipsolar/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pm1006/test.esp32-c3-idf.yaml b/tests/components/pm1006/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/pm1006/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pm2005/test.esp32-c3-idf.yaml b/tests/components/pm2005/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/pm2005/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pmsa003i/test.esp32-c3-idf.yaml b/tests/components/pmsa003i/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/pmsa003i/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pmsx003/test.esp32-c3-idf.yaml b/tests/components/pmsx003/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/pmsx003/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pmwcs3/test.esp32-c3-idf.yaml b/tests/components/pmwcs3/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/pmwcs3/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.esp32-c3-idf.yaml b/tests/components/pn532_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/pn532_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pn532_spi/test.esp32-c3-idf.yaml b/tests/components/pn532_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/pn532_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml b/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index cdf8445263..0000000000 --- a/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - irq_pin: GPIO6 - ven_pin: GPIO7 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml b/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index cdf8445263..0000000000 --- a/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - irq_pin: GPIO6 - ven_pin: GPIO7 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.esp32-c3-idf.yaml b/tests/components/pn7160_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index ac18bfff5c..0000000000 --- a/tests/components/pn7160_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - irq_pin: GPIO9 - ven_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/power_supply/test.esp32-c3-idf.yaml b/tests/components/power_supply/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/power_supply/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/prometheus/test.esp32-c3-idf.yaml b/tests/components/prometheus/test.esp32-c3-idf.yaml deleted file mode 100644 index fedeaf822a..0000000000 --- a/tests/components/prometheus/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - verify_ssl: "false" - -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pulse_meter/test.esp32-c3-idf.yaml b/tests/components/pulse_meter/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pulse_meter/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pulse_width/test.esp32-c3-idf.yaml b/tests/components/pulse_width/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/pulse_width/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/pvvx_mithermometer/test.esp32-c3-idf.yaml b/tests/components/pvvx_mithermometer/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/pvvx_mithermometer/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pylontech/test.esp32-c3-idf.yaml b/tests/components/pylontech/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/pylontech/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pzem004t/test.esp32-c3-idf.yaml b/tests/components/pzem004t/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/pzem004t/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pzemac/test.esp32-c3-idf.yaml b/tests/components/pzemac/test.esp32-c3-idf.yaml deleted file mode 100644 index 6c6e95488f..0000000000 --- a/tests/components/pzemac/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/pzemdc/test.esp32-c3-idf.yaml b/tests/components/pzemdc/test.esp32-c3-idf.yaml deleted file mode 100644 index 6c6e95488f..0000000000 --- a/tests/components/pzemdc/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/qmc5883l/test.esp32-c3-idf.yaml b/tests/components/qmc5883l/test.esp32-c3-idf.yaml deleted file mode 100644 index 854ddc25e7..0000000000 --- a/tests/components/qmc5883l/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - drdy_pin: GPIO6 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/qmp6988/test.esp32-c3-idf.yaml b/tests/components/qmp6988/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/qmp6988/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/qr_code/test.esp32-c3-idf.yaml b/tests/components/qr_code/test.esp32-c3-idf.yaml deleted file mode 100644 index b112cf4c31..0000000000 --- a/tests/components/qr_code/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.esp32-c3-idf.yaml b/tests/components/qwiic_pir/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/qwiic_pir/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/radon_eye_ble/test.esp32-c3-idf.yaml b/tests/components/radon_eye_ble/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/radon_eye_ble/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/radon_eye_rd200/test.esp32-c3-idf.yaml b/tests/components/radon_eye_rd200/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/radon_eye_rd200/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.esp32-c3-idf.yaml b/tests/components/rc522_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/rc522_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/rc522_spi/test.esp32-c3-idf.yaml b/tests/components/rc522_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/rc522_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/rdm6300/test.esp32-c3-idf.yaml b/tests/components/rdm6300/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/rdm6300/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/resampler/test.esp32-c3-idf.yaml b/tests/components/resampler/test.esp32-c3-idf.yaml deleted file mode 100644 index f1721f0862..0000000000 --- a/tests/components/resampler/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - lrclk_pin: GPIO4 - bclk_pin: GPIO5 - mclk_pin: GPIO6 - dout_pin: GPIO7 - -<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-c3-idf.yaml b/tests/components/resistance/test.esp32-c3-idf.yaml deleted file mode 100644 index 37fb325f4a..0000000000 --- a/tests/components/resistance/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/restart/test.esp32-c3-idf.yaml b/tests/components/restart/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/restart/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/rf_bridge/test.esp32-c3-idf.yaml b/tests/components/rf_bridge/test.esp32-c3-idf.yaml deleted file mode 100644 index a19013bf54..0000000000 --- a/tests/components/rf_bridge/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/rgb/test.esp32-c3-idf.yaml b/tests/components/rgb/test.esp32-c3-idf.yaml deleted file mode 100644 index 1fe4a4bb90..0000000000 --- a/tests/components/rgb/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO2 - pin2: GPIO3 - pin3: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/rgbct/test.esp32-c3-idf.yaml b/tests/components/rgbct/test.esp32-c3-idf.yaml deleted file mode 100644 index 27a1fbca4d..0000000000 --- a/tests/components/rgbct/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO2 - pin2: GPIO3 - pin3: GPIO4 - pin4: GPIO5 - pin5: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/rgbw/test.esp32-c3-idf.yaml b/tests/components/rgbw/test.esp32-c3-idf.yaml deleted file mode 100644 index b44734344e..0000000000 --- a/tests/components/rgbw/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO2 - pin2: GPIO3 - pin3: GPIO4 - pin4: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/rgbww/test.esp32-c3-idf.yaml b/tests/components/rgbww/test.esp32-c3-idf.yaml deleted file mode 100644 index 27a1fbca4d..0000000000 --- a/tests/components/rgbww/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - light_platform: ledc - pin1: GPIO2 - pin2: GPIO3 - pin3: GPIO4 - pin4: GPIO5 - pin5: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/rotary_encoder/test.esp32-c3-idf.yaml b/tests/components/rotary_encoder/test.esp32-c3-idf.yaml deleted file mode 100644 index b71a454bdd..0000000000 --- a/tests/components/rotary_encoder/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - pin_a: GPIO2 - pin_b: GPIO3 - pin_reset: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/rtttl/test.esp32-c3-idf.yaml b/tests/components/rtttl/test.esp32-c3-idf.yaml deleted file mode 100644 index 7476963591..0000000000 --- a/tests/components/rtttl/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/ruuvi_ble/test.esp32-c3-idf.yaml b/tests/components/ruuvi_ble/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/ruuvi_ble/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ruuvitag/test.esp32-c3-idf.yaml b/tests/components/ruuvitag/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/ruuvitag/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/safe_mode/test-enabled.esp32-c3-idf.yaml b/tests/components/safe_mode/test-enabled.esp32-c3-idf.yaml deleted file mode 100644 index 97fd63d70e..0000000000 --- a/tests/components/safe_mode/test-enabled.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common-enabled.yaml diff --git a/tests/components/scd30/test.esp32-c3-idf.yaml b/tests/components/scd30/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/scd30/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/scd4x/test.esp32-c3-idf.yaml b/tests/components/scd4x/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/scd4x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/script/test.esp32-c3-idf.yaml b/tests/components/script/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/script/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sdm_meter/test.esp32-c3-idf.yaml b/tests/components/sdm_meter/test.esp32-c3-idf.yaml deleted file mode 100644 index 6c6e95488f..0000000000 --- a/tests/components/sdm_meter/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sdp3x/test.esp32-c3-idf.yaml b/tests/components/sdp3x/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sdp3x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sds011/test.esp32-c3-idf.yaml b/tests/components/sds011/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/sds011/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/seeed_mr24hpc1/test.esp32-c3-idf.yaml b/tests/components/seeed_mr24hpc1/test.esp32-c3-idf.yaml deleted file mode 100644 index a19013bf54..0000000000 --- a/tests/components/seeed_mr24hpc1/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml b/tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml deleted file mode 100644 index fea89f5768..0000000000 --- a/tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - uart_115200: !include ../../test_build_components/common/uart_115200/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml b/tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml deleted file mode 100644 index fea89f5768..0000000000 --- a/tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - uart_115200: !include ../../test_build_components/common/uart_115200/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/selec_meter/test.esp32-c3-idf.yaml b/tests/components/selec_meter/test.esp32-c3-idf.yaml deleted file mode 100644 index beb90e1471..0000000000 --- a/tests/components/selec_meter/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - flow_control_pin: GPIO10 - -packages: - modbus: !include ../../test_build_components/common/modbus/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sen0321/test.esp32-c3-idf.yaml b/tests/components/sen0321/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sen0321/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sen21231/test.esp32-c3-idf.yaml b/tests/components/sen21231/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sen21231/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sen5x/test.esp32-c3-idf.yaml b/tests/components/sen5x/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sen5x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/senseair/test.esp32-c3-idf.yaml b/tests/components/senseair/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/senseair/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/servo/test.esp32-c3-idf.yaml b/tests/components/servo/test.esp32-c3-idf.yaml deleted file mode 100644 index 7476963591..0000000000 --- a/tests/components/servo/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sfa30/test.esp32-c3-idf.yaml b/tests/components/sfa30/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sfa30/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sgp30/test.esp32-c3-idf.yaml b/tests/components/sgp30/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sgp30/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sgp4x/test.esp32-c3-idf.yaml b/tests/components/sgp4x/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sgp4x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sht3xd/test.esp32-c3-idf.yaml b/tests/components/sht3xd/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sht3xd/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sht4x/test.esp32-c3-idf.yaml b/tests/components/sht4x/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sht4x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/shtcx/test.esp32-c3-idf.yaml b/tests/components/shtcx/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/shtcx/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/shutdown/test.esp32-c3-idf.yaml b/tests/components/shutdown/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/shutdown/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sim800l/test.esp32-c3-idf.yaml b/tests/components/sim800l/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/sim800l/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/slow_pwm/test.esp32-c3-idf.yaml b/tests/components/slow_pwm/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/slow_pwm/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sm16716/test.esp32-c3-idf.yaml b/tests/components/sm16716/test.esp32-c3-idf.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/sm16716/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sm2135/test.esp32-c3-idf.yaml b/tests/components/sm2135/test.esp32-c3-idf.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/sm2135/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sm2235/test.esp32-c3-idf.yaml b/tests/components/sm2235/test.esp32-c3-idf.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/sm2235/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sm2335/test.esp32-c3-idf.yaml b/tests/components/sm2335/test.esp32-c3-idf.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/sm2335/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/sm300d2/test.esp32-c3-idf.yaml b/tests/components/sm300d2/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/sm300d2/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sml/test.esp32-c3-idf.yaml b/tests/components/sml/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/sml/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/smt100/test.esp32-c3-idf.yaml b/tests/components/smt100/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/smt100/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sn74hc165/test.esp32-c3-idf.yaml b/tests/components/sn74hc165/test.esp32-c3-idf.yaml deleted file mode 100644 index 0a3db917b7..0000000000 --- a/tests/components/sn74hc165/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clock_pin: GPIO3 - data_pin: GPIO4 - load_pin: GPIO5 - clock_inhibit_pin: GPIO6 - -<<: !include common.yaml diff --git a/tests/components/sn74hc595/test.esp32-c3-idf.yaml b/tests/components/sn74hc595/test.esp32-c3-idf.yaml deleted file mode 100644 index 74b5e855fa..0000000000 --- a/tests/components/sn74hc595/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,12 +0,0 @@ -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -substitutions: - clock_pin: GPIO7 - data_pin: GPIO10 - latch_pin1: GPIO1 - oe_pin1: GPIO2 - latch_pin2: GPIO3 - oe_pin2: GPIO9 - -<<: !include common.yaml diff --git a/tests/components/sntp/test.esp32-c3-idf.yaml b/tests/components/sntp/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sntp/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.esp32-c3-idf.yaml b/tests/components/sonoff_d1/test.esp32-c3-idf.yaml deleted file mode 100644 index a19013bf54..0000000000 --- a/tests/components/sonoff_d1/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sound_level/test.esp32-c3-idf.yaml b/tests/components/sound_level/test.esp32-c3-idf.yaml deleted file mode 100644 index aeb7d9f0af..0000000000 --- a/tests/components/sound_level/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - i2s_bclk_pin: GPIO6 - i2s_lrclk_pin: GPIO7 - i2s_dout_pin: GPIO8 - -<<: !include common.yaml diff --git a/tests/components/speaker/audio_dac.esp32-c3-idf.yaml b/tests/components/speaker/audio_dac.esp32-c3-idf.yaml deleted file mode 100644 index 30900f1920..0000000000 --- a/tests/components/speaker/audio_dac.esp32-c3-idf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -substitutions: - i2s_bclk_pin: GPIO7 - i2s_lrclk_pin: GPIO6 - i2s_mclk_pin: GPIO9 - i2s_dout_pin: GPIO8 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common-audio_dac.yaml diff --git a/tests/components/speaker/test.esp32-c3-idf.yaml b/tests/components/speaker/test.esp32-c3-idf.yaml deleted file mode 100644 index 9d1a1cca3b..0000000000 --- a/tests/components/speaker/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,12 +0,0 @@ -substitutions: - scl_pin: GPIO5 - sda_pin: GPIO4 - i2s_bclk_pin: GPIO7 - i2s_lrclk_pin: GPIO6 - i2s_mclk_pin: GPIO9 - i2s_dout_pin: GPIO8 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/speed/test.esp32-c3-idf.yaml b/tests/components/speed/test.esp32-c3-idf.yaml deleted file mode 100644 index 7476963591..0000000000 --- a/tests/components/speed/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - output_platform: ledc - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/spi_device/test.esp32-c3-idf.yaml b/tests/components/spi_device/test.esp32-c3-idf.yaml deleted file mode 100644 index 6d64c2b23b..0000000000 --- a/tests/components/spi_device/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.esp32-c3-idf.yaml b/tests/components/spi_led_strip/test.esp32-c3-idf.yaml deleted file mode 100644 index 6d64c2b23b..0000000000 --- a/tests/components/spi_led_strip/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sprinkler/test.esp32-c3-idf.yaml b/tests/components/sprinkler/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sprinkler/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sps30/test.esp32-c3-idf.yaml b/tests/components/sps30/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sps30/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml b/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index f8bfab2319..0000000000 --- a/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - reset_pin: GPIO3 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index b112cf4c31..0000000000 --- a/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index b112cf4c31..0000000000 --- a/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index b112cf4c31..0000000000 --- a/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml b/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index f8bfab2319..0000000000 --- a/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - reset_pin: GPIO3 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index b112cf4c31..0000000000 --- a/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index b112cf4c31..0000000000 --- a/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index b112cf4c31..0000000000 --- a/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.esp32-c3-idf.yaml b/tests/components/st7567_i2c/test.esp32-c3-idf.yaml deleted file mode 100644 index f8bfab2319..0000000000 --- a/tests/components/st7567_i2c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - reset_pin: GPIO3 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/st7567_spi/test.esp32-c3-idf.yaml b/tests/components/st7567_spi/test.esp32-c3-idf.yaml deleted file mode 100644 index b112cf4c31..0000000000 --- a/tests/components/st7567_spi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/st7735/test.esp32-c3-idf.yaml b/tests/components/st7735/test.esp32-c3-idf.yaml deleted file mode 100644 index b112cf4c31..0000000000 --- a/tests/components/st7735/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/st7789v/test.esp32-c3-idf.yaml b/tests/components/st7789v/test.esp32-c3-idf.yaml deleted file mode 100644 index b4d70edb31..0000000000 --- a/tests/components/st7789v/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -substitutions: - cs_pin: GPIO8 - dc_pin: GPIO9 - reset_pin: GPIO10 - backlight_pin: GPIO7 - -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/st7920/test.esp32-c3-idf.yaml b/tests/components/st7920/test.esp32-c3-idf.yaml deleted file mode 100644 index 15d986e157..0000000000 --- a/tests/components/st7920/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO8 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/statsD/test.esp32-c3-idf.yaml b/tests/components/statsD/test.esp32-c3-idf.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/statsD/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/status/test.esp32-c3-idf.yaml b/tests/components/status/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/status/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/status_led/test.esp32-c3-idf.yaml b/tests/components/status_led/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/status_led/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/stepper/test.esp32-c3-idf.yaml b/tests/components/stepper/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/stepper/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sts3x/test.esp32-c3-idf.yaml b/tests/components/sts3x/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sts3x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sun/test.esp32-c3-idf.yaml b/tests/components/sun/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/sun/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.esp32-c3-idf.yaml b/tests/components/sun_gtil2/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/sun_gtil2/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/switch/test.esp32-c3-idf.yaml b/tests/components/switch/test.esp32-c3-idf.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/switch/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/sx126x/test.esp32-c3-idf.yaml b/tests/components/sx126x/test.esp32-c3-idf.yaml deleted file mode 100644 index e27f11032e..0000000000 --- a/tests/components/sx126x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -substitutions: - cs_pin: GPIO1 - rst_pin: GPIO2 - busy_pin: GPIO7 - dio1_pin: GPIO3 - -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sx127x/test.esp32-c3-idf.yaml b/tests/components/sx127x/test.esp32-c3-idf.yaml deleted file mode 100644 index dfee192545..0000000000 --- a/tests/components/sx127x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - cs_pin: GPIO1 - rst_pin: GPIO2 - dio0_pin: GPIO3 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/sx1509/test.esp32-c3-idf.yaml b/tests/components/sx1509/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/sx1509/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/syslog/test.esp32-c3-idf.yaml b/tests/components/syslog/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/syslog/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/t6615/test.esp32-c3-idf.yaml b/tests/components/t6615/test.esp32-c3-idf.yaml deleted file mode 100644 index 147d967dd4..0000000000 --- a/tests/components/t6615/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - uart_19200: !include ../../test_build_components/common/uart_19200/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tc74/test.esp32-c3-idf.yaml b/tests/components/tc74/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tc74/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tca9548a/test.esp32-c3-idf.yaml b/tests/components/tca9548a/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tca9548a/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tca9555/test.esp32-c3-idf.yaml b/tests/components/tca9555/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tca9555/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tcl112/test.esp32-c3-idf.yaml b/tests/components/tcl112/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/tcl112/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tcs34725/test.esp32-c3-idf.yaml b/tests/components/tcs34725/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tcs34725/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tee501/test.esp32-c3-idf.yaml b/tests/components/tee501/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tee501/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/teleinfo/test.esp32-c3-idf.yaml b/tests/components/teleinfo/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/teleinfo/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/template/test.esp32-c3-idf.yaml b/tests/components/template/test.esp32-c3-idf.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/template/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/thermostat/test.esp32-c3-idf.yaml b/tests/components/thermostat/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/thermostat/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/time/test.esp32-c3-idf.yaml b/tests/components/time/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/time/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/time_based/test.esp32-c3-idf.yaml b/tests/components/time_based/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/time_based/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/tlc59208f/test.esp32-c3-idf.yaml b/tests/components/tlc59208f/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tlc59208f/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tlc5947/test.esp32-c3-idf.yaml b/tests/components/tlc5947/test.esp32-c3-idf.yaml deleted file mode 100644 index 4694c43642..0000000000 --- a/tests/components/tlc5947/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - lat_pin: GPIO3 - -packages: - common: !include common.yaml diff --git a/tests/components/tlc5971/test.esp32-c3-idf.yaml b/tests/components/tlc5971/test.esp32-c3-idf.yaml deleted file mode 100644 index d898a21d46..0000000000 --- a/tests/components/tlc5971/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -packages: - common: !include common.yaml diff --git a/tests/components/tm1621/test.esp32-c3-idf.yaml b/tests/components/tm1621/test.esp32-c3-idf.yaml deleted file mode 100644 index 562ced7485..0000000000 --- a/tests/components/tm1621/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - cs_pin: GPIO6 - data_pin: GPIO7 - read_pin: GPIO2 - write_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/tm1637/test.esp32-c3-idf.yaml b/tests/components/tm1637/test.esp32-c3-idf.yaml deleted file mode 100644 index 0c4d4a9a7a..0000000000 --- a/tests/components/tm1637/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clk_pin: GPIO7 - dio_pin: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/tm1638/test.esp32-c3-idf.yaml b/tests/components/tm1638/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/tm1638/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/tm1651/test.esp32-c3-idf.yaml b/tests/components/tm1651/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/tm1651/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/tmp102/test.esp32-c3-idf.yaml b/tests/components/tmp102/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tmp102/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tmp1075/test.esp32-c3-idf.yaml b/tests/components/tmp1075/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tmp1075/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tmp117/test.esp32-c3-idf.yaml b/tests/components/tmp117/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tmp117/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tof10120/test.esp32-c3-idf.yaml b/tests/components/tof10120/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tof10120/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tormatic/test.esp32-c3-idf.yaml b/tests/components/tormatic/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/tormatic/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/toshiba/test.esp32-c3-idf.yaml b/tests/components/toshiba/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/toshiba/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/toshiba/test_ras2819t.esp32-c3-idf.yaml b/tests/components/toshiba/test_ras2819t.esp32-c3-idf.yaml deleted file mode 100644 index 00805baa01..0000000000 --- a/tests/components/toshiba/test_ras2819t.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - tx_pin: GPIO5 - rx_pin: GPIO4 - -<<: !include common_ras2819t.yaml diff --git a/tests/components/total_daily_energy/test.esp32-c3-idf.yaml b/tests/components/total_daily_energy/test.esp32-c3-idf.yaml deleted file mode 100644 index 8b0d069ce2..0000000000 --- a/tests/components/total_daily_energy/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - sel_pin: GPIO2 - cf_pin: GPIO3 - cf1_pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/tsl2561/test.esp32-c3-idf.yaml b/tests/components/tsl2561/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tsl2561/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tsl2591/test.esp32-c3-idf.yaml b/tests/components/tsl2591/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/tsl2591/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tt21100/test.esp32-c3-idf.yaml b/tests/components/tt21100/test.esp32-c3-idf.yaml deleted file mode 100644 index a7265e10b2..0000000000 --- a/tests/components/tt21100/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - disp_reset_pin: GPIO7 - interrupt_pin: GPIO2 - reset_pin: GPIO3 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml b/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml deleted file mode 100644 index ad1c58b40e..0000000000 --- a/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - ttp229_scl_pin: GPIO7 - ttp229_sdo_pin: GPIO4 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml b/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tuya/test.esp32-c3-idf.yaml b/tests/components/tuya/test.esp32-c3-idf.yaml deleted file mode 100644 index 43c28ba7b3..0000000000 --- a/tests/components/tuya/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - status_pin: GPIO2 -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/tx20/test.esp32-c3-idf.yaml b/tests/components/tx20/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/tx20/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/udp/test.esp32-c3-idf.yaml b/tests/components/udp/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/udp/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ufire_ec/test.esp32-c3-idf.yaml b/tests/components/ufire_ec/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ufire_ec/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ufire_ise/test.esp32-c3-idf.yaml b/tests/components/ufire_ise/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/ufire_ise/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/uln2003/test.esp32-c3-idf.yaml b/tests/components/uln2003/test.esp32-c3-idf.yaml deleted file mode 100644 index 11d16a4d5d..0000000000 --- a/tests/components/uln2003/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -substitutions: - pin_a: GPIO0 - pin_b: GPIO1 - pin_c: GPIO2 - pin_d: GPIO3 - -<<: !include common.yaml diff --git a/tests/components/ultrasonic/test.esp32-c3-idf.yaml b/tests/components/ultrasonic/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/ultrasonic/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/uponor_smatrix/test.esp32-c3-idf.yaml b/tests/components/uponor_smatrix/test.esp32-c3-idf.yaml deleted file mode 100644 index cd26c783c2..0000000000 --- a/tests/components/uponor_smatrix/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -packages: - uart_19200: !include ../../test_build_components/common/uart_19200/esp32-c3-idf.yaml - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/uptime/test.esp32-c3-idf.yaml b/tests/components/uptime/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/uptime/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/vbus/test.esp32-c3-idf.yaml b/tests/components/vbus/test.esp32-c3-idf.yaml deleted file mode 100644 index a19013bf54..0000000000 --- a/tests/components/vbus/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/veml3235/test.esp32-c3-idf.yaml b/tests/components/veml3235/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/veml3235/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/veml7700/test.esp32-c3-idf.yaml b/tests/components/veml7700/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/veml7700/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/version/test.esp32-c3-idf.yaml b/tests/components/version/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/version/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/vl53l0x/test.esp32-c3-idf.yaml b/tests/components/vl53l0x/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/vl53l0x/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/voice_assistant/test.esp32-c3-idf.yaml b/tests/components/voice_assistant/test.esp32-c3-idf.yaml deleted file mode 100644 index 46745e4308..0000000000 --- a/tests/components/voice_assistant/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - i2s_lrclk_pin: GPIO6 - i2s_bclk_pin: GPIO7 - i2s_mclk_pin: GPIO5 - i2s_din_pin: GPIO3 - i2s_dout_pin: GPIO2 - -<<: !include common-idf.yaml diff --git a/tests/components/wake_on_lan/test.esp32-c3-idf.yaml b/tests/components/wake_on_lan/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/wake_on_lan/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml b/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml deleted file mode 100644 index bdd7e1d350..0000000000 --- a/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - cs_pin: GPIO7 - dc_pin: GPIO1 - busy_pin: GPIO2 - reset_pin: GPIO3 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/web_server/test.esp32-c3-idf.yaml b/tests/components/web_server/test.esp32-c3-idf.yaml deleted file mode 100644 index 7e6658e20e..0000000000 --- a/tests/components/web_server/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common_v2.yaml diff --git a/tests/components/whirlpool/test.esp32-c3-idf.yaml b/tests/components/whirlpool/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/whirlpool/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/whynter/test.esp32-c3-idf.yaml b/tests/components/whynter/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/whynter/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wiegand/test.esp32-c3-idf.yaml b/tests/components/wiegand/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wiegand/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wifi/test.esp32-c3-idf.yaml b/tests/components/wifi/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wifi/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wifi_info/test.esp32-c3-idf.yaml b/tests/components/wifi_info/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/wifi_info/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wifi_signal/test.esp32-c3-idf.yaml b/tests/components/wifi_signal/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/wifi_signal/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/wireguard/test.esp32-c3-idf.yaml b/tests/components/wireguard/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/wireguard/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wl_134/test.esp32-c3-idf.yaml b/tests/components/wl_134/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/wl_134/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wts01/test.esp32-c3-idf.yaml b/tests/components/wts01/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/wts01/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/x9c/test.esp32-c3-idf.yaml b/tests/components/x9c/test.esp32-c3-idf.yaml deleted file mode 100644 index b06e15a98c..0000000000 --- a/tests/components/x9c/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,6 +0,0 @@ -substitutions: - cs_pin: GPIO3 - inc_pin: GPIO4 - ud_pin: GPIO5 - -<<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.esp32-c3-idf.yaml b/tests/components/xgzp68xx/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/xgzp68xx/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_ble/test.esp32-c3-idf.yaml b/tests/components/xiaomi_ble/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_ble/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgd1/test.esp32-c3-idf.yaml b/tests/components/xiaomi_cgd1/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_cgd1/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgdk2/test.esp32-c3-idf.yaml b/tests/components/xiaomi_cgdk2/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_cgdk2/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgg1/test.esp32-c3-idf.yaml b/tests/components/xiaomi_cgg1/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_cgg1/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_cgpr1/test.esp32-c3-idf.yaml b/tests/components/xiaomi_cgpr1/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_cgpr1/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_gcls002/test.esp32-c3-idf.yaml b/tests/components/xiaomi_gcls002/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_gcls002/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_hhccjcy01/test.esp32-c3-idf.yaml b/tests/components/xiaomi_hhccjcy01/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_hhccjcy01/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_hhccpot002/test.esp32-c3-idf.yaml b/tests/components/xiaomi_hhccpot002/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_hhccpot002/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_jqjcy01ym/test.esp32-c3-idf.yaml b/tests/components/xiaomi_jqjcy01ym/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_jqjcy01ym/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02/test.esp32-c3-idf.yaml b/tests/components/xiaomi_lywsd02/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_lywsd02/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml b/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd03mmc/test.esp32-c3-idf.yaml b/tests/components/xiaomi_lywsd03mmc/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_lywsd03mmc/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsdcgq/test.esp32-c3-idf.yaml b/tests/components/xiaomi_lywsdcgq/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_lywsdcgq/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_mhoc303/test.esp32-c3-idf.yaml b/tests/components/xiaomi_mhoc303/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_mhoc303/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_mhoc401/test.esp32-c3-idf.yaml b/tests/components/xiaomi_mhoc401/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_mhoc401/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_miscale/test.esp32-c3-idf.yaml b/tests/components/xiaomi_miscale/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_miscale/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_mjyd02yla/test.esp32-c3-idf.yaml b/tests/components/xiaomi_mjyd02yla/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_mjyd02yla/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_mue4094rt/test.esp32-c3-idf.yaml b/tests/components/xiaomi_mue4094rt/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_mue4094rt/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_rtcgq02lm/test.esp32-c3-idf.yaml b/tests/components/xiaomi_rtcgq02lm/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_rtcgq02lm/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_wx08zm/test.esp32-c3-idf.yaml b/tests/components/xiaomi_wx08zm/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_wx08zm/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml b/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml deleted file mode 100644 index 9f2634f967..0000000000 --- a/tests/components/xiaomi_xmwsdj04mmc/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - ble: !include ../../test_build_components/common/ble/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xl9535/test.esp32-c3-idf.yaml b/tests/components/xl9535/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/xl9535/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/xpt2046/test.esp32-c3-idf.yaml b/tests/components/xpt2046/test.esp32-c3-idf.yaml deleted file mode 100644 index ff7a32c26c..0000000000 --- a/tests/components/xpt2046/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -substitutions: - dc_pin: GPIO7 - cs_pin: GPIO0 - disp_cs_pin: GPIO1 - interrupt_pin: GPIO3 - reset_pin: GPIO10 -packages: - spi: !include ../../test_build_components/common/spi/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/yashima/test.esp32-c3-idf.yaml b/tests/components/yashima/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/yashima/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/zhlt01/test.esp32-c3-idf.yaml b/tests/components/zhlt01/test.esp32-c3-idf.yaml deleted file mode 100644 index 43d5343715..0000000000 --- a/tests/components/zhlt01/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml b/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml deleted file mode 100644 index 9990d96d29..0000000000 --- a/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/zwave_proxy/test.esp32-c3-idf.yaml b/tests/components/zwave_proxy/test.esp32-c3-idf.yaml deleted file mode 100644 index 4b7c8351a7..0000000000 --- a/tests/components/zwave_proxy/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: -packages: - uart: !include ../../test_build_components/common/uart/esp32-c3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/zyaura/test.esp32-c3-idf.yaml b/tests/components/zyaura/test.esp32-c3-idf.yaml deleted file mode 100644 index 7808481215..0000000000 --- a/tests/components/zyaura/test.esp32-c3-idf.yaml +++ /dev/null @@ -1,5 +0,0 @@ -substitutions: - clock_pin: GPIO5 - data_pin: GPIO4 - -<<: !include common.yaml From 182e106bfa31c08024f6e9de702651c4c9f4bc31 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 6 Nov 2025 20:44:40 -0600 Subject: [PATCH 420/526] [wifi] Guard AP-related members with USE_WIFI_AP to save RAM (#11753) --- esphome/components/wifi/wifi_component.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index ac63e0eb0c..ef595e9891 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -233,6 +233,7 @@ class WiFiComponent : public Component { */ void set_ap(const WiFiAP &ap); WiFiAP get_ap() { return this->ap_; } + void set_ap_timeout(uint32_t ap_timeout) { ap_timeout_ = ap_timeout; } #endif // USE_WIFI_AP void enable(); @@ -241,7 +242,6 @@ class WiFiComponent : public Component { void start_scanning(); void check_scanning_finished(); void start_connecting(const WiFiAP &ap, bool two); - void set_ap_timeout(uint32_t ap_timeout) { ap_timeout_ = ap_timeout; } void check_connecting_finished(); @@ -397,7 +397,9 @@ class WiFiComponent : public Component { std::vector sta_priorities_; wifi_scan_vector_t scan_result_; WiFiAP selected_ap_; +#ifdef USE_WIFI_AP WiFiAP ap_; +#endif optional output_power_; ESPPreferenceObject pref_; #ifdef USE_WIFI_FAST_CONNECT @@ -408,7 +410,9 @@ class WiFiComponent : public Component { uint32_t action_started_; uint32_t last_connected_{0}; uint32_t reboot_timeout_{}; +#ifdef USE_WIFI_AP uint32_t ap_timeout_{}; +#endif // Group all 8-bit values together WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF}; From 7c30d57391ff082e610c575b547298255b4090b0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 6 Nov 2025 21:26:53 -0600 Subject: [PATCH 421/526] [wifi] Refactor AP selection to use index instead of copy (saves 88 bytes) (#11749) --- esphome/components/wifi/__init__.py | 8 +- esphome/components/wifi/wifi_component.cpp | 219 +++++++++++++-------- esphome/components/wifi/wifi_component.h | 41 +++- 3 files changed, 177 insertions(+), 91 deletions(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index b980bab4aa..5f4190a933 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -54,6 +54,10 @@ AUTO_LOAD = ["network"] NO_WIFI_VARIANTS = [const.VARIANT_ESP32H2, const.VARIANT_ESP32P4] CONF_SAVE = "save" +# Maximum number of WiFi networks that can be configured +# Limited to 127 because selected_sta_index_ is int8_t in C++ +MAX_WIFI_NETWORKS = 127 + wifi_ns = cg.esphome_ns.namespace("wifi") EAPAuth = wifi_ns.struct("EAPAuth") ManualIP = wifi_ns.struct("ManualIP") @@ -260,7 +264,9 @@ CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(WiFiComponent), - cv.Optional(CONF_NETWORKS): cv.ensure_list(WIFI_NETWORK_STA), + cv.Optional(CONF_NETWORKS): cv.All( + cv.ensure_list(WIFI_NETWORK_STA), cv.Length(max=MAX_WIFI_NETWORKS) + ), cv.Optional(CONF_SSID): cv.ssid, cv.Optional(CONF_PASSWORD): validate_password, cv.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 51b5756f29..789c22bae1 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1,5 +1,6 @@ #include "wifi_component.h" #ifdef USE_WIFI +#include #include #ifdef USE_ESP32 @@ -109,12 +110,15 @@ void WiFiComponent::start() { } #ifdef USE_WIFI_FAST_CONNECT - this->trying_loaded_ap_ = this->load_fast_connect_settings_(); + WiFiAP params; + this->trying_loaded_ap_ = this->load_fast_connect_settings_(params); if (!this->trying_loaded_ap_) { - this->ap_index_ = 0; - this->selected_ap_ = this->sta_[this->ap_index_]; + // FAST CONNECT FALLBACK: No saved settings available + // Use first config (will use SSID from config) + this->selected_sta_index_ = 0; + params = this->build_wifi_ap_from_selected_(); } - this->start_connecting(this->selected_ap_, false); + this->start_connecting(params, false); #else this->start_scanning(); #endif @@ -169,15 +173,16 @@ void WiFiComponent::loop() { this->status_set_warning(LOG_STR("waiting to reconnect")); if (millis() - this->action_started_ > 5000) { #ifdef USE_WIFI_FAST_CONNECT - // NOTE: This check may not make sense here as it could interfere with AP cycling - if (!this->selected_ap_.get_bssid().has_value()) - this->selected_ap_ = this->sta_[0]; - this->start_connecting(this->selected_ap_, false); + // Safety check: Ensure selected_sta_index_ is valid before retrying + // (should already be set by retry_connect(), but check for robustness) + this->reset_selected_ap_to_first_if_invalid_(); + WiFiAP params = this->build_wifi_ap_from_selected_(); + this->start_connecting(params, false); #else if (this->retry_hidden_) { - if (!this->selected_ap_.get_bssid().has_value()) - this->selected_ap_ = this->sta_[0]; - this->start_connecting(this->selected_ap_, false); + this->reset_selected_ap_to_first_if_invalid_(); + WiFiAP params = this->build_wifi_ap_from_selected_(); + this->start_connecting(params, false); } else { this->start_scanning(); } @@ -336,8 +341,42 @@ void WiFiComponent::set_sta(const WiFiAP &ap) { this->clear_sta(); this->init_sta(1); this->add_sta(ap); + this->selected_sta_index_ = 0; +} + +WiFiAP WiFiComponent::build_wifi_ap_from_selected_() const { + // PRECONDITION: selected_sta_index_ must be valid (ensured by all callers) + const WiFiAP *config = this->get_selected_sta_(); + assert(config != nullptr); + WiFiAP params = *config; + + // SYNCHRONIZATION: selected_sta_index_ and scan_result_[0] are kept in sync after wifi_scan_done(): + // - wifi_scan_done() sorts all scan results by priority/RSSI (best first) + // - It then finds which sta_[i] config matches scan_result_[0] + // - Sets selected_sta_index_ = i to record that matching config + // This sync holds until scan_result_ is cleared (e.g., after connection or in reset_for_next_ap_attempt_()) + if (!this->scan_result_.empty()) { + // Override with scan data - network is visible + const WiFiScanResult &scan = this->scan_result_[0]; + params.set_hidden(false); + params.set_ssid(scan.get_ssid()); + params.set_bssid(scan.get_bssid()); + params.set_channel(scan.get_channel()); + } else if (params.get_hidden()) { + // Hidden network - clear BSSID and channel even if set in config + // There might be multiple hidden networks with same SSID but we can't know which is correct + // Rely on probe-req with just SSID. Empty channel triggers ALL_CHANNEL_SCAN. + params.set_bssid(optional{}); + params.set_channel(optional{}); + } + + return params; +} + +WiFiAP WiFiComponent::get_sta() const { + const WiFiAP *config = this->get_selected_sta_(); + return config ? *config : WiFiAP{}; } -void WiFiComponent::clear_sta() { this->sta_.clear(); } void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &password) { SavedWifiSettings save{}; // zero-initialized - all bytes set to \0, guaranteeing null termination strncpy(save.ssid, ssid.c_str(), sizeof(save.ssid) - 1); // max 32 chars, byte 32 remains \0 @@ -485,8 +524,8 @@ void WiFiComponent::print_connect_params_() { LOG_STR_ARG(get_signal_bars(rssi)), get_wifi_channel(), wifi_subnet_mask_().str().c_str(), wifi_gateway_ip_().str().c_str(), wifi_dns_ip_(0).str().c_str(), wifi_dns_ip_(1).str().c_str()); #ifdef ESPHOME_LOG_HAS_VERBOSE - if (this->selected_ap_.get_bssid().has_value()) { - ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*this->selected_ap_.get_bssid())); + if (const WiFiAP *config = this->get_selected_sta_(); config && config->get_bssid().has_value()) { + ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*config->get_bssid())); } #endif #ifdef USE_WIFI_11KV_SUPPORT @@ -633,55 +672,38 @@ void WiFiComponent::check_scanning_finished() { log_scan_result(res); } - if (!this->scan_result_[0].get_matches()) { + // SYNCHRONIZATION POINT: Establish link between scan_result_[0] and selected_sta_index_ + // After sorting, scan_result_[0] contains the best network. Now find which sta_[i] config + // matches that network and record it in selected_sta_index_. This keeps the two indices + // synchronized so build_wifi_ap_from_selected_() can safely use both to build connection parameters. + const WiFiScanResult &scan_res = this->scan_result_[0]; + if (!scan_res.get_matches()) { ESP_LOGW(TAG, "No matching network found"); this->retry_connect(); return; } - // Build connection params directly into selected_ap_ to avoid extra copy - const WiFiScanResult &scan_res = this->scan_result_[0]; - WiFiAP &selected = this->selected_ap_; - for (auto &config : this->sta_) { - // search for matching STA config, at least one will match (from checks before) - if (!scan_res.matches(config)) { - continue; + bool found_match = false; + for (size_t i = 0; i < this->sta_.size(); i++) { + if (scan_res.matches(this->sta_[i])) { + // Safe cast: sta_.size() limited to MAX_WIFI_NETWORKS (127) in __init__.py validation + // No overflow check needed - YAML validation prevents >127 networks + this->selected_sta_index_ = static_cast(i); // Links scan_result_[0] with sta_[i] + found_match = true; + break; } + } - if (config.get_hidden()) { - // selected network is hidden, we use the data from the config - selected.set_hidden(true); - selected.set_ssid(config.get_ssid()); - // Clear channel and BSSID for hidden networks - there might be multiple hidden networks - // but we can't know which one is the correct one. Rely on probe-req with just SSID. - selected.set_channel(0); - selected.set_bssid(optional{}); - } else { - // selected network is visible, we use the data from the scan - // limit the connect params to only connect to exactly this network - // (network selection is done during scan phase). - selected.set_hidden(false); - selected.set_ssid(scan_res.get_ssid()); - selected.set_channel(scan_res.get_channel()); - selected.set_bssid(scan_res.get_bssid()); - } - // copy manual IP (if set) - selected.set_manual_ip(config.get_manual_ip()); - -#ifdef USE_WIFI_WPA2_EAP - // copy EAP parameters (if set) - selected.set_eap(config.get_eap()); -#endif - - // copy password (if set) - selected.set_password(config.get_password()); - - break; + if (!found_match) { + ESP_LOGW(TAG, "No matching network found"); + this->retry_connect(); + return; } yield(); - this->start_connecting(this->selected_ap_, false); + WiFiAP params = this->build_wifi_ap_from_selected_(); + this->start_connecting(params, false); } void WiFiComponent::dump_config() { @@ -700,9 +722,12 @@ void WiFiComponent::check_connecting_finished() { } ESP_LOGI(TAG, "Connected"); - // We won't retry hidden networks unless a reconnect fails more than three times again - if (this->retry_hidden_ && !this->selected_ap_.get_hidden()) - ESP_LOGW(TAG, "Network '%s' should be marked as hidden", this->selected_ap_.get_ssid().c_str()); + // Warn if we had to retry with hidden network mode for a network that's not marked hidden + // Only warn if we actually connected without scan data (SSID only), not if scan succeeded on retry + if (const WiFiAP *config = this->get_selected_sta_(); + this->retry_hidden_ && config && !config->get_hidden() && this->scan_result_.empty()) { + ESP_LOGW(TAG, "Network '%s' should be marked as hidden", config->get_ssid().c_str()); + } this->retry_hidden_ = false; this->print_connect_params_(); @@ -725,16 +750,16 @@ void WiFiComponent::check_connecting_finished() { this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTED; this->num_retried_ = 0; +#ifdef USE_WIFI_FAST_CONNECT + this->save_fast_connect_settings_(); +#endif + // Free scan results memory unless a component needs them if (!this->keep_scan_results_) { this->scan_result_.clear(); this->scan_result_.shrink_to_fit(); } -#ifdef USE_WIFI_FAST_CONNECT - this->save_fast_connect_settings_(); -#endif - return; } @@ -772,8 +797,8 @@ void WiFiComponent::check_connecting_finished() { } void WiFiComponent::retry_connect() { - if (this->selected_ap_.get_bssid()) { - auto bssid = *this->selected_ap_.get_bssid(); + if (const WiFiAP *config = this->get_selected_sta_(); config && config->get_bssid()) { + auto bssid = *config->get_bssid(); float priority = this->get_sta_priority(bssid); this->set_sta_priority(bssid, priority - 1.0f); } @@ -782,19 +807,26 @@ void WiFiComponent::retry_connect() { if (!this->is_captive_portal_active_() && !this->is_esp32_improv_active_() && (this->num_retried_ > 3 || this->error_from_callback_)) { #ifdef USE_WIFI_FAST_CONNECT + // No empty check needed - YAML validation requires at least one network for fast_connect if (this->trying_loaded_ap_) { this->trying_loaded_ap_ = false; - this->ap_index_ = 0; // Retry from the first configured AP - } else if (this->ap_index_ >= this->sta_.size() - 1) { + this->selected_sta_index_ = 0; // Retry from the first configured AP + this->reset_for_next_ap_attempt_(); + } else if (this->selected_sta_index_ >= static_cast(this->sta_.size()) - 1) { + // Safe cast: sta_.size() limited to MAX_WIFI_NETWORKS (127) in __init__.py validation + // Exhausted all configured APs, restart adapter and cycle back to first + // Restart clears any stuck WiFi driver state + // Each AP is tried with config data only (SSID + optional BSSID/channel if user configured them) + // Typically SSID only, which triggers ESP-IDF internal scanning ESP_LOGW(TAG, "No more APs to try"); - this->ap_index_ = 0; + this->selected_sta_index_ = 0; + this->reset_for_next_ap_attempt_(); this->restart_adapter(); } else { // Try next AP - this->ap_index_++; + this->selected_sta_index_++; + this->reset_for_next_ap_attempt_(); } - this->num_retried_ = 0; - this->selected_ap_ = this->sta_[this->ap_index_]; #else if (this->num_retried_ > 5) { // If retry failed for more than 5 times, let's restart STA @@ -813,7 +845,8 @@ void WiFiComponent::retry_connect() { if (this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTING) { yield(); this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING_2; - this->start_connecting(this->selected_ap_, true); + WiFiAP params = this->build_wifi_ap_from_selected_(); + this->start_connecting(params, true); return; } @@ -852,16 +885,29 @@ bool WiFiComponent::is_esp32_improv_active_() { } #ifdef USE_WIFI_FAST_CONNECT -bool WiFiComponent::load_fast_connect_settings_() { +bool WiFiComponent::load_fast_connect_settings_(WiFiAP ¶ms) { SavedWifiFastConnectSettings fast_connect_save{}; if (this->fast_connect_pref_.load(&fast_connect_save)) { + // Validate saved AP index + if (fast_connect_save.ap_index < 0 || static_cast(fast_connect_save.ap_index) >= this->sta_.size()) { + ESP_LOGW(TAG, "AP index out of bounds"); + return false; + } + + // Set selected index for future operations (save, retry, etc) + this->selected_sta_index_ = fast_connect_save.ap_index; + + // Copy entire config, then override with fast connect data + params = this->sta_[fast_connect_save.ap_index]; + + // Override with saved BSSID/channel from fast connect (SSID/password/etc already copied from config) bssid_t bssid{}; std::copy(fast_connect_save.bssid, fast_connect_save.bssid + 6, bssid.begin()); - this->ap_index_ = fast_connect_save.ap_index; - this->selected_ap_ = this->sta_[this->ap_index_]; - this->selected_ap_.set_bssid(bssid); - this->selected_ap_.set_channel(fast_connect_save.channel); + params.set_bssid(bssid); + params.set_channel(fast_connect_save.channel); + // Fast connect uses specific BSSID+channel, not hidden network probe (even if config has hidden: true) + params.set_hidden(false); ESP_LOGD(TAG, "Loaded fast_connect settings"); return true; @@ -873,18 +919,25 @@ bool WiFiComponent::load_fast_connect_settings_() { void WiFiComponent::save_fast_connect_settings_() { bssid_t bssid = wifi_bssid(); uint8_t channel = get_wifi_channel(); + // selected_sta_index_ is always valid here (called only after successful connection) + // Fallback to 0 is defensive programming for robustness + int8_t ap_index = this->selected_sta_index_ >= 0 ? this->selected_sta_index_ : 0; - if (bssid != this->selected_ap_.get_bssid() || channel != this->selected_ap_.get_channel()) { - SavedWifiFastConnectSettings fast_connect_save{}; - - memcpy(fast_connect_save.bssid, bssid.data(), 6); - fast_connect_save.channel = channel; - fast_connect_save.ap_index = this->ap_index_; - - this->fast_connect_pref_.save(&fast_connect_save); - - ESP_LOGD(TAG, "Saved fast_connect settings"); + // Skip save if settings haven't changed (compare with previously saved settings to reduce flash wear) + SavedWifiFastConnectSettings previous_save{}; + if (this->fast_connect_pref_.load(&previous_save) && memcmp(previous_save.bssid, bssid.data(), 6) == 0 && + previous_save.channel == channel && previous_save.ap_index == ap_index) { + return; // No change, nothing to save } + + SavedWifiFastConnectSettings fast_connect_save{}; + memcpy(fast_connect_save.bssid, bssid.data(), 6); + fast_connect_save.channel = channel; + fast_connect_save.ap_index = ap_index; + + this->fast_connect_pref_.save(&fast_connect_save); + + ESP_LOGD(TAG, "Saved fast_connect settings"); } #endif diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index ef595e9891..cb75edf5a0 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -218,10 +218,14 @@ class WiFiComponent : public Component { WiFiComponent(); void set_sta(const WiFiAP &ap); - WiFiAP get_sta() { return this->selected_ap_; } + // Returns a copy of the currently selected AP configuration + WiFiAP get_sta() const; void init_sta(size_t count); void add_sta(const WiFiAP &ap); - void clear_sta(); + void clear_sta() { + this->sta_.clear(); + this->selected_sta_index_ = -1; + } #ifdef USE_WIFI_AP /** Setup an Access Point that should be created if no connection to a station can be made. @@ -337,6 +341,29 @@ class WiFiComponent : public Component { #endif // USE_WIFI_AP void print_connect_params_(); + WiFiAP build_wifi_ap_from_selected_() const; + + const WiFiAP *get_selected_sta_() const { + if (this->selected_sta_index_ >= 0 && static_cast(this->selected_sta_index_) < this->sta_.size()) { + return &this->sta_[this->selected_sta_index_]; + } + return nullptr; + } + + void reset_selected_ap_to_first_if_invalid_() { + if (this->selected_sta_index_ < 0 || static_cast(this->selected_sta_index_) >= this->sta_.size()) { + this->selected_sta_index_ = this->sta_.empty() ? -1 : 0; + } + } + +#ifdef USE_WIFI_FAST_CONNECT + // Reset state for next fast connect AP attempt + // Clears old scan data so the new AP is tried with config only (SSID without specific BSSID/channel) + void reset_for_next_ap_attempt_() { + this->num_retried_ = 0; + this->scan_result_.clear(); + } +#endif void wifi_loop_(); bool wifi_mode_(optional sta, optional ap); @@ -365,7 +392,7 @@ class WiFiComponent : public Component { bool is_esp32_improv_active_(); #ifdef USE_WIFI_FAST_CONNECT - bool load_fast_connect_settings_(); + bool load_fast_connect_settings_(WiFiAP ¶ms); void save_fast_connect_settings_(); #endif @@ -396,7 +423,6 @@ class WiFiComponent : public Component { FixedVector sta_; std::vector sta_priorities_; wifi_scan_vector_t scan_result_; - WiFiAP selected_ap_; #ifdef USE_WIFI_AP WiFiAP ap_; #endif @@ -418,9 +444,10 @@ class WiFiComponent : public Component { WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF}; WiFiPowerSaveMode power_save_{WIFI_POWER_SAVE_NONE}; uint8_t num_retried_{0}; -#ifdef USE_WIFI_FAST_CONNECT - uint8_t ap_index_{0}; -#endif + // Index into sta_ array for the currently selected AP configuration (-1 = none selected) + // Used to access password, manual_ip, priority, EAP settings, and hidden flag + // int8_t limits to 127 APs (enforced in __init__.py via MAX_WIFI_NETWORKS) + int8_t selected_sta_index_{-1}; #if USE_NETWORK_IPV6 uint8_t num_ipv6_addresses_{0}; #endif /* USE_NETWORK_IPV6 */ From 3c41e080c578f33f4b6d1fcecd5e024b2f30dbab Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 6 Nov 2025 21:37:02 -0600 Subject: [PATCH 422/526] [core] Use ESPDEPRECATED macro for deprecation warnings (#11755) --- esphome/components/select/select.h | 2 +- esphome/core/entity_base.h | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/esphome/components/select/select.h b/esphome/components/select/select.h index f859594cd1..7459c9d146 100644 --- a/esphome/components/select/select.h +++ b/esphome/components/select/select.h @@ -35,7 +35,7 @@ class Select : public EntityBase { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" /// @deprecated Use current_option() instead. This member will be removed in ESPHome 2026.5.0. - __attribute__((deprecated("Use current_option() instead of .state. Will be removed in 2026.5.0"))) + ESPDEPRECATED("Use current_option() instead of .state. Will be removed in 2026.5.0", "2025.11.0") std::string state{}; Select() = default; diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index 6e5362464f..1486ff5360 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -61,9 +61,10 @@ class EntityBase { } // Get/set this entity's icon - [[deprecated("Use get_icon_ref() instead for better performance (avoids string copy). Will stop working in ESPHome " - "2026.5.0")]] std::string - get_icon() const; + ESPDEPRECATED( + "Use get_icon_ref() instead for better performance (avoids string copy). Will be removed in ESPHome 2026.5.0", + "2025.11.0") + std::string get_icon() const; void set_icon(const char *icon); StringRef get_icon_ref() const { static constexpr auto EMPTY_STRING = StringRef::from_lit(""); @@ -160,9 +161,10 @@ class EntityBase { class EntityBase_DeviceClass { // NOLINT(readability-identifier-naming) public: /// Get the device class, using the manual override if set. - [[deprecated("Use get_device_class_ref() instead for better performance (avoids string copy). Will stop working in " - "ESPHome 2026.5.0")]] std::string - get_device_class(); + ESPDEPRECATED("Use get_device_class_ref() instead for better performance (avoids string copy). Will be removed in " + "ESPHome 2026.5.0", + "2025.11.0") + std::string get_device_class(); /// Manually set the device class. void set_device_class(const char *device_class); /// Get the device class as StringRef @@ -178,9 +180,10 @@ class EntityBase_DeviceClass { // NOLINT(readability-identifier-naming) class EntityBase_UnitOfMeasurement { // NOLINT(readability-identifier-naming) public: /// Get the unit of measurement, using the manual override if set. - [[deprecated("Use get_unit_of_measurement_ref() instead for better performance (avoids string copy). Will stop " - "working in ESPHome 2026.5.0")]] std::string - get_unit_of_measurement(); + ESPDEPRECATED("Use get_unit_of_measurement_ref() instead for better performance (avoids string copy). Will be " + "removed in ESPHome 2026.5.0", + "2025.11.0") + std::string get_unit_of_measurement(); /// Manually set the unit of measurement. void set_unit_of_measurement(const char *unit_of_measurement); /// Get the unit of measurement as StringRef From 4f08f0750a66299d4de5e562b4b406ce14bf22f6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 6 Nov 2025 22:34:53 -0600 Subject: [PATCH 423/526] [ai_instructions] Add public API and breaking changes guidelines (#11756) --- .ai/instructions.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.ai/instructions.md b/.ai/instructions.md index 9309c67c65..8d81c6cf0f 100644 --- a/.ai/instructions.md +++ b/.ai/instructions.md @@ -440,3 +440,45 @@ This document provides essential context for AI models interacting with this pro * **Python:** When adding a new Python dependency, add it to the appropriate `requirements*.txt` file and `pyproject.toml`. * **C++ / PlatformIO:** When adding a new C++ dependency, add it to `platformio.ini` and use `cg.add_library`. * **Build Flags:** Use `cg.add_build_flag(...)` to add compiler flags. + +## 8. Public API and Breaking Changes + +* **Public C++ API:** + * **Components**: Only documented features at [esphome.io](https://esphome.io) are public API. Undocumented `public` members are internal. + * **Core/Base Classes** (`esphome/core/`, `Component`, `Sensor`, etc.): All `public` members are public API. + * **Components with Global Accessors** (`global_api_server`, etc.): All `public` members are public API (except config setters). + +* **Public Python API:** + * All documented configuration options at [esphome.io](https://esphome.io) are public API. + * Python code in `esphome/core/` actively used by existing core components is considered stable API. + * Other Python code is internal unless explicitly documented for external component use. + +* **Breaking Changes Policy:** + * Aim for **6-month deprecation window** when possible + * Clean breaks allowed for: signature changes, deep refactorings, resource constraints + * Must document migration path in PR description (generates release notes) + * Blog post required for core/base class changes or significant architectural changes + * Full details: https://developers.esphome.io/contributing/code/#public-api-and-breaking-changes + +* **Breaking Change Checklist:** + - [ ] Clear justification (RAM/flash savings, architectural improvement) + - [ ] Explored non-breaking alternatives + - [ ] Added deprecation warnings if possible (use `ESPDEPRECATED` macro for C++) + - [ ] Documented migration path in PR description with before/after examples + - [ ] Updated all internal usage and esphome-docs + - [ ] Tested backward compatibility during deprecation period + +* **Deprecation Pattern (C++):** + ```cpp + // Remove before 2026.6.0 + ESPDEPRECATED("Use new_method() instead. Removed in 2026.6.0", "2025.12.0") + void old_method() { this->new_method(); } + ``` + +* **Deprecation Pattern (Python):** + ```python + # Remove before 2026.6.0 + if CONF_OLD_KEY in config: + _LOGGER.warning(f"'{CONF_OLD_KEY}' deprecated, use '{CONF_NEW_KEY}'. Removed in 2026.6.0") + config[CONF_NEW_KEY] = config.pop(CONF_OLD_KEY) # Auto-migrate + ``` From 85d2565f25057cba2ca362fa2c40db5122f212b1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 7 Nov 2025 01:18:43 -0600 Subject: [PATCH 424/526] [tests] Fix determine_jobs tests failing when target branch is beta (#11758) --- tests/script/test_determine_jobs.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 4894a5e28a..cadc7f9cd7 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -70,6 +70,13 @@ def mock_changed_files() -> Generator[Mock, None, None]: yield mock +@pytest.fixture +def mock_target_branch_dev() -> Generator[Mock, None, None]: + """Mock get_target_branch to return 'dev' for memory impact tests.""" + with patch.object(determine_jobs, "get_target_branch", return_value="dev") as mock: + yield mock + + @pytest.fixture(autouse=True) def clear_determine_jobs_caches() -> None: """Clear all cached functions before each test.""" @@ -688,6 +695,7 @@ def test_main_detects_components_with_variant_tests( # Tests for detect_memory_impact_config function +@pytest.mark.usefixtures("mock_target_branch_dev") def test_detect_memory_impact_config_with_common_platform(tmp_path: Path) -> None: """Test memory impact detection when components share a common platform.""" # Create test directory structure @@ -722,6 +730,7 @@ def test_detect_memory_impact_config_with_common_platform(tmp_path: Path) -> Non assert result["use_merged_config"] == "true" +@pytest.mark.usefixtures("mock_target_branch_dev") def test_detect_memory_impact_config_core_only_changes(tmp_path: Path) -> None: """Test memory impact detection with core C++ changes (no component changes).""" # Create test directory structure with fallback component @@ -779,6 +788,7 @@ def test_detect_memory_impact_config_core_python_only_changes(tmp_path: Path) -> assert result["should_run"] == "false" +@pytest.mark.usefixtures("mock_target_branch_dev") def test_detect_memory_impact_config_no_common_platform(tmp_path: Path) -> None: """Test memory impact detection when components have no common platform.""" # Create test directory structure @@ -855,6 +865,7 @@ def test_detect_memory_impact_config_no_components_with_tests(tmp_path: Path) -> assert result["should_run"] == "false" +@pytest.mark.usefixtures("mock_target_branch_dev") def test_detect_memory_impact_config_includes_base_bus_components( tmp_path: Path, ) -> None: @@ -897,6 +908,7 @@ def test_detect_memory_impact_config_includes_base_bus_components( assert result["platform"] == "esp32-idf" # Common platform +@pytest.mark.usefixtures("mock_target_branch_dev") def test_detect_memory_impact_config_with_variant_tests(tmp_path: Path) -> None: """Test memory impact detection for components with only variant test files. @@ -1125,6 +1137,7 @@ def test_main_core_files_changed_still_detects_components( assert len(output["changed_components"]) > 0 +@pytest.mark.usefixtures("mock_target_branch_dev") def test_detect_memory_impact_config_filters_incompatible_esp32_on_esp8266( tmp_path: Path, ) -> None: @@ -1178,6 +1191,7 @@ def test_detect_memory_impact_config_filters_incompatible_esp32_on_esp8266( assert result["use_merged_config"] == "true" +@pytest.mark.usefixtures("mock_target_branch_dev") def test_detect_memory_impact_config_filters_incompatible_esp8266_on_esp32( tmp_path: Path, ) -> None: From a5bf55b6acbf6c1715f20487c167a7ff2593ed2a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 7 Nov 2025 01:19:45 -0600 Subject: [PATCH 425/526] =?UTF-8?q?[ci]=20Fix=20component=20batching=20for?= =?UTF-8?q?=20beta/release=20branches=20(3-4=20=E2=86=92=2040=20per=20batc?= =?UTF-8?q?h)=20(#11759)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/determine-jobs.py | 18 +++- tests/script/test_determine_jobs.py | 148 ++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) diff --git a/script/determine-jobs.py b/script/determine-jobs.py index 39a7571fbe..5cc3f2570a 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -756,11 +756,27 @@ def main() -> None: component_test_batches: list[str] if changed_components_with_tests: tests_dir = Path(root_path) / ESPHOME_TESTS_COMPONENTS_PATH + + # For beta/release branches, group all components for faster CI + # (no isolation, all components are groupable) + target_branch = get_target_branch() + is_release_branch = target_branch and ( + target_branch.startswith("release") or target_branch.startswith("beta") + ) + + if is_release_branch: + # For beta/release: Don't isolate any components - group everything + # This allows components to be merged into single builds + batch_directly_changed = set() # Empty set - no isolation + else: + # Normal PR: only directly changed components are isolated + batch_directly_changed = directly_changed_with_tests + batches, _ = create_intelligent_batches( components=changed_components_with_tests, tests_dir=tests_dir, batch_size=COMPONENT_TEST_BATCH_SIZE, - directly_changed=directly_changed_with_tests, + directly_changed=batch_directly_changed, ) # Convert batches to space-separated strings for CI matrix component_test_batches = [" ".join(batch) for batch in batches] diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index cadc7f9cd7..291a23967b 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -19,6 +19,8 @@ sys.path.insert(0, script_dir) # Import helpers module for patching import helpers # noqa: E402 +import script.helpers # noqa: E402 + spec = importlib.util.spec_from_file_location( "determine_jobs", os.path.join(script_dir, "determine-jobs.py") ) @@ -132,6 +134,16 @@ def test_main_all_tests_should_run( ["wifi", "api"] if not deps else ["wifi", "api", "sensor"] ), ), + patch.object( + determine_jobs, + "detect_memory_impact_config", + return_value={"should_run": "false"}, + ), + patch.object( + determine_jobs, + "create_intelligent_batches", + return_value=([["wifi", "api", "sensor"]], {}), + ), ): determine_jobs.main() @@ -203,6 +215,16 @@ def test_main_no_tests_should_run( patch.object( determine_jobs, "get_components_with_dependencies", return_value=[] ), + patch.object( + determine_jobs, + "detect_memory_impact_config", + return_value={"should_run": "false"}, + ), + patch.object( + determine_jobs, + "create_intelligent_batches", + return_value=([], {}), + ), ): determine_jobs.main() @@ -266,6 +288,16 @@ def test_main_with_branch_argument( patch.object( determine_jobs, "get_components_with_dependencies", return_value=["mqtt"] ), + patch.object( + determine_jobs, + "detect_memory_impact_config", + return_value={"should_run": "false"}, + ), + patch.object( + determine_jobs, + "create_intelligent_batches", + return_value=([["mqtt"]], {}), + ), ): determine_jobs.main() @@ -571,6 +603,11 @@ def test_main_filters_components_without_tests( ), ), patch.object(determine_jobs, "changed_files", return_value=[]), + patch.object( + determine_jobs, + "detect_memory_impact_config", + return_value={"should_run": "false"}, + ), ): # Clear the cache since we're mocking root_path determine_jobs.main() @@ -670,6 +707,11 @@ def test_main_detects_components_with_variant_tests( ), ), patch.object(determine_jobs, "changed_files", return_value=[]), + patch.object( + determine_jobs, + "detect_memory_impact_config", + return_value={"should_run": "false"}, + ), ): # Clear the cache since we're mocking root_path determine_jobs.main() @@ -1124,6 +1166,16 @@ def test_main_core_files_changed_still_detects_components( else ["select", "api", "bluetooth_proxy", "logger"] ), ), + patch.object( + determine_jobs, + "detect_memory_impact_config", + return_value={"should_run": "false"}, + ), + patch.object( + determine_jobs, + "create_intelligent_batches", + return_value=([["select", "api", "bluetooth_proxy", "logger"]], {}), + ), ): determine_jobs.main() @@ -1367,3 +1419,99 @@ def test_detect_memory_impact_config_runs_at_component_limit(tmp_path: Path) -> # Memory impact should run at exactly 40 components (at limit but not over) assert result["should_run"] == "true" assert len(result["components"]) == 40 + + +def test_component_batching_beta_branch_40_per_batch( + tmp_path: Path, + mock_should_run_integration_tests: Mock, + mock_should_run_clang_tidy: Mock, + mock_should_run_clang_format: Mock, + mock_should_run_python_linters: Mock, + mock_changed_files: Mock, + mock_determine_cpp_unit_tests: Mock, + capsys: pytest.CaptureFixture[str], +) -> None: + """Test that beta/release branches create batches with 40 actual components each. + + For beta/release branches, all components should be groupable (not isolated), + and each batch should contain 40 actual components with weight 1 each. + This matches the original behavior before consolidation. + """ + # Create 120 test components with test files + component_names = [f"comp_{i:03d}" for i in range(120)] + tests_dir = tmp_path / "tests" / "components" + + for comp in component_names: + comp_dir = tests_dir / comp + comp_dir.mkdir(parents=True) + (comp_dir / "test.esp32-idf.yaml").write_text(f"# Test for {comp}") + + # Setup mocks + mock_should_run_integration_tests.return_value = False + mock_should_run_clang_tidy.return_value = False + mock_should_run_clang_format.return_value = False + mock_should_run_python_linters.return_value = False + mock_determine_cpp_unit_tests.return_value = (False, []) + + # Mock changed_files to return all component files + changed_files = [ + f"esphome/components/{comp}/{comp}.cpp" for comp in component_names + ] + mock_changed_files.return_value = changed_files + + # Run main function with beta branch + # Don't mock create_intelligent_batches - that's what we're testing! + with ( + patch("sys.argv", ["determine-jobs.py", "--branch", "beta"]), + patch.object(determine_jobs, "root_path", str(tmp_path)), + patch.object(helpers, "root_path", str(tmp_path)), + patch.object(script.helpers, "root_path", str(tmp_path)), + patch.object(determine_jobs, "get_target_branch", return_value="beta"), + patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=False), + patch.object( + determine_jobs, + "get_changed_components", + return_value=component_names, + ), + patch.object( + determine_jobs, + "filter_component_and_test_files", + side_effect=lambda f: f.startswith("esphome/components/"), + ), + patch.object( + determine_jobs, + "get_components_with_dependencies", + side_effect=lambda files, deps: component_names, + ), + patch.object( + determine_jobs, + "detect_memory_impact_config", + return_value={"should_run": "false"}, + ), + ): + determine_jobs.main() + + # Check output + captured = capsys.readouterr() + output = json.loads(captured.out) + + # Verify batches are present and properly sized + assert "component_test_batches" in output + batches = output["component_test_batches"] + + # Should have 3 batches (120 components / 40 per batch = 3) + assert len(batches) == 3, f"Expected 3 batches, got {len(batches)}" + + # Each batch should have approximately 40 components (all weight=1, groupable) + for i, batch_str in enumerate(batches): + batch_components = batch_str.split() + assert len(batch_components) == 40, ( + f"Batch {i} should have 40 components, got {len(batch_components)}" + ) + + # Verify all 120 components are in batches + all_components = [] + for batch_str in batches: + all_components.extend(batch_str.split()) + assert len(all_components) == 120 + assert set(all_components) == set(component_names) From 79d1a558af778df183764b656a838ec650d9fb47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 20:12:15 +0000 Subject: [PATCH 426/526] Bump ruff from 0.14.3 to 0.14.4 (#11768) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston --- .pre-commit-config.yaml | 2 +- requirements_test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5356bffd96..dab660b03f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ ci: repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.14.3 + rev: v0.14.4 hooks: # Run the linter. - id: ruff diff --git a/requirements_test.txt b/requirements_test.txt index 11367172b1..81cb711eec 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ pylint==4.0.2 flake8==7.3.0 # also change in .pre-commit-config.yaml when updating -ruff==0.14.3 # also change in .pre-commit-config.yaml when updating +ruff==0.14.4 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating pre-commit From c77bb3b269bf7fba517fb359535c59cccef49152 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 7 Nov 2025 15:46:16 -0600 Subject: [PATCH 427/526] [event] Store event types in flash memory (#11767) --- esphome/components/api/api.proto | 2 +- esphome/components/api/api_connection.cpp | 3 +- esphome/components/api/api_pb2.cpp | 10 +++--- esphome/components/api/api_pb2.h | 2 +- esphome/components/api/api_pb2_dump.cpp | 2 +- esphome/components/event/event.cpp | 30 +++++++++++++---- esphome/components/event/event.h | 35 +++++++++++++++++--- esphome/components/mqtt/mqtt_event.cpp | 4 +-- esphome/components/web_server/web_server.cpp | 5 +-- 9 files changed, 67 insertions(+), 26 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 7a50fa6b17..e115e4630d 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -2147,7 +2147,7 @@ message ListEntitiesEventResponse { EntityCategory entity_category = 7; string device_class = 8; - repeated string event_types = 9; + repeated string event_types = 9 [(container_pointer_no_template) = "FixedVector"]; uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"]; } message EventResponse { diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 5ab8a6eb05..8c293b41a2 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1310,8 +1310,7 @@ uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *c auto *event = static_cast(entity); ListEntitiesEventResponse msg; msg.set_device_class(event->get_device_class_ref()); - for (const auto &event_type : event->get_event_types()) - msg.event_types.push_back(event_type); + msg.event_types = &event->get_event_types(); return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index dfa1a1320f..0a073fb662 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -2877,8 +2877,8 @@ void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(6, this->disabled_by_default); buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_string(8, this->device_class_ref_); - for (auto &it : this->event_types) { - buffer.encode_string(9, it, true); + for (const char *it : *this->event_types) { + buffer.encode_string(9, it, strlen(it), true); } #ifdef USE_DEVICES buffer.encode_uint32(10, this->device_id); @@ -2894,9 +2894,9 @@ void ListEntitiesEventResponse::calculate_size(ProtoSize &size) const { size.add_bool(1, this->disabled_by_default); size.add_uint32(1, static_cast(this->entity_category)); size.add_length(1, this->device_class_ref_.size()); - if (!this->event_types.empty()) { - for (const auto &it : this->event_types) { - size.add_length_force(1, it.size()); + if (!this->event_types->empty()) { + for (const char *it : *this->event_types) { + size.add_length_force(1, strlen(it)); } } #ifdef USE_DEVICES diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 716f1a6e9b..358049026e 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -2788,7 +2788,7 @@ class ListEntitiesEventResponse final : public InfoResponseProtoMessage { #endif StringRef device_class_ref_{}; void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; } - std::vector event_types{}; + const FixedVector *event_types{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(ProtoSize &size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP diff --git a/esphome/components/api/api_pb2_dump.cpp b/esphome/components/api/api_pb2_dump.cpp index d94ceaaa9c..d9662483bf 100644 --- a/esphome/components/api/api_pb2_dump.cpp +++ b/esphome/components/api/api_pb2_dump.cpp @@ -2053,7 +2053,7 @@ void ListEntitiesEventResponse::dump_to(std::string &out) const { dump_field(out, "disabled_by_default", this->disabled_by_default); dump_field(out, "entity_category", static_cast(this->entity_category)); dump_field(out, "device_class", this->device_class_ref_); - for (const auto &it : this->event_types) { + for (const auto &it : *this->event_types) { dump_field(out, "event_types", it, 4); } #ifdef USE_DEVICES diff --git a/esphome/components/event/event.cpp b/esphome/components/event/event.cpp index 20549ad0a5..a14afbd7f5 100644 --- a/esphome/components/event/event.cpp +++ b/esphome/components/event/event.cpp @@ -8,11 +8,11 @@ namespace event { static const char *const TAG = "event"; void Event::trigger(const std::string &event_type) { - // Linear search - faster than std::set for small datasets (1-5 items typical) - const std::string *found = nullptr; - for (const auto &type : this->types_) { - if (type == event_type) { - found = &type; + // Linear search with strcmp - faster than std::set for small datasets (1-5 items typical) + const char *found = nullptr; + for (const char *type : this->types_) { + if (strcmp(type, event_type.c_str()) == 0) { + found = type; break; } } @@ -20,11 +20,27 @@ void Event::trigger(const std::string &event_type) { ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str()); return; } - last_event_type = found; - ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), last_event_type->c_str()); + this->last_event_type_ = found; + ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), this->last_event_type_); this->event_callback_.call(event_type); } +void Event::set_event_types(const FixedVector &event_types) { + this->types_.init(event_types.size()); + for (const char *type : event_types) { + this->types_.push_back(type); + } + this->last_event_type_ = nullptr; // Reset when types change +} + +void Event::set_event_types(const std::vector &event_types) { + this->types_.init(event_types.size()); + for (const char *type : event_types) { + this->types_.push_back(type); + } + this->last_event_type_ = nullptr; // Reset when types change +} + void Event::add_on_event_callback(std::function &&callback) { this->event_callback_.add(std::move(callback)); } diff --git a/esphome/components/event/event.h b/esphome/components/event/event.h index 2f6267a200..e4b2e0b845 100644 --- a/esphome/components/event/event.h +++ b/esphome/components/event/event.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include "esphome/core/component.h" #include "esphome/core/entity_base.h" @@ -22,16 +24,39 @@ namespace event { class Event : public EntityBase, public EntityBase_DeviceClass { public: - const std::string *last_event_type; - void trigger(const std::string &event_type); - void set_event_types(const std::initializer_list &event_types) { this->types_ = event_types; } - const FixedVector &get_event_types() const { return this->types_; } + + /// Set the event types supported by this event (from initializer list). + void set_event_types(std::initializer_list event_types) { + this->types_ = event_types; + this->last_event_type_ = nullptr; // Reset when types change + } + /// Set the event types supported by this event (from FixedVector). + void set_event_types(const FixedVector &event_types); + /// Set the event types supported by this event (from vector). + void set_event_types(const std::vector &event_types); + + // Deleted overloads to catch incorrect std::string usage at compile time with clear error messages + void set_event_types(std::initializer_list event_types) = delete; + void set_event_types(const FixedVector &event_types) = delete; + void set_event_types(const std::vector &event_types) = delete; + + /// Return the event types supported by this event. + const FixedVector &get_event_types() const { return this->types_; } + + /// Return the last triggered event type (pointer to string in types_), or nullptr if no event triggered yet. + const char *get_last_event_type() const { return this->last_event_type_; } + void add_on_event_callback(std::function &&callback); protected: CallbackManager event_callback_; - FixedVector types_; + FixedVector types_; + + private: + /// Last triggered event type - must point to entry in types_ to ensure valid lifetime. + /// Set by trigger() after validation, reset to nullptr when types_ changes. + const char *last_event_type_{nullptr}; }; } // namespace event diff --git a/esphome/components/mqtt/mqtt_event.cpp b/esphome/components/mqtt/mqtt_event.cpp index e206335446..fd095ea041 100644 --- a/esphome/components/mqtt/mqtt_event.cpp +++ b/esphome/components/mqtt/mqtt_event.cpp @@ -38,8 +38,8 @@ void MQTTEventComponent::setup() { void MQTTEventComponent::dump_config() { ESP_LOGCONFIG(TAG, "MQTT Event '%s': ", this->event_->get_name().c_str()); ESP_LOGCONFIG(TAG, "Event Types: "); - for (const auto &event_type : this->event_->get_event_types()) { - ESP_LOGCONFIG(TAG, "- %s", event_type.c_str()); + for (const char *event_type : this->event_->get_event_types()) { + ESP_LOGCONFIG(TAG, "- %s", event_type); } LOG_MQTT_COMPONENT(true, true); } diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index f1d1a75875..91ca076474 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1628,7 +1628,8 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa } static std::string get_event_type(event::Event *event) { - return (event && event->last_event_type) ? *event->last_event_type : ""; + const char *last_type = event ? event->get_last_event_type() : nullptr; + return last_type ? last_type : ""; } std::string WebServer::event_state_json_generator(WebServer *web_server, void *source) { @@ -1649,7 +1650,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty } if (start_config == DETAIL_ALL) { JsonArray event_types = root["event_types"].to(); - for (auto const &event_type : obj->get_event_types()) { + for (const char *event_type : obj->get_event_types()) { event_types.add(event_type); } root["device_class"] = obj->get_device_class_ref(); From f55c87218075a63bdf39629a38aa34cab3443e40 Mon Sep 17 00:00:00 2001 From: optimusprimespace <62800678+optimusprimespace@users.noreply.github.com> Date: Sat, 8 Nov 2025 22:56:51 +0200 Subject: [PATCH 428/526] Updated AQI calculation for HM3301 to the new standard (#9442) Co-authored-by: J. Nick Koston --- esphome/components/hm3301/aqi_calculator.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/esphome/components/hm3301/aqi_calculator.h b/esphome/components/hm3301/aqi_calculator.h index c1b47826a2..aa01060d2c 100644 --- a/esphome/components/hm3301/aqi_calculator.h +++ b/esphome/components/hm3301/aqi_calculator.h @@ -1,7 +1,7 @@ #pragma once - +#include #include "abstract_aqi_calculator.h" -// https://www.airnow.gov/sites/default/files/2020-05/aqi-technical-assistance-document-sept2018.pdf +// https://document.airnow.gov/technical-assistance-document-for-the-reporting-of-daily-air-quailty.pdf namespace esphome { namespace hm3301 { @@ -16,16 +16,15 @@ class AQICalculator : public AbstractAQICalculator { } protected: - static const int AMOUNT_OF_LEVELS = 7; + static const int AMOUNT_OF_LEVELS = 6; - int index_grid_[AMOUNT_OF_LEVELS][2] = {{0, 50}, {51, 100}, {101, 150}, {151, 200}, - {201, 300}, {301, 400}, {401, 500}}; + int index_grid_[AMOUNT_OF_LEVELS][2] = {{0, 50}, {51, 100}, {101, 150}, {151, 200}, {201, 300}, {301, 500}}; - int pm2_5_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 12}, {13, 35}, {36, 55}, {56, 150}, - {151, 250}, {251, 350}, {351, 500}}; + int pm2_5_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 9}, {10, 35}, {36, 55}, + {56, 125}, {126, 225}, {226, INT_MAX}}; - int pm10_0_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 54}, {55, 154}, {155, 254}, {255, 354}, - {355, 424}, {425, 504}, {505, 604}}; + int pm10_0_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 54}, {55, 154}, {155, 254}, + {255, 354}, {355, 424}, {425, INT_MAX}}; int calculate_index_(uint16_t value, int array[AMOUNT_OF_LEVELS][2]) { int grid_index = get_grid_index_(value, array); From b61027607f53f9344b924a5827f34e1513e44257 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Nov 2025 15:22:40 -0600 Subject: [PATCH 429/526] Bump aioesphomeapi from 42.6.0 to 42.7.0 (#11771) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 33fa2b64eb..40802422f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==5.1.0 click==8.1.7 esphome-dashboard==20251013.0 -aioesphomeapi==42.6.0 +aioesphomeapi==42.7.0 zeroconf==0.148.0 puremagic==1.30 ruamel.yaml==0.18.16 # dashboard_import From a290b88cd6fa86566068370b3ee7e0723ecdd967 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:09:03 -0600 Subject: [PATCH 430/526] Expand uart.write tests (#11785) --- tests/components/uart/test.esp32-idf.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/components/uart/test.esp32-idf.yaml b/tests/components/uart/test.esp32-idf.yaml index 9744a48409..6ffd0d7282 100644 --- a/tests/components/uart/test.esp32-idf.yaml +++ b/tests/components/uart/test.esp32-idf.yaml @@ -3,6 +3,8 @@ esphome: then: - uart.write: 'Hello World' - uart.write: [0x00, 0x20, 0x42] + - uart.write: !lambda |- + return {0xAA, 0xBB, 0xCC}; uart: - id: uart_uart @@ -46,6 +48,15 @@ switch: turn_on: "TURN_ON" turn_off: "TURN_OFF" +number: + - platform: template + name: "Test Number" + id: test_number + optimistic: true + min_value: 0 + max_value: 100 + step: 1 + button: # Test uart button with array data - platform: uart @@ -57,3 +68,10 @@ button: name: "UART Button String" uart_id: uart_uart data: "BUTTON_PRESS" + # Test uart button with lambda (function pointer) + - platform: template + name: "UART Lambda Test" + on_press: + - uart.write: !lambda |- + std::string cmd = "VALUE=" + str_sprintf("%.0f", id(test_number).state) + "\r\n"; + return std::vector(cmd.begin(), cmd.end()); From b49619d9bf17eef24f6029af9d2dfa9c5be722a7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:09:25 -0600 Subject: [PATCH 431/526] Add ble_client lambda compile tests (#11787) --- tests/components/ble_client/common.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/components/ble_client/common.yaml b/tests/components/ble_client/common.yaml index aa4b639463..4ea1dd60f3 100644 --- a/tests/components/ble_client/common.yaml +++ b/tests/components/ble_client/common.yaml @@ -52,3 +52,25 @@ sensor: name: "BLE Sensor without Lambda" service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678" characteristic_uuid: "abcd1237-abcd-1234-abcd-abcd12345678" + +number: + - platform: template + name: "Test Number" + id: test_number + optimistic: true + min_value: 0 + max_value: 255 + step: 1 + +button: + # Test ble_write with lambda that references a component (function pointer) + - platform: template + name: "BLE Write Lambda Test" + on_press: + - ble_client.ble_write: + id: test_blec + service_uuid: "abcd1234-abcd-1234-abcd-abcd12345678" + characteristic_uuid: "abcd1235-abcd-1234-abcd-abcd12345678" + value: !lambda |- + uint8_t val = (uint8_t)id(test_number).state; + return std::vector{0xAA, val, 0xBB}; From 783dbd1e6b6f462317ed5881df871c87903ea442 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:09:46 -0600 Subject: [PATCH 432/526] Add additional compile time tests for canbus (#11789) --- tests/components/canbus/common.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/components/canbus/common.yaml b/tests/components/canbus/common.yaml index fd146cc3a3..8bddeb7409 100644 --- a/tests/components/canbus/common.yaml +++ b/tests/components/canbus/common.yaml @@ -37,6 +37,15 @@ canbus: break; } +number: + - platform: template + name: "Test Number" + id: test_number + optimistic: true + min_value: 0 + max_value: 255 + step: 1 + button: - platform: template name: Canbus Actions @@ -44,3 +53,7 @@ button: - canbus.send: "abc" - canbus.send: [0, 1, 2] - canbus.send: !lambda return {0, 1, 2}; + # Test canbus.send with lambda that references a component (function pointer) + - canbus.send: !lambda |- + uint8_t val = (uint8_t)id(test_number).state; + return std::vector{0xAA, val, 0xBB}; From 4c078dea2c035fec594c1abfed51c0d03e51de2d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:10:31 -0600 Subject: [PATCH 433/526] Add additional sx126x lambda tests (#11791) --- tests/components/sx126x/common.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/components/sx126x/common.yaml b/tests/components/sx126x/common.yaml index 3f540a4bae..659550cc01 100644 --- a/tests/components/sx126x/common.yaml +++ b/tests/components/sx126x/common.yaml @@ -26,6 +26,15 @@ sx126x: - lambda: |- ESP_LOGD("lambda", "packet %.2f %.2f %s", rssi, snr, format_hex(x).c_str()); +number: + - platform: template + name: "SX126x Number" + id: my_number + optimistic: true + min_value: 0 + max_value: 100 + step: 1 + button: - platform: template name: "SX126x Button" @@ -37,3 +46,5 @@ button: - sx126x.set_mode_rx - sx126x.send_packet: data: [0xC5, 0x51, 0x78, 0x82, 0xB7, 0xF9, 0x9C, 0x5C] + - sx126x.send_packet: !lambda |- + return {0x01, 0x02, (uint8_t)id(my_number).state}; From e468ca48812ef2ab649ecc4f8649da6975dabc4d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:11:31 -0600 Subject: [PATCH 434/526] Add additional sx127x lambda tests (#11793) --- tests/components/sx127x/common.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/components/sx127x/common.yaml b/tests/components/sx127x/common.yaml index 540381fc08..6e48952fcc 100644 --- a/tests/components/sx127x/common.yaml +++ b/tests/components/sx127x/common.yaml @@ -26,6 +26,15 @@ sx127x: - sx127x.send_packet: data: [0xC5, 0x51, 0x78, 0x82, 0xB7, 0xF9, 0x9C, 0x5C] +number: + - platform: template + name: "SX127x Number" + id: my_number + optimistic: true + min_value: 0 + max_value: 100 + step: 1 + button: - platform: template name: "SX127x Button" @@ -38,3 +47,5 @@ button: - sx127x.set_mode_rx - sx127x.send_packet: data: [0xC5, 0x51, 0x78, 0x82, 0xB7, 0xF9, 0x9C, 0x5C] + - sx127x.send_packet: !lambda |- + return {0x01, 0x02, (uint8_t)id(my_number).state}; From 55853552637893cbb4ca4e8be25f2314b6614b21 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:15:50 -0600 Subject: [PATCH 435/526] Add additional speaker lambda tests (#11797) --- tests/components/speaker/common.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/components/speaker/common.yaml b/tests/components/speaker/common.yaml index c04674ee29..fa54fa7e39 100644 --- a/tests/components/speaker/common.yaml +++ b/tests/components/speaker/common.yaml @@ -1,3 +1,12 @@ +number: + - platform: template + name: "Speaker Number" + id: my_number + optimistic: true + min_value: 0 + max_value: 100 + step: 1 + esphome: on_boot: then: @@ -14,6 +23,15 @@ esphome: - speaker.finish: - speaker.stop: +button: + - platform: template + name: "Speaker Button" + on_press: + then: + - speaker.play: [0x10, 0x20, 0x30, 0x40] + - speaker.play: !lambda |- + return {0x01, 0x02, (uint8_t)id(my_number).state}; + i2s_audio: i2s_lrclk_pin: ${i2s_bclk_pin} i2s_bclk_pin: ${i2s_lrclk_pin} From eb0558ca3fb71c10516c96f366d2880f1e325717 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:16:09 -0600 Subject: [PATCH 436/526] Add additional udp lambda tests (#11795) --- tests/components/udp/common.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/components/udp/common.yaml b/tests/components/udp/common.yaml index 96224d0d1f..98546d49ef 100644 --- a/tests/components/udp/common.yaml +++ b/tests/components/udp/common.yaml @@ -17,3 +17,22 @@ udp: id: my_udp data: !lambda |- return std::vector{1,3,4,5,6}; + +number: + - platform: template + name: "UDP Number" + id: my_number + optimistic: true + min_value: 0 + max_value: 100 + step: 1 + +button: + - platform: template + name: "UDP Button" + on_press: + then: + - udp.write: + data: [0x01, 0x02, 0x03] + - udp.write: !lambda |- + return {0x10, 0x20, (uint8_t)id(my_number).state}; From f7179d42557b926f1f30adc1c4a6acd433491715 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:16:53 -0600 Subject: [PATCH 437/526] Add additonal abbwelcome remote_base tests (#11799) --- .../remote_transmitter/common-buttons.yaml | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml index e9593cc97c..101d60a893 100644 --- a/tests/components/remote_transmitter/common-buttons.yaml +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -1,3 +1,11 @@ +number: + - platform: template + id: test_number + optimistic: true + min_value: 0 + max_value: 255 + step: 1 + button: - platform: template name: Beo4 audio mute @@ -217,6 +225,23 @@ button: command: 0xEC rc_code_1: 0x0D rc_code_2: 0x0D + - platform: template + name: ABBWelcome static + on_press: + remote_transmitter.transmit_abbwelcome: + source_address: 0x1234 + destination_address: 0x5678 + message_type: 0x01 + data: [0x10, 0x20, 0x30] + - platform: template + name: ABBWelcome lambda + on_press: + remote_transmitter.transmit_abbwelcome: + source_address: 0x1234 + destination_address: 0x5678 + message_type: 0x01 + data: !lambda |- + return {(uint8_t)id(test_number).state, 0x20, 0x30}; - platform: template name: Digital Write on_press: From 5f9c7a70ff8721d0f80c739827810e166b870cb6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:17:14 -0600 Subject: [PATCH 438/526] Add additional tests for remote_transmitter raw (#11801) --- tests/components/remote_transmitter/common-buttons.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml index 101d60a893..d48d36bd54 100644 --- a/tests/components/remote_transmitter/common-buttons.yaml +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -136,10 +136,16 @@ button: address: 0x00 command: 0x0B - platform: template - name: RC5 Raw + name: RC5 Raw static on_press: remote_transmitter.transmit_raw: code: [1000, -1000] + - platform: template + name: RC5 Raw lambda + on_press: + remote_transmitter.transmit_raw: + code: !lambda |- + return {(int32_t)id(test_number).state * 100, -1000}; - platform: template name: AEHA id: eaha_hitachi_climate_power_on From 870b2c4f848b6bc0ad123cb907a7012300db02e9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:21:25 -0600 Subject: [PATCH 439/526] [ble_client] Optimize ble_write memory usage - store static data in flash (#11786) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/ble_client/__init__.py | 8 +++- esphome/components/ble_client/automation.h | 43 +++++++++------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 768a345213..37db181584 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -15,6 +15,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_VALUE, ) +from esphome.core import ID AUTO_LOAD = ["esp32_ble_client"] CODEOWNERS = ["@buxtronix", "@clydebarrow"] @@ -198,7 +199,12 @@ async def ble_write_to_code(config, action_id, template_arg, args): templ = await cg.templatable(value, args, cg.std_vector.template(cg.uint8)) cg.add(var.set_value_template(templ)) else: - cg.add(var.set_value_simple(value)) + # Generate static array in flash to avoid RAM copy + if isinstance(value, bytes): + value = list(value) + arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8) + arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*value)) + cg.add(var.set_value_simple(arr, len(value))) if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): cg.add( diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index ce534501f3..9c5646b3d1 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -96,11 +96,8 @@ template class BLEClientWriteAction : public Action, publ BLEClientWriteAction(BLEClient *ble_client) { ble_client->register_ble_node(this); ble_client_ = ble_client; - this->construct_simple_value_(); } - ~BLEClientWriteAction() { this->destroy_simple_value_(); } - void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); } void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } @@ -110,17 +107,14 @@ template class BLEClientWriteAction : public Action, publ void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } void set_value_template(std::vector (*func)(Ts...)) { - this->destroy_simple_value_(); - this->value_.template_func = func; - this->has_simple_value_ = false; + this->value_.func = func; + this->len_ = -1; // Sentinel value indicates template mode } - void set_value_simple(const std::vector &value) { - if (!this->has_simple_value_) { - this->construct_simple_value_(); - } - this->value_.simple = value; - this->has_simple_value_ = true; + // Store pointer to static data in flash (no RAM copy) + void set_value_simple(const uint8_t *data, size_t len) { + this->value_.data = data; + this->len_ = len; // Length >= 0 indicates static mode } void play(const Ts &...x) override {} @@ -128,7 +122,14 @@ template class BLEClientWriteAction : public Action, publ void play_complex(const Ts &...x) override { this->num_running_++; this->var_ = std::make_tuple(x...); - auto value = this->has_simple_value_ ? this->value_.simple : this->value_.template_func(x...); + std::vector value; + if (this->len_ >= 0) { + // Static mode: copy from flash to vector + value.assign(this->value_.data, this->value_.data + this->len_); + } else { + // Template mode: call function + value = this->value_.func(x...); + } // on write failure, continue the automation chain rather than stopping so that e.g. disconnect can work. if (!write(value)) this->play_next_(x...); @@ -201,21 +202,11 @@ template class BLEClientWriteAction : public Action, publ } private: - void construct_simple_value_() { new (&this->value_.simple) std::vector(); } - - void destroy_simple_value_() { - if (this->has_simple_value_) { - this->value_.simple.~vector(); - } - } - BLEClient *ble_client_; - bool has_simple_value_ = true; + ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length union Value { - std::vector simple; - std::vector (*template_func)(Ts...); - Value() {} // trivial constructor - ~Value() {} // trivial destructor - we manage lifetime via discriminator + std::vector (*func)(Ts...); // Function pointer (stateless lambdas) + const uint8_t *data; // Pointer to static data in flash } value_; espbt::ESPBTUUID service_uuid_; espbt::ESPBTUUID char_uuid_; From 3bcbfe8d9756ef97a47dff0ed4e641919022b0fb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:22:15 -0600 Subject: [PATCH 440/526] [canbus] Optimize canbus.send memory usage - store static data in flash (#11788) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/canbus/__init__.py | 7 ++++-- esphome/components/canbus/canbus.h | 34 +++++++++++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/esphome/components/canbus/__init__.py b/esphome/components/canbus/__init__.py index e1de1eb2f2..7b51c2c45c 100644 --- a/esphome/components/canbus/__init__.py +++ b/esphome/components/canbus/__init__.py @@ -4,7 +4,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_DATA, CONF_ID, CONF_TRIGGER_ID -from esphome.core import CORE +from esphome.core import CORE, ID CODEOWNERS = ["@mvturnho", "@danielschramm"] IS_PLATFORM_COMPONENT = True @@ -176,5 +176,8 @@ async def canbus_action_to_code(config, action_id, template_arg, args): else: if isinstance(data, bytes): data = [int(x) for x in data] - cg.add(var.set_data_static(data)) + # Generate static array in flash to avoid RAM copy + arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8) + arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data)) + cg.add(var.set_data_static(arr, len(data))) return var diff --git a/esphome/components/canbus/canbus.h b/esphome/components/canbus/canbus.h index 029eb278c0..f7b84111bd 100644 --- a/esphome/components/canbus/canbus.h +++ b/esphome/components/canbus/canbus.h @@ -112,13 +112,16 @@ class Canbus : public Component { template class CanbusSendAction : public Action, public Parented { public: - void set_data_template(const std::function(Ts...)> func) { - this->data_func_ = func; - this->static_ = false; + void set_data_template(std::vector (*func)(Ts...)) { + // Stateless lambdas (generated by ESPHome) implicitly convert to function pointers + this->data_.func = func; + this->len_ = -1; // Sentinel value indicates template mode } - void set_data_static(const std::vector &data) { - this->data_static_ = data; - this->static_ = true; + + // Store pointer to static data in flash (no RAM copy) + void set_data_static(const uint8_t *data, size_t len) { + this->data_.data = data; + this->len_ = len; // Length >= 0 indicates static mode } void set_can_id(uint32_t can_id) { this->can_id_ = can_id; } @@ -133,21 +136,26 @@ template class CanbusSendAction : public Action, public P auto can_id = this->can_id_.has_value() ? *this->can_id_ : this->parent_->can_id_; auto use_extended_id = this->use_extended_id_.has_value() ? *this->use_extended_id_ : this->parent_->use_extended_id_; - if (this->static_) { - this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, this->data_static_); + std::vector data; + if (this->len_ >= 0) { + // Static mode: copy from flash to vector + data.assign(this->data_.data, this->data_.data + this->len_); } else { - auto val = this->data_func_(x...); - this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, val); + // Template mode: call function + data = this->data_.func(x...); } + this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, data); } protected: optional can_id_{}; optional use_extended_id_{}; bool remote_transmission_request_{false}; - bool static_{false}; - std::function(Ts...)> data_func_{}; - std::vector data_static_{}; + ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length + union Data { + std::vector (*func)(Ts...); // Function pointer (stateless lambdas) + const uint8_t *data; // Pointer to static data in flash + } data_; }; class CanbusTrigger : public Trigger, uint32_t, bool>, public Component { From 26a3ec41d6037f44b4479ee35983aab9f789688f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:23:33 -0600 Subject: [PATCH 441/526] [sx126x] Optimize send_packet action memory usage - store static data in flash (#11790) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/sx126x/__init__.py | 7 ++++-- esphome/components/sx126x/automation.h | 30 +++++++++++++++----------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/esphome/components/sx126x/__init__.py b/esphome/components/sx126x/__init__.py index 370cd102d4..f8f3b9d104 100644 --- a/esphome/components/sx126x/__init__.py +++ b/esphome/components/sx126x/__init__.py @@ -3,7 +3,7 @@ import esphome.codegen as cg from esphome.components import spi import esphome.config_validation as cv from esphome.const import CONF_BUSY_PIN, CONF_DATA, CONF_FREQUENCY, CONF_ID -from esphome.core import TimePeriod +from esphome.core import ID, TimePeriod MULTI_CONF = True CODEOWNERS = ["@swoboda1337"] @@ -329,5 +329,8 @@ async def send_packet_action_to_code(config, action_id, template_arg, args): templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8)) cg.add(var.set_data_template(templ)) else: - cg.add(var.set_data_static(data)) + # Generate static array in flash to avoid RAM copy + arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8) + arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data)) + cg.add(var.set_data_static(arr, len(data))) return var diff --git a/esphome/components/sx126x/automation.h b/esphome/components/sx126x/automation.h index 6b2371e253..2282c583cb 100644 --- a/esphome/components/sx126x/automation.h +++ b/esphome/components/sx126x/automation.h @@ -14,28 +14,34 @@ template class RunImageCalAction : public Action, public template class SendPacketAction : public Action, public Parented { public: - void set_data_template(std::function(Ts...)> func) { - this->data_func_ = func; - this->static_ = false; + void set_data_template(std::vector (*func)(Ts...)) { + this->data_.func = func; + this->len_ = -1; // Sentinel value indicates template mode } - void set_data_static(const std::vector &data) { - this->data_static_ = data; - this->static_ = true; + void set_data_static(const uint8_t *data, size_t len) { + this->data_.data = data; + this->len_ = len; // Length >= 0 indicates static mode } void play(const Ts &...x) override { - if (this->static_) { - this->parent_->transmit_packet(this->data_static_); + std::vector data; + if (this->len_ >= 0) { + // Static mode: copy from flash to vector + data.assign(this->data_.data, this->data_.data + this->len_); } else { - this->parent_->transmit_packet(this->data_func_(x...)); + // Template mode: call function + data = this->data_.func(x...); } + this->parent_->transmit_packet(data); } protected: - bool static_{false}; - std::function(Ts...)> data_func_{}; - std::vector data_static_{}; + ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length + union Data { + std::vector (*func)(Ts...); // Function pointer (stateless lambdas) + const uint8_t *data; // Pointer to static data in flash + } data_; }; template class SetModeTxAction : public Action, public Parented { From 77ab096b59311c49679899b3f0c3b56e02e50dd6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:25:16 -0600 Subject: [PATCH 442/526] [remote_base] Optimize raw transmit action memory usage - use function pointers (#11800) --- esphome/components/remote_base/raw_protocol.h | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/esphome/components/remote_base/raw_protocol.h b/esphome/components/remote_base/raw_protocol.h index 9b671e611f..941b6aab42 100644 --- a/esphome/components/remote_base/raw_protocol.h +++ b/esphome/components/remote_base/raw_protocol.h @@ -42,17 +42,20 @@ class RawTrigger : public Trigger, public Component, public RemoteRe template class RawAction : public RemoteTransmitterActionBase { public: - void set_code_template(std::function func) { this->code_func_ = func; } + void set_code_template(RawTimings (*func)(Ts...)) { + this->code_.func = func; + this->len_ = -1; // Sentinel value indicates template mode + } void set_code_static(const int32_t *code, size_t len) { - this->code_static_ = code; - this->code_static_len_ = len; + this->code_.data = code; + this->len_ = len; // Length >= 0 indicates static mode } TEMPLATABLE_VALUE(uint32_t, carrier_frequency); void encode(RemoteTransmitData *dst, Ts... x) override { - if (this->code_static_ != nullptr) { - for (size_t i = 0; i < this->code_static_len_; i++) { - auto val = this->code_static_[i]; + if (this->len_ >= 0) { + for (size_t i = 0; i < static_cast(this->len_); i++) { + auto val = this->code_.data[i]; if (val < 0) { dst->space(static_cast(-val)); } else { @@ -60,15 +63,17 @@ template class RawAction : public RemoteTransmitterActionBaseset_data(this->code_func_(x...)); + dst->set_data(this->code_.func(x...)); } dst->set_carrier_frequency(this->carrier_frequency_.value(x...)); } protected: - std::function code_func_{nullptr}; - const int32_t *code_static_{nullptr}; - int32_t code_static_len_{0}; + ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length + union Code { + RawTimings (*func)(Ts...); + const int32_t *data; + } code_; }; class RawDumper : public RemoteReceiverDumperBase { From 7705a5de063a80b763cb81104142fb3371410f53 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:25:40 -0600 Subject: [PATCH 443/526] [sx127x] Optimize send_packet action memory usage - store static data in flash (#11792) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/sx127x/__init__.py | 6 +++++- esphome/components/sx127x/automation.h | 30 +++++++++++++++----------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/esphome/components/sx127x/__init__.py b/esphome/components/sx127x/__init__.py index 33b556db07..77cb61f7f8 100644 --- a/esphome/components/sx127x/__init__.py +++ b/esphome/components/sx127x/__init__.py @@ -3,6 +3,7 @@ import esphome.codegen as cg from esphome.components import spi import esphome.config_validation as cv from esphome.const import CONF_DATA, CONF_FREQUENCY, CONF_ID +from esphome.core import ID MULTI_CONF = True CODEOWNERS = ["@swoboda1337"] @@ -321,5 +322,8 @@ async def send_packet_action_to_code(config, action_id, template_arg, args): templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8)) cg.add(var.set_data_template(templ)) else: - cg.add(var.set_data_static(data)) + # Generate static array in flash to avoid RAM copy + arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8) + arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data)) + cg.add(var.set_data_static(arr, len(data))) return var diff --git a/esphome/components/sx127x/automation.h b/esphome/components/sx127x/automation.h index eae16c11fa..fb0367fcca 100644 --- a/esphome/components/sx127x/automation.h +++ b/esphome/components/sx127x/automation.h @@ -14,28 +14,34 @@ template class RunImageCalAction : public Action, public template class SendPacketAction : public Action, public Parented { public: - void set_data_template(std::function(Ts...)> func) { - this->data_func_ = func; - this->static_ = false; + void set_data_template(std::vector (*func)(Ts...)) { + this->data_.func = func; + this->len_ = -1; // Sentinel value indicates template mode } - void set_data_static(const std::vector &data) { - this->data_static_ = data; - this->static_ = true; + void set_data_static(const uint8_t *data, size_t len) { + this->data_.data = data; + this->len_ = len; // Length >= 0 indicates static mode } void play(const Ts &...x) override { - if (this->static_) { - this->parent_->transmit_packet(this->data_static_); + std::vector data; + if (this->len_ >= 0) { + // Static mode: copy from flash to vector + data.assign(this->data_.data, this->data_.data + this->len_); } else { - this->parent_->transmit_packet(this->data_func_(x...)); + // Template mode: call function + data = this->data_.func(x...); } + this->parent_->transmit_packet(data); } protected: - bool static_{false}; - std::function(Ts...)> data_func_{}; - std::vector data_static_{}; + ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length + union Data { + std::vector (*func)(Ts...); // Function pointer (stateless lambdas) + const uint8_t *data; // Pointer to static data in flash + } data_; }; template class SetModeTxAction : public Action, public Parented { From e7ff56f1cd377128c9123a4cb4ca8c6f7f8ca8bb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:27:09 -0600 Subject: [PATCH 444/526] [remote_base] Eliminate substr() allocations in Pronto dump logging (#11726) --- .../remote_base/pronto_protocol.cpp | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/esphome/components/remote_base/pronto_protocol.cpp b/esphome/components/remote_base/pronto_protocol.cpp index 35fd782248..9fbc9e85ba 100644 --- a/esphome/components/remote_base/pronto_protocol.cpp +++ b/esphome/components/remote_base/pronto_protocol.cpp @@ -71,6 +71,7 @@ static const uint16_t FALLBACK_FREQUENCY = 64767U; // To use with frequency = 0 static const uint32_t MICROSECONDS_IN_SECONDS = 1000000UL; static const uint16_t PRONTO_DEFAULT_GAP = 45000; static const uint16_t MARK_EXCESS_MICROS = 20; +static constexpr size_t PRONTO_LOG_CHUNK_SIZE = 230; static uint16_t to_frequency_k_hz(uint16_t code) { if (code == 0) @@ -225,18 +226,18 @@ optional ProntoProtocol::decode(RemoteReceiveData src) { } void ProntoProtocol::dump(const ProntoData &data) { - std::string rest; - - rest = data.data; ESP_LOGI(TAG, "Received Pronto: data="); - while (true) { - ESP_LOGI(TAG, "%s", rest.substr(0, 230).c_str()); - if (rest.size() > 230) { - rest = rest.substr(230); - } else { - break; - } - } + + const char *ptr = data.data.c_str(); + size_t remaining = data.data.size(); + + // Log in chunks, always logging at least once (even for empty string) + do { + size_t chunk_size = remaining < PRONTO_LOG_CHUNK_SIZE ? remaining : PRONTO_LOG_CHUNK_SIZE; + ESP_LOGI(TAG, "%.*s", (int) chunk_size, ptr); + ptr += chunk_size; + remaining -= chunk_size; + } while (remaining > 0); } } // namespace remote_base From cbb98c40502be70cb50ffa8ae741dc18be324f3c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:27:56 -0600 Subject: [PATCH 445/526] [bl0940] Fix calibration number preference hash for multi-device configs (#11769) --- esphome/components/bl0940/number/calibration_number.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/bl0940/number/calibration_number.cpp b/esphome/components/bl0940/number/calibration_number.cpp index cdb26cd298..e83c3add1f 100644 --- a/esphome/components/bl0940/number/calibration_number.cpp +++ b/esphome/components/bl0940/number/calibration_number.cpp @@ -9,7 +9,7 @@ static const char *const TAG = "bl0940.number"; void CalibrationNumber::setup() { float value = 0.0f; if (this->restore_value_) { - this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); + this->pref_ = global_preferences->make_preference(this->get_preference_hash()); if (!this->pref_.load(&value)) { value = 0.0f; } From 8b9600b930d74339a9c47035bc641d147bf0d2e9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:33:29 -0600 Subject: [PATCH 446/526] [speaker] Optimize speaker.play action memory usage - store static data in flash (#11796) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/speaker/__init__.py | 7 ++++-- esphome/components/speaker/automation.h | 29 +++++++++++++++---------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/esphome/components/speaker/__init__.py b/esphome/components/speaker/__init__.py index 5f1ba94ee6..18e1d9782c 100644 --- a/esphome/components/speaker/__init__.py +++ b/esphome/components/speaker/__init__.py @@ -3,7 +3,7 @@ import esphome.codegen as cg from esphome.components import audio, audio_dac import esphome.config_validation as cv from esphome.const import CONF_DATA, CONF_ID, CONF_VOLUME -from esphome.core import CORE +from esphome.core import CORE, ID from esphome.coroutine import CoroPriority, coroutine_with_priority AUTO_LOAD = ["audio"] @@ -90,7 +90,10 @@ async def speaker_play_action(config, action_id, template_arg, args): templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8)) cg.add(var.set_data_template(templ)) else: - cg.add(var.set_data_static(data)) + # Generate static array in flash to avoid RAM copy + arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8) + arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data)) + cg.add(var.set_data_static(arr, len(data))) return var diff --git a/esphome/components/speaker/automation.h b/esphome/components/speaker/automation.h index 80bba25030..391c9e4c62 100644 --- a/esphome/components/speaker/automation.h +++ b/esphome/components/speaker/automation.h @@ -10,28 +10,33 @@ namespace speaker { template class PlayAction : public Action, public Parented { public: - void set_data_template(std::function(Ts...)> func) { - this->data_func_ = func; - this->static_ = false; + void set_data_template(std::vector (*func)(Ts...)) { + this->data_.func = func; + this->len_ = -1; // Sentinel value indicates template mode } - void set_data_static(const std::vector &data) { - this->data_static_ = data; - this->static_ = true; + + void set_data_static(const uint8_t *data, size_t len) { + this->data_.data = data; + this->len_ = len; // Length >= 0 indicates static mode } void play(const Ts &...x) override { - if (this->static_) { - this->parent_->play(this->data_static_); + if (this->len_ >= 0) { + // Static mode: pass pointer directly to play(const uint8_t *, size_t) + this->parent_->play(this->data_.data, static_cast(this->len_)); } else { - auto val = this->data_func_(x...); + // Template mode: call function and pass vector to play(const std::vector &) + auto val = this->data_.func(x...); this->parent_->play(val); } } protected: - bool static_{false}; - std::function(Ts...)> data_func_{}; - std::vector data_static_{}; + ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length + union Data { + std::vector (*func)(Ts...); // Function pointer (stateless lambdas) + const uint8_t *data; // Pointer to static data in flash + } data_; }; template class VolumeSetAction : public Action, public Parented { From fb1c67490ad170fd92bdeea357242fae9fb847b0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:33:56 -0600 Subject: [PATCH 447/526] [udp] Optimize udp.write action memory usage - store static data in flash (#11794) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/udp/__init__.py | 7 +++++-- esphome/components/udp/automation.h | 29 +++++++++++++++++------------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/esphome/components/udp/__init__.py b/esphome/components/udp/__init__.py index 6b1e4f8ed8..69abf4b989 100644 --- a/esphome/components/udp/__init__.py +++ b/esphome/components/udp/__init__.py @@ -12,7 +12,7 @@ from esphome.components.packet_transport import ( ) import esphome.config_validation as cv from esphome.const import CONF_DATA, CONF_ID, CONF_PORT, CONF_TRIGGER_ID -from esphome.core import Lambda +from esphome.core import ID, Lambda from esphome.cpp_generator import ExpressionStatement, MockObj CODEOWNERS = ["@clydebarrow"] @@ -158,5 +158,8 @@ async def udp_write_to_code(config, action_id, template_arg, args): templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8)) cg.add(var.set_data_template(templ)) else: - cg.add(var.set_data_static(data)) + # Generate static array in flash to avoid RAM copy + arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8) + arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data)) + cg.add(var.set_data_static(arr, len(data))) return var diff --git a/esphome/components/udp/automation.h b/esphome/components/udp/automation.h index c5e5e2eae8..b66c2a9892 100644 --- a/esphome/components/udp/automation.h +++ b/esphome/components/udp/automation.h @@ -11,28 +11,33 @@ namespace udp { template class UDPWriteAction : public Action, public Parented { public: - void set_data_template(std::function(Ts...)> func) { - this->data_func_ = func; - this->static_ = false; + void set_data_template(std::vector (*func)(Ts...)) { + this->data_.func = func; + this->len_ = -1; // Sentinel value indicates template mode } - void set_data_static(const std::vector &data) { - this->data_static_ = data; - this->static_ = true; + + void set_data_static(const uint8_t *data, size_t len) { + this->data_.data = data; + this->len_ = len; // Length >= 0 indicates static mode } void play(const Ts &...x) override { - if (this->static_) { - this->parent_->send_packet(this->data_static_); + if (this->len_ >= 0) { + // Static mode: pass pointer directly to send_packet(const uint8_t *, size_t) + this->parent_->send_packet(this->data_.data, static_cast(this->len_)); } else { - auto val = this->data_func_(x...); + // Template mode: call function and pass vector to send_packet(const std::vector &) + auto val = this->data_.func(x...); this->parent_->send_packet(val); } } protected: - bool static_{false}; - std::function(Ts...)> data_func_{}; - std::vector data_static_{}; + ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length + union Data { + std::vector (*func)(Ts...); // Function pointer (stateless lambdas) + const uint8_t *data; // Pointer to static data in flash + } data_; }; } // namespace udp From d516627957778427af22ce57c2d5c1294e9948d5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:37:14 -0600 Subject: [PATCH 448/526] [uart] Store static data in flash and use function pointers for lambdas (#11784) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/uart/__init__.py | 7 ++++-- esphome/components/uart/automation.h | 35 +++++++++++++++------------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index eb911ed007..cbc11d0db0 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -31,7 +31,7 @@ from esphome.const import ( PLATFORM_HOST, PlatformFramework, ) -from esphome.core import CORE +from esphome.core import CORE, ID import esphome.final_validate as fv from esphome.yaml_util import make_data_base @@ -446,7 +446,10 @@ async def uart_write_to_code(config, action_id, template_arg, args): templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8)) cg.add(var.set_data_template(templ)) else: - cg.add(var.set_data_static(cg.ArrayInitializer(*data))) + # Generate static array in flash to avoid RAM copy + arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8) + arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data)) + cg.add(var.set_data_static(arr, len(data))) return var diff --git a/esphome/components/uart/automation.h b/esphome/components/uart/automation.h index ad2c4d2bf1..c2eb308eb8 100644 --- a/esphome/components/uart/automation.h +++ b/esphome/components/uart/automation.h @@ -10,32 +10,35 @@ namespace uart { template class UARTWriteAction : public Action, public Parented { public: - void set_data_template(std::function(Ts...)> func) { - this->data_func_ = func; - this->static_ = false; + void set_data_template(std::vector (*func)(Ts...)) { + // Stateless lambdas (generated by ESPHome) implicitly convert to function pointers + this->code_.func = func; + this->len_ = -1; // Sentinel value indicates template mode } - void set_data_static(std::vector &&data) { - this->data_static_ = std::move(data); - this->static_ = true; - } - void set_data_static(std::initializer_list data) { - this->data_static_ = std::vector(data); - this->static_ = true; + + // Store pointer to static data in flash (no RAM copy) + void set_data_static(const uint8_t *data, size_t len) { + this->code_.data = data; + this->len_ = len; // Length >= 0 indicates static mode } void play(const Ts &...x) override { - if (this->static_) { - this->parent_->write_array(this->data_static_); + if (this->len_ >= 0) { + // Static mode: use pointer and length + this->parent_->write_array(this->code_.data, static_cast(this->len_)); } else { - auto val = this->data_func_(x...); + // Template mode: call function + auto val = this->code_.func(x...); this->parent_->write_array(val); } } protected: - bool static_{false}; - std::function(Ts...)> data_func_{}; - std::vector data_static_{}; + ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length + union Code { + std::vector (*func)(Ts...); // Function pointer (stateless lambdas) + const uint8_t *data; // Pointer to static data in flash + } code_; }; } // namespace uart From 7b86e1feb0f71e23dc8a7db9663f8bebb4d8075b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:39:27 -0600 Subject: [PATCH 449/526] [core] Remove deprecated EntityBase::hash_base() method (#11783) --- esphome/core/entity_base.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index 1486ff5360..2b52d66f76 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -129,9 +129,6 @@ class EntityBase { // Returns empty StringRef if object_id is dynamic (needs allocation) StringRef get_object_id_ref_for_api_() const; - /// The hash_base() function has been deprecated. It is kept in this - /// class for now, to prevent external components from not compiling. - virtual uint32_t hash_base() { return 0L; } void calc_object_id_(); /// Check if the object_id is dynamic (changes with MAC suffix) From 0d735dc259b8338b35464ddddca6c1533c331bd4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 16:46:01 -0600 Subject: [PATCH 450/526] [remote_base] Optimize abbwelcome action memory usage - store static data in flash (#11798) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/remote_base/__init__.py | 6 ++-- .../remote_base/abbwelcome_protocol.h | 29 ++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 8d735ea563..d24d24b000 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -39,7 +39,7 @@ from esphome.const import ( CONF_WAND_ID, CONF_ZERO, ) -from esphome.core import coroutine +from esphome.core import ID, coroutine from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from esphome.util import Registry, SimpleRegistry @@ -2104,7 +2104,9 @@ async def abbwelcome_action(var, config, args): ) cg.add(var.set_data_template(template_)) else: - cg.add(var.set_data_static(data_)) + arr_id = ID(f"{var.base}_data", is_declaration=True, type=cg.uint8) + arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data_)) + cg.add(var.set_data_static(arr, len(data_))) # Mirage diff --git a/esphome/components/remote_base/abbwelcome_protocol.h b/esphome/components/remote_base/abbwelcome_protocol.h index b258bd920b..4b922eb2f1 100644 --- a/esphome/components/remote_base/abbwelcome_protocol.h +++ b/esphome/components/remote_base/abbwelcome_protocol.h @@ -214,10 +214,13 @@ template class ABBWelcomeAction : public RemoteTransmitterAction TEMPLATABLE_VALUE(uint8_t, message_type) TEMPLATABLE_VALUE(uint8_t, message_id) TEMPLATABLE_VALUE(bool, auto_message_id) - void set_data_static(std::vector data) { data_static_ = std::move(data); } - void set_data_template(std::function(Ts...)> func) { - this->data_func_ = func; - has_data_func_ = true; + void set_data_template(std::vector (*func)(Ts...)) { + this->data_.func = func; + this->len_ = -1; // Sentinel value indicates template mode + } + void set_data_static(const uint8_t *data, size_t len) { + this->data_.data = data; + this->len_ = len; // Length >= 0 indicates static mode } void encode(RemoteTransmitData *dst, Ts... x) override { ABBWelcomeData data; @@ -228,19 +231,25 @@ template class ABBWelcomeAction : public RemoteTransmitterAction data.set_message_type(this->message_type_.value(x...)); data.set_message_id(this->message_id_.value(x...)); data.auto_message_id = this->auto_message_id_.value(x...); - if (has_data_func_) { - data.set_data(this->data_func_(x...)); + std::vector data_vec; + if (this->len_ >= 0) { + // Static mode: copy from flash to vector + data_vec.assign(this->data_.data, this->data_.data + this->len_); } else { - data.set_data(this->data_static_); + // Template mode: call function + data_vec = this->data_.func(x...); } + data.set_data(data_vec); data.finalize(); ABBWelcomeProtocol().encode(dst, data); } protected: - std::function(Ts...)> data_func_{}; - std::vector data_static_{}; - bool has_data_func_{false}; + ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length + union Data { + std::vector (*func)(Ts...); // Function pointer (stateless lambdas) + const uint8_t *data; // Pointer to static data in flash + } data_; }; } // namespace remote_base From 1dabe83d048601b36d29c8b33be70acc84540a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Sun, 9 Nov 2025 23:48:33 +0100 Subject: [PATCH 451/526] [nrf52] api (#11751) --- esphome/components/api/__init__.py | 1 + esphome/components/api/api_connection.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 449572c0e5..7f69a9fda1 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -227,6 +227,7 @@ CONFIG_SCHEMA = cv.All( esp32=8, # More RAM, can buffer more rp2040=5, # Limited RAM bk72xx=8, # Moderate RAM + nrf52=8, # Moderate RAM rtl87xx=8, # Moderate RAM host=16, # Abundant resources ln882x=8, # Moderate RAM diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 8c293b41a2..7eb61f08b6 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1467,6 +1467,8 @@ bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) { static constexpr auto MANUFACTURER = StringRef::from_lit("Beken"); #elif defined(USE_LN882X) static constexpr auto MANUFACTURER = StringRef::from_lit("Lightning"); +#elif defined(USE_NRF52) + static constexpr auto MANUFACTURER = StringRef::from_lit("Nordic Semiconductor"); #elif defined(USE_RTL87XX) static constexpr auto MANUFACTURER = StringRef::from_lit("Realtek"); #elif defined(USE_HOST) From 7abb6d499870ef418b27bbd63e3281aff85eb11c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 17:34:08 -0600 Subject: [PATCH 452/526] [core] Implement Global Controller Registry to reduce RAM usage (#11772) --- .../alarm_control_panel.cpp | 9 +- esphome/components/api/__init__.py | 3 + esphome/components/api/api_server.cpp | 33 +-- esphome/components/api/api_server.h | 14 +- .../binary_sensor/binary_sensor.cpp | 5 + esphome/components/climate/climate.cpp | 5 + esphome/components/cover/cover.cpp | 7 + esphome/components/datetime/date_entity.cpp | 6 +- .../components/datetime/datetime_entity.cpp | 6 +- esphome/components/datetime/time_entity.cpp | 6 +- esphome/components/event/event.cpp | 6 +- esphome/components/fan/fan.cpp | 5 + esphome/components/light/light_state.cpp | 14 +- esphome/components/lock/lock.cpp | 5 + .../components/media_player/media_player.cpp | 10 +- esphome/components/number/number.cpp | 5 + esphome/components/select/select.cpp | 5 + esphome/components/sensor/sensor.cpp | 5 + esphome/components/switch/switch.cpp | 5 + esphome/components/text/text.cpp | 5 + .../components/text_sensor/text_sensor.cpp | 5 + esphome/components/update/update_entity.cpp | 6 +- esphome/components/valve/valve.cpp | 5 + esphome/components/web_server/__init__.py | 3 + esphome/components/web_server/web_server.cpp | 54 +++- esphome/components/web_server/web_server.h | 14 +- esphome/core/__init__.py | 8 + esphome/core/config.py | 17 +- esphome/core/controller.cpp | 134 ---------- esphome/core/controller.h | 15 +- esphome/core/controller_registry.cpp | 114 ++++++++ esphome/core/controller_registry.h | 245 ++++++++++++++++++ esphome/core/defines.h | 2 + 33 files changed, 583 insertions(+), 198 deletions(-) delete mode 100644 esphome/core/controller.cpp create mode 100644 esphome/core/controller_registry.cpp create mode 100644 esphome/core/controller_registry.h diff --git a/esphome/components/alarm_control_panel/alarm_control_panel.cpp b/esphome/components/alarm_control_panel/alarm_control_panel.cpp index 9f1485ee90..c29e02c8ef 100644 --- a/esphome/components/alarm_control_panel/alarm_control_panel.cpp +++ b/esphome/components/alarm_control_panel/alarm_control_panel.cpp @@ -1,6 +1,8 @@ -#include - #include "alarm_control_panel.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" + +#include #include "esphome/core/application.h" #include "esphome/core/helpers.h" @@ -34,6 +36,9 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) { LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state))); this->current_state_ = state; this->state_callback_.call(); +#if defined(USE_ALARM_CONTROL_PANEL) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_alarm_control_panel_update(this); +#endif if (state == ACP_STATE_TRIGGERED) { this->triggered_callback_.call(); } else if (state == ACP_STATE_ARMING) { diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 7f69a9fda1..a9286c531f 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -245,6 +245,9 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) + # Track controller registration for StaticVector sizing + CORE.register_controller() + cg.add(var.set_port(config[CONF_PORT])) if config[CONF_PASSWORD]: cg.add_define("USE_API_PASSWORD") diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index e5f0d9795e..18601d74ff 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -5,6 +5,7 @@ #include "esphome/components/network/util.h" #include "esphome/core/application.h" #include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" #include "esphome/core/util.h" @@ -34,7 +35,7 @@ APIServer::APIServer() { } void APIServer::setup() { - this->setup_controller(); + ControllerRegistry::register_controller(this); #ifdef USE_API_NOISE uint32_t hash = 88491486UL; @@ -269,7 +270,7 @@ bool APIServer::check_password(const uint8_t *password_data, size_t password_len void APIServer::handle_disconnect(APIConnection *conn) {} -// Macro for entities without extra parameters +// Macro for controller update dispatch #define API_DISPATCH_UPDATE(entity_type, entity_name) \ void APIServer::on_##entity_name##_update(entity_type *obj) { /* NOLINT(bugprone-macro-parentheses) */ \ if (obj->is_internal()) \ @@ -278,15 +279,6 @@ void APIServer::handle_disconnect(APIConnection *conn) {} c->send_##entity_name##_state(obj); \ } -// Macro for entities with extra parameters (but parameters not used in send) -#define API_DISPATCH_UPDATE_IGNORE_PARAMS(entity_type, entity_name, ...) \ - void APIServer::on_##entity_name##_update(entity_type *obj, __VA_ARGS__) { /* NOLINT(bugprone-macro-parentheses) */ \ - if (obj->is_internal()) \ - return; \ - for (auto &c : this->clients_) \ - c->send_##entity_name##_state(obj); \ - } - #ifdef USE_BINARY_SENSOR API_DISPATCH_UPDATE(binary_sensor::BinarySensor, binary_sensor) #endif @@ -304,15 +296,15 @@ API_DISPATCH_UPDATE(light::LightState, light) #endif #ifdef USE_SENSOR -API_DISPATCH_UPDATE_IGNORE_PARAMS(sensor::Sensor, sensor, float state) +API_DISPATCH_UPDATE(sensor::Sensor, sensor) #endif #ifdef USE_SWITCH -API_DISPATCH_UPDATE_IGNORE_PARAMS(switch_::Switch, switch, bool state) +API_DISPATCH_UPDATE(switch_::Switch, switch) #endif #ifdef USE_TEXT_SENSOR -API_DISPATCH_UPDATE_IGNORE_PARAMS(text_sensor::TextSensor, text_sensor, const std::string &state) +API_DISPATCH_UPDATE(text_sensor::TextSensor, text_sensor) #endif #ifdef USE_CLIMATE @@ -320,7 +312,7 @@ API_DISPATCH_UPDATE(climate::Climate, climate) #endif #ifdef USE_NUMBER -API_DISPATCH_UPDATE_IGNORE_PARAMS(number::Number, number, float state) +API_DISPATCH_UPDATE(number::Number, number) #endif #ifdef USE_DATETIME_DATE @@ -336,11 +328,11 @@ API_DISPATCH_UPDATE(datetime::DateTimeEntity, datetime) #endif #ifdef USE_TEXT -API_DISPATCH_UPDATE_IGNORE_PARAMS(text::Text, text, const std::string &state) +API_DISPATCH_UPDATE(text::Text, text) #endif #ifdef USE_SELECT -API_DISPATCH_UPDATE_IGNORE_PARAMS(select::Select, select, const std::string &state, size_t index) +API_DISPATCH_UPDATE(select::Select, select) #endif #ifdef USE_LOCK @@ -356,12 +348,13 @@ API_DISPATCH_UPDATE(media_player::MediaPlayer, media_player) #endif #ifdef USE_EVENT -// Event is a special case - it's the only entity that passes extra parameters to the send method -void APIServer::on_event(event::Event *obj, const std::string &event_type) { +// Event is a special case - unlike other entities with simple state fields, +// events store their state in a member accessed via obj->get_last_event_type() +void APIServer::on_event(event::Event *obj) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_event(obj, event_type); + c->send_event(obj, obj->get_last_event_type()); } #endif diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index f1f44a266d..2d58063d6c 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -72,19 +72,19 @@ class APIServer : public Component, public Controller { void on_light_update(light::LightState *obj) override; #endif #ifdef USE_SENSOR - void on_sensor_update(sensor::Sensor *obj, float state) override; + void on_sensor_update(sensor::Sensor *obj) override; #endif #ifdef USE_SWITCH - void on_switch_update(switch_::Switch *obj, bool state) override; + void on_switch_update(switch_::Switch *obj) override; #endif #ifdef USE_TEXT_SENSOR - void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override; + void on_text_sensor_update(text_sensor::TextSensor *obj) override; #endif #ifdef USE_CLIMATE void on_climate_update(climate::Climate *obj) override; #endif #ifdef USE_NUMBER - void on_number_update(number::Number *obj, float state) override; + void on_number_update(number::Number *obj) override; #endif #ifdef USE_DATETIME_DATE void on_date_update(datetime::DateEntity *obj) override; @@ -96,10 +96,10 @@ class APIServer : public Component, public Controller { void on_datetime_update(datetime::DateTimeEntity *obj) override; #endif #ifdef USE_TEXT - void on_text_update(text::Text *obj, const std::string &state) override; + void on_text_update(text::Text *obj) override; #endif #ifdef USE_SELECT - void on_select_update(select::Select *obj, const std::string &state, size_t index) override; + void on_select_update(select::Select *obj) override; #endif #ifdef USE_LOCK void on_lock_update(lock::Lock *obj) override; @@ -141,7 +141,7 @@ class APIServer : public Component, public Controller { void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override; #endif #ifdef USE_EVENT - void on_event(event::Event *obj, const std::string &event_type) override; + void on_event(event::Event *obj) override; #endif #ifdef USE_UPDATE void on_update(update::UpdateEntity *obj) override; diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 33b3de6d72..220ed685db 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -1,4 +1,6 @@ #include "binary_sensor.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -37,6 +39,9 @@ void BinarySensor::send_state_internal(bool new_state) { // Note that set_state_ de-dups and will only trigger callbacks if the state has actually changed if (this->set_state_(new_state)) { ESP_LOGD(TAG, "'%s': New state is %s", this->get_name().c_str(), ONOFF(new_state)); +#if defined(USE_BINARY_SENSOR) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_binary_sensor_update(this); +#endif } } diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 7df38758dc..82b75660ba 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -1,4 +1,6 @@ #include "climate.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/macros.h" namespace esphome { @@ -463,6 +465,9 @@ void Climate::publish_state() { // Send state to frontend this->state_callback_.call(*this); +#if defined(USE_CLIMATE) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_climate_update(this); +#endif // Save state this->save_state_(); } diff --git a/esphome/components/cover/cover.cpp b/esphome/components/cover/cover.cpp index 654bb956a5..3062dba28a 100644 --- a/esphome/components/cover/cover.cpp +++ b/esphome/components/cover/cover.cpp @@ -1,5 +1,9 @@ #include "cover.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" + #include + #include "esphome/core/log.h" namespace esphome { @@ -169,6 +173,9 @@ void Cover::publish_state(bool save) { ESP_LOGD(TAG, " Current Operation: %s", cover_operation_to_str(this->current_operation)); this->state_callback_.call(); +#if defined(USE_COVER) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_cover_update(this); +#endif if (save) { CoverRestoreState restore{}; diff --git a/esphome/components/datetime/date_entity.cpp b/esphome/components/datetime/date_entity.cpp index c164a98b2e..2c2775ecf4 100644 --- a/esphome/components/datetime/date_entity.cpp +++ b/esphome/components/datetime/date_entity.cpp @@ -1,5 +1,6 @@ #include "date_entity.h" - +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #ifdef USE_DATETIME_DATE #include "esphome/core/log.h" @@ -32,6 +33,9 @@ void DateEntity::publish_state() { this->set_has_state(true); ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_); this->state_callback_.call(); +#if defined(USE_DATETIME_DATE) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_date_update(this); +#endif } DateCall DateEntity::make_call() { return DateCall(this); } diff --git a/esphome/components/datetime/datetime_entity.cpp b/esphome/components/datetime/datetime_entity.cpp index 4e3b051eb3..8606a47fa7 100644 --- a/esphome/components/datetime/datetime_entity.cpp +++ b/esphome/components/datetime/datetime_entity.cpp @@ -1,5 +1,6 @@ #include "datetime_entity.h" - +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #ifdef USE_DATETIME_DATETIME #include "esphome/core/log.h" @@ -48,6 +49,9 @@ void DateTimeEntity::publish_state() { ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_, this->month_, this->day_, this->hour_, this->minute_, this->second_); this->state_callback_.call(); +#if defined(USE_DATETIME_DATETIME) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_datetime_update(this); +#endif } DateTimeCall DateTimeEntity::make_call() { return DateTimeCall(this); } diff --git a/esphome/components/datetime/time_entity.cpp b/esphome/components/datetime/time_entity.cpp index 9b05c2124f..469be077ea 100644 --- a/esphome/components/datetime/time_entity.cpp +++ b/esphome/components/datetime/time_entity.cpp @@ -1,5 +1,6 @@ #include "time_entity.h" - +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #ifdef USE_DATETIME_TIME #include "esphome/core/log.h" @@ -29,6 +30,9 @@ void TimeEntity::publish_state() { ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_, this->second_); this->state_callback_.call(); +#if defined(USE_DATETIME_TIME) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_time_update(this); +#endif } TimeCall TimeEntity::make_call() { return TimeCall(this); } diff --git a/esphome/components/event/event.cpp b/esphome/components/event/event.cpp index a14afbd7f5..4c74a11388 100644 --- a/esphome/components/event/event.cpp +++ b/esphome/components/event/event.cpp @@ -1,5 +1,6 @@ #include "event.h" - +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -23,6 +24,9 @@ void Event::trigger(const std::string &event_type) { this->last_event_type_ = found; ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), this->last_event_type_); this->event_callback_.call(event_type); +#if defined(USE_EVENT) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_event(this); +#endif } void Event::set_event_types(const FixedVector &event_types) { diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 959572e9d9..d37825a651 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -1,4 +1,6 @@ #include "fan.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -181,6 +183,9 @@ void Fan::publish_state() { ESP_LOGD(TAG, " Preset Mode: %s", preset); } this->state_callback_.call(); +#if defined(USE_FAN) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_fan_update(this); +#endif this->save_state_(); } diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 7b0a698bb8..4c253ec5a8 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -1,7 +1,8 @@ -#include "esphome/core/log.h" - -#include "light_output.h" #include "light_state.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" +#include "esphome/core/log.h" +#include "light_output.h" #include "transformers.h" namespace esphome { @@ -137,7 +138,12 @@ void LightState::loop() { float LightState::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; } -void LightState::publish_state() { this->remote_values_callback_.call(); } +void LightState::publish_state() { + this->remote_values_callback_.call(); +#if defined(USE_LIGHT) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_light_update(this); +#endif +} LightOutput *LightState::get_output() const { return this->output_; } diff --git a/esphome/components/lock/lock.cpp b/esphome/components/lock/lock.cpp index ddc5445349..54fefe8745 100644 --- a/esphome/components/lock/lock.cpp +++ b/esphome/components/lock/lock.cpp @@ -1,4 +1,6 @@ #include "lock.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -53,6 +55,9 @@ void Lock::publish_state(LockState state) { this->rtc_.save(&this->state); ESP_LOGD(TAG, "'%s': Sending state %s", this->name_.c_str(), lock_state_to_string(state)); this->state_callback_.call(); +#if defined(USE_LOCK) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_lock_update(this); +#endif } void Lock::add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } diff --git a/esphome/components/media_player/media_player.cpp b/esphome/components/media_player/media_player.cpp index 3f274bf73b..b46ec39d30 100644 --- a/esphome/components/media_player/media_player.cpp +++ b/esphome/components/media_player/media_player.cpp @@ -1,5 +1,6 @@ #include "media_player.h" - +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -148,7 +149,12 @@ void MediaPlayer::add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } -void MediaPlayer::publish_state() { this->state_callback_.call(); } +void MediaPlayer::publish_state() { + this->state_callback_.call(); +#if defined(USE_MEDIA_PLAYER) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_media_player_update(this); +#endif +} } // namespace media_player } // namespace esphome diff --git a/esphome/components/number/number.cpp b/esphome/components/number/number.cpp index da08faf655..f12e0e9e1e 100644 --- a/esphome/components/number/number.cpp +++ b/esphome/components/number/number.cpp @@ -1,4 +1,6 @@ #include "number.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -32,6 +34,9 @@ void Number::publish_state(float state) { this->state = state; ESP_LOGD(TAG, "'%s': Sending state %f", this->get_name().c_str(), state); this->state_callback_.call(state); +#if defined(USE_NUMBER) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_number_update(this); +#endif } void Number::add_on_state_callback(std::function &&callback) { diff --git a/esphome/components/select/select.cpp b/esphome/components/select/select.cpp index 6bb01ba6e2..9fe7a52422 100644 --- a/esphome/components/select/select.cpp +++ b/esphome/components/select/select.cpp @@ -1,4 +1,6 @@ #include "select.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" #include @@ -33,6 +35,9 @@ void Select::publish_state(size_t index) { ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", this->get_name().c_str(), option, index); // Callback signature requires std::string, create temporary for compatibility this->state_callback_.call(std::string(option), index); +#if defined(USE_SELECT) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_select_update(this); +#endif } const char *Select::current_option() const { return this->has_state() ? this->option_at(this->active_index_) : ""; } diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index 92da4345b7..df6bd644e8 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -1,4 +1,6 @@ #include "sensor.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -131,6 +133,9 @@ void Sensor::internal_send_state_to_frontend(float state) { ESP_LOGD(TAG, "'%s': Sending state %.5f %s with %d decimals of accuracy", this->get_name().c_str(), state, this->get_unit_of_measurement_ref().c_str(), this->get_accuracy_decimals()); this->callback_.call(state); +#if defined(USE_SENSOR) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_sensor_update(this); +#endif } } // namespace sensor diff --git a/esphome/components/switch/switch.cpp b/esphome/components/switch/switch.cpp index 02cee91a76..3c3a437ff3 100644 --- a/esphome/components/switch/switch.cpp +++ b/esphome/components/switch/switch.cpp @@ -1,4 +1,6 @@ #include "switch.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -62,6 +64,9 @@ void Switch::publish_state(bool state) { ESP_LOGD(TAG, "'%s': Sending state %s", this->name_.c_str(), ONOFF(this->state)); this->state_callback_.call(this->state); +#if defined(USE_SWITCH) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_switch_update(this); +#endif } bool Switch::assumed_state() { return false; } diff --git a/esphome/components/text/text.cpp b/esphome/components/text/text.cpp index 654893d4e4..933d82c85c 100644 --- a/esphome/components/text/text.cpp +++ b/esphome/components/text/text.cpp @@ -1,4 +1,6 @@ #include "text.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -16,6 +18,9 @@ void Text::publish_state(const std::string &state) { ESP_LOGD(TAG, "'%s': Sending state %s", this->get_name().c_str(), state.c_str()); } this->state_callback_.call(state); +#if defined(USE_TEXT) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_text_update(this); +#endif } void Text::add_on_state_callback(std::function &&callback) { diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp index 0294d65861..a7bcf19967 100644 --- a/esphome/components/text_sensor/text_sensor.cpp +++ b/esphome/components/text_sensor/text_sensor.cpp @@ -1,4 +1,6 @@ #include "text_sensor.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -84,6 +86,9 @@ void TextSensor::internal_send_state_to_frontend(const std::string &state) { this->set_has_state(true); ESP_LOGD(TAG, "'%s': Sending state '%s'", this->name_.c_str(), state.c_str()); this->callback_.call(state); +#if defined(USE_TEXT_SENSOR) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_text_sensor_update(this); +#endif } } // namespace text_sensor diff --git a/esphome/components/update/update_entity.cpp b/esphome/components/update/update_entity.cpp index ce97fb1b77..567fc9fc8e 100644 --- a/esphome/components/update/update_entity.cpp +++ b/esphome/components/update/update_entity.cpp @@ -1,5 +1,6 @@ #include "update_entity.h" - +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" namespace esphome { @@ -32,6 +33,9 @@ void UpdateEntity::publish_state() { this->set_has_state(true); this->state_callback_.call(); +#if defined(USE_UPDATE) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_update(this); +#endif } } // namespace update diff --git a/esphome/components/valve/valve.cpp b/esphome/components/valve/valve.cpp index b041fe8449..381d9061de 100644 --- a/esphome/components/valve/valve.cpp +++ b/esphome/components/valve/valve.cpp @@ -1,4 +1,6 @@ #include "valve.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/log.h" #include @@ -147,6 +149,9 @@ void Valve::publish_state(bool save) { ESP_LOGD(TAG, " Current Operation: %s", valve_operation_to_str(this->current_operation)); this->state_callback_.call(); +#if defined(USE_VALVE) && defined(USE_CONTROLLER_REGISTRY) + ControllerRegistry::notify_valve_update(this); +#endif if (save) { ValveRestoreState restore{}; diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index a7fdf30eef..17ad496f30 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -289,6 +289,9 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID], paren) await cg.register_component(var, config) + # Track controller registration for StaticVector sizing + CORE.register_controller() + version = config[CONF_VERSION] cg.add(paren.set_port(config[CONF_PORT])) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 91ca076474..5a8128ba43 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -3,6 +3,8 @@ #include "esphome/components/json/json_util.h" #include "esphome/components/network/util.h" #include "esphome/core/application.h" +#include "esphome/core/defines.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" @@ -294,7 +296,7 @@ std::string WebServer::get_config_json() { } void WebServer::setup() { - this->setup_controller(this->include_internal_); + ControllerRegistry::register_controller(this); this->base_->init(); #ifdef USE_LOGGER @@ -430,7 +432,9 @@ static JsonDetail get_request_detail(AsyncWebServerRequest *request) { } #ifdef USE_SENSOR -void WebServer::on_sensor_update(sensor::Sensor *obj, float state) { +void WebServer::on_sensor_update(sensor::Sensor *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", sensor_state_json_generator); } void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -473,7 +477,9 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail #endif #ifdef USE_TEXT_SENSOR -void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) { +void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", text_sensor_state_json_generator); } void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -513,7 +519,9 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std: #endif #ifdef USE_SWITCH -void WebServer::on_switch_update(switch_::Switch *obj, bool state) { +void WebServer::on_switch_update(switch_::Switch *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", switch_state_json_generator); } void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -625,6 +633,8 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) #ifdef USE_BINARY_SENSOR void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", binary_sensor_state_json_generator); } void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -664,6 +674,8 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool #ifdef USE_FAN void WebServer::on_fan_update(fan::Fan *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", fan_state_json_generator); } void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -738,6 +750,8 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) { #ifdef USE_LIGHT void WebServer::on_light_update(light::LightState *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", light_state_json_generator); } void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -811,6 +825,8 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi #ifdef USE_COVER void WebServer::on_cover_update(cover::Cover *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", cover_state_json_generator); } void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -895,7 +911,9 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) { #endif #ifdef USE_NUMBER -void WebServer::on_number_update(number::Number *obj, float state) { +void WebServer::on_number_update(number::Number *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", number_state_json_generator); } void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -961,6 +979,8 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail #ifdef USE_DATETIME_DATE void WebServer::on_date_update(datetime::DateEntity *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", date_state_json_generator); } void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1016,6 +1036,8 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con #ifdef USE_DATETIME_TIME void WebServer::on_time_update(datetime::TimeEntity *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", time_state_json_generator); } void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1070,6 +1092,8 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con #ifdef USE_DATETIME_DATETIME void WebServer::on_datetime_update(datetime::DateTimeEntity *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", datetime_state_json_generator); } void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1124,7 +1148,9 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s #endif // USE_DATETIME_DATETIME #ifdef USE_TEXT -void WebServer::on_text_update(text::Text *obj, const std::string &state) { +void WebServer::on_text_update(text::Text *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", text_state_json_generator); } void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1178,7 +1204,9 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json #endif #ifdef USE_SELECT -void WebServer::on_select_update(select::Select *obj, const std::string &state, size_t index) { +void WebServer::on_select_update(select::Select *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", select_state_json_generator); } void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1237,6 +1265,8 @@ std::string WebServer::select_json(select::Select *obj, const char *value, JsonD #ifdef USE_CLIMATE void WebServer::on_climate_update(climate::Climate *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", climate_state_json_generator); } void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1378,6 +1408,8 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf #ifdef USE_LOCK void WebServer::on_lock_update(lock::Lock *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", lock_state_json_generator); } void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1449,6 +1481,8 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet #ifdef USE_VALVE void WebServer::on_valve_update(valve::Valve *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", valve_state_json_generator); } void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1530,6 +1564,8 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) { #ifdef USE_ALARM_CONTROL_PANEL void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", alarm_control_panel_state_json_generator); } void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1607,7 +1643,9 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro #endif #ifdef USE_EVENT -void WebServer::on_event(event::Event *obj, const std::string &event_type) { +void WebServer::on_event(event::Event *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", event_state_json_generator); } diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 328140cfae..7e1af88645 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -255,7 +255,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif #ifdef USE_SENSOR - void on_sensor_update(sensor::Sensor *obj, float state) override; + void on_sensor_update(sensor::Sensor *obj) override; /// Handle a sensor request under '/sensor/'. void handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match); @@ -266,7 +266,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif #ifdef USE_SWITCH - void on_switch_update(switch_::Switch *obj, bool state) override; + void on_switch_update(switch_::Switch *obj) override; /// Handle a switch request under '/switch//'. void handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match); @@ -324,7 +324,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif #ifdef USE_TEXT_SENSOR - void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override; + void on_text_sensor_update(text_sensor::TextSensor *obj) override; /// Handle a text sensor request under '/text_sensor/'. void handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match); @@ -348,7 +348,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif #ifdef USE_NUMBER - void on_number_update(number::Number *obj, float state) override; + void on_number_update(number::Number *obj) override; /// Handle a number request under '/number/'. void handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match); @@ -392,7 +392,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif #ifdef USE_TEXT - void on_text_update(text::Text *obj, const std::string &state) override; + void on_text_update(text::Text *obj) override; /// Handle a text input request under '/text/'. void handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match); @@ -403,7 +403,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif #ifdef USE_SELECT - void on_select_update(select::Select *obj, const std::string &state, size_t index) override; + void on_select_update(select::Select *obj) override; /// Handle a select request under '/select/'. void handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match); @@ -462,7 +462,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif #ifdef USE_EVENT - void on_event(event::Event *obj, const std::string &event_type) override; + void on_event(event::Event *obj) override; static std::string event_state_json_generator(WebServer *web_server, void *source); static std::string event_all_json_generator(WebServer *web_server, void *source); diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index fed5265d6b..08753b0f2d 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -48,6 +48,9 @@ if TYPE_CHECKING: _LOGGER = logging.getLogger(__name__) +# Key for tracking controller count in CORE.data for ControllerRegistry StaticVector sizing +KEY_CONTROLLER_REGISTRY_COUNT = "controller_registry_count" + class EsphomeError(Exception): """General ESPHome exception occurred.""" @@ -910,6 +913,11 @@ class EsphomeCore: """ self.platform_counts[platform_name] += 1 + def register_controller(self) -> None: + """Track registration of a Controller for ControllerRegistry StaticVector sizing.""" + controller_count = self.data.setdefault(KEY_CONTROLLER_REGISTRY_COUNT, 0) + self.data[KEY_CONTROLLER_REGISTRY_COUNT] = controller_count + 1 + @property def cpp_main_section(self): from esphome.cpp_generator import statement diff --git a/esphome/core/config.py b/esphome/core/config.py index 2740453808..763f9ebd9f 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -40,7 +40,12 @@ from esphome.const import ( PlatformFramework, __version__ as ESPHOME_VERSION, ) -from esphome.core import CORE, CoroPriority, coroutine_with_priority +from esphome.core import ( + CORE, + KEY_CONTROLLER_REGISTRY_COUNT, + CoroPriority, + coroutine_with_priority, +) from esphome.helpers import ( copy_file_if_changed, fnv1a_32bit_hash, @@ -462,6 +467,15 @@ async def _add_platform_defines() -> None: cg.add_define(f"USE_{platform_name.upper()}") +@coroutine_with_priority(CoroPriority.FINAL) +async def _add_controller_registry_define() -> None: + # Generate StaticVector size for ControllerRegistry + controller_count = CORE.data.get(KEY_CONTROLLER_REGISTRY_COUNT, 0) + if controller_count > 0: + cg.add_define("USE_CONTROLLER_REGISTRY") + cg.add_define("CONTROLLER_REGISTRY_MAX", controller_count) + + @coroutine_with_priority(CoroPriority.CORE) async def to_code(config: ConfigType) -> None: cg.add_global(cg.global_ns.namespace("esphome").using) @@ -483,6 +497,7 @@ async def to_code(config: ConfigType) -> None: cg.add_define("ESPHOME_COMPONENT_COUNT", len(CORE.component_ids)) CORE.add_job(_add_platform_defines) + CORE.add_job(_add_controller_registry_define) CORE.add_job(_add_automations, config) diff --git a/esphome/core/controller.cpp b/esphome/core/controller.cpp deleted file mode 100644 index f7ff5a9734..0000000000 --- a/esphome/core/controller.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "controller.h" -#include "esphome/core/application.h" -#include "esphome/core/log.h" - -namespace esphome { - -void Controller::setup_controller(bool include_internal) { -#ifdef USE_BINARY_SENSOR - for (auto *obj : App.get_binary_sensors()) { - if (include_internal || !obj->is_internal()) { - obj->add_full_state_callback( - [this, obj](optional previous, optional state) { this->on_binary_sensor_update(obj); }); - } - } -#endif -#ifdef USE_FAN - for (auto *obj : App.get_fans()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj]() { this->on_fan_update(obj); }); - } -#endif -#ifdef USE_LIGHT - for (auto *obj : App.get_lights()) { - if (include_internal || !obj->is_internal()) - obj->add_new_remote_values_callback([this, obj]() { this->on_light_update(obj); }); - } -#endif -#ifdef USE_SENSOR - for (auto *obj : App.get_sensors()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj](float state) { this->on_sensor_update(obj, state); }); - } -#endif -#ifdef USE_SWITCH - for (auto *obj : App.get_switches()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj](bool state) { this->on_switch_update(obj, state); }); - } -#endif -#ifdef USE_COVER - for (auto *obj : App.get_covers()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj]() { this->on_cover_update(obj); }); - } -#endif -#ifdef USE_TEXT_SENSOR - for (auto *obj : App.get_text_sensors()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj](const std::string &state) { this->on_text_sensor_update(obj, state); }); - } -#endif -#ifdef USE_CLIMATE - for (auto *obj : App.get_climates()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj](climate::Climate & /*unused*/) { this->on_climate_update(obj); }); - } -#endif -#ifdef USE_NUMBER - for (auto *obj : App.get_numbers()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj](float state) { this->on_number_update(obj, state); }); - } -#endif -#ifdef USE_DATETIME_DATE - for (auto *obj : App.get_dates()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj]() { this->on_date_update(obj); }); - } -#endif -#ifdef USE_DATETIME_TIME - for (auto *obj : App.get_times()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj]() { this->on_time_update(obj); }); - } -#endif -#ifdef USE_DATETIME_DATETIME - for (auto *obj : App.get_datetimes()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj]() { this->on_datetime_update(obj); }); - } -#endif -#ifdef USE_TEXT - for (auto *obj : App.get_texts()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj](const std::string &state) { this->on_text_update(obj, state); }); - } -#endif -#ifdef USE_SELECT - for (auto *obj : App.get_selects()) { - if (include_internal || !obj->is_internal()) { - obj->add_on_state_callback( - [this, obj](const std::string &state, size_t index) { this->on_select_update(obj, state, index); }); - } - } -#endif -#ifdef USE_LOCK - for (auto *obj : App.get_locks()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj]() { this->on_lock_update(obj); }); - } -#endif -#ifdef USE_VALVE - for (auto *obj : App.get_valves()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj]() { this->on_valve_update(obj); }); - } -#endif -#ifdef USE_MEDIA_PLAYER - for (auto *obj : App.get_media_players()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj]() { this->on_media_player_update(obj); }); - } -#endif -#ifdef USE_ALARM_CONTROL_PANEL - for (auto *obj : App.get_alarm_control_panels()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj]() { this->on_alarm_control_panel_update(obj); }); - } -#endif -#ifdef USE_EVENT - for (auto *obj : App.get_events()) { - if (include_internal || !obj->is_internal()) - obj->add_on_event_callback([this, obj](const std::string &event_type) { this->on_event(obj, event_type); }); - } -#endif -#ifdef USE_UPDATE - for (auto *obj : App.get_updates()) { - if (include_internal || !obj->is_internal()) - obj->add_on_state_callback([this, obj]() { this->on_update(obj); }); - } -#endif -} - -} // namespace esphome diff --git a/esphome/core/controller.h b/esphome/core/controller.h index b475e326ee..697017217d 100644 --- a/esphome/core/controller.h +++ b/esphome/core/controller.h @@ -69,7 +69,6 @@ namespace esphome { class Controller { public: - void setup_controller(bool include_internal = false); #ifdef USE_BINARY_SENSOR virtual void on_binary_sensor_update(binary_sensor::BinarySensor *obj){}; #endif @@ -80,22 +79,22 @@ class Controller { virtual void on_light_update(light::LightState *obj){}; #endif #ifdef USE_SENSOR - virtual void on_sensor_update(sensor::Sensor *obj, float state){}; + virtual void on_sensor_update(sensor::Sensor *obj){}; #endif #ifdef USE_SWITCH - virtual void on_switch_update(switch_::Switch *obj, bool state){}; + virtual void on_switch_update(switch_::Switch *obj){}; #endif #ifdef USE_COVER virtual void on_cover_update(cover::Cover *obj){}; #endif #ifdef USE_TEXT_SENSOR - virtual void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state){}; + virtual void on_text_sensor_update(text_sensor::TextSensor *obj){}; #endif #ifdef USE_CLIMATE virtual void on_climate_update(climate::Climate *obj){}; #endif #ifdef USE_NUMBER - virtual void on_number_update(number::Number *obj, float state){}; + virtual void on_number_update(number::Number *obj){}; #endif #ifdef USE_DATETIME_DATE virtual void on_date_update(datetime::DateEntity *obj){}; @@ -107,10 +106,10 @@ class Controller { virtual void on_datetime_update(datetime::DateTimeEntity *obj){}; #endif #ifdef USE_TEXT - virtual void on_text_update(text::Text *obj, const std::string &state){}; + virtual void on_text_update(text::Text *obj){}; #endif #ifdef USE_SELECT - virtual void on_select_update(select::Select *obj, const std::string &state, size_t index){}; + virtual void on_select_update(select::Select *obj){}; #endif #ifdef USE_LOCK virtual void on_lock_update(lock::Lock *obj){}; @@ -125,7 +124,7 @@ class Controller { virtual void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj){}; #endif #ifdef USE_EVENT - virtual void on_event(event::Event *obj, const std::string &event_type){}; + virtual void on_event(event::Event *obj){}; #endif #ifdef USE_UPDATE virtual void on_update(update::UpdateEntity *obj){}; diff --git a/esphome/core/controller_registry.cpp b/esphome/core/controller_registry.cpp new file mode 100644 index 0000000000..0a84bb0d0d --- /dev/null +++ b/esphome/core/controller_registry.cpp @@ -0,0 +1,114 @@ +#include "esphome/core/controller_registry.h" + +#ifdef USE_CONTROLLER_REGISTRY + +#include "esphome/core/controller.h" + +namespace esphome { + +StaticVector ControllerRegistry::controllers; + +void ControllerRegistry::register_controller(Controller *controller) { controllers.push_back(controller); } + +// Macro for standard registry notification dispatch - calls on__update() +#define CONTROLLER_REGISTRY_NOTIFY(entity_type, entity_name) \ + void ControllerRegistry::notify_##entity_name##_update(entity_type *obj) { /* NOLINT(bugprone-macro-parentheses) */ \ + for (auto *controller : controllers) { \ + controller->on_##entity_name##_update(obj); \ + } \ + } + +// Macro for entities where controller method has no "_update" suffix (Event, Update) +#define CONTROLLER_REGISTRY_NOTIFY_NO_UPDATE_SUFFIX(entity_type, entity_name) \ + void ControllerRegistry::notify_##entity_name(entity_type *obj) { /* NOLINT(bugprone-macro-parentheses) */ \ + for (auto *controller : controllers) { \ + controller->on_##entity_name(obj); \ + } \ + } + +#ifdef USE_BINARY_SENSOR +CONTROLLER_REGISTRY_NOTIFY(binary_sensor::BinarySensor, binary_sensor) +#endif + +#ifdef USE_FAN +CONTROLLER_REGISTRY_NOTIFY(fan::Fan, fan) +#endif + +#ifdef USE_LIGHT +CONTROLLER_REGISTRY_NOTIFY(light::LightState, light) +#endif + +#ifdef USE_SENSOR +CONTROLLER_REGISTRY_NOTIFY(sensor::Sensor, sensor) +#endif + +#ifdef USE_SWITCH +CONTROLLER_REGISTRY_NOTIFY(switch_::Switch, switch) +#endif + +#ifdef USE_COVER +CONTROLLER_REGISTRY_NOTIFY(cover::Cover, cover) +#endif + +#ifdef USE_TEXT_SENSOR +CONTROLLER_REGISTRY_NOTIFY(text_sensor::TextSensor, text_sensor) +#endif + +#ifdef USE_CLIMATE +CONTROLLER_REGISTRY_NOTIFY(climate::Climate, climate) +#endif + +#ifdef USE_NUMBER +CONTROLLER_REGISTRY_NOTIFY(number::Number, number) +#endif + +#ifdef USE_DATETIME_DATE +CONTROLLER_REGISTRY_NOTIFY(datetime::DateEntity, date) +#endif + +#ifdef USE_DATETIME_TIME +CONTROLLER_REGISTRY_NOTIFY(datetime::TimeEntity, time) +#endif + +#ifdef USE_DATETIME_DATETIME +CONTROLLER_REGISTRY_NOTIFY(datetime::DateTimeEntity, datetime) +#endif + +#ifdef USE_TEXT +CONTROLLER_REGISTRY_NOTIFY(text::Text, text) +#endif + +#ifdef USE_SELECT +CONTROLLER_REGISTRY_NOTIFY(select::Select, select) +#endif + +#ifdef USE_LOCK +CONTROLLER_REGISTRY_NOTIFY(lock::Lock, lock) +#endif + +#ifdef USE_VALVE +CONTROLLER_REGISTRY_NOTIFY(valve::Valve, valve) +#endif + +#ifdef USE_MEDIA_PLAYER +CONTROLLER_REGISTRY_NOTIFY(media_player::MediaPlayer, media_player) +#endif + +#ifdef USE_ALARM_CONTROL_PANEL +CONTROLLER_REGISTRY_NOTIFY(alarm_control_panel::AlarmControlPanel, alarm_control_panel) +#endif + +#ifdef USE_EVENT +CONTROLLER_REGISTRY_NOTIFY_NO_UPDATE_SUFFIX(event::Event, event) +#endif + +#ifdef USE_UPDATE +CONTROLLER_REGISTRY_NOTIFY_NO_UPDATE_SUFFIX(update::UpdateEntity, update) +#endif + +#undef CONTROLLER_REGISTRY_NOTIFY +#undef CONTROLLER_REGISTRY_NOTIFY_NO_UPDATE_SUFFIX + +} // namespace esphome + +#endif // USE_CONTROLLER_REGISTRY diff --git a/esphome/core/controller_registry.h b/esphome/core/controller_registry.h new file mode 100644 index 0000000000..640a276a0a --- /dev/null +++ b/esphome/core/controller_registry.h @@ -0,0 +1,245 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_CONTROLLER_REGISTRY + +#include "esphome/core/helpers.h" + +// Forward declarations +namespace esphome { + +class Controller; + +#ifdef USE_BINARY_SENSOR +namespace binary_sensor { +class BinarySensor; +} +#endif + +#ifdef USE_FAN +namespace fan { +class Fan; +} +#endif + +#ifdef USE_LIGHT +namespace light { +class LightState; +} +#endif + +#ifdef USE_SENSOR +namespace sensor { +class Sensor; +} +#endif + +#ifdef USE_SWITCH +namespace switch_ { +class Switch; +} +#endif + +#ifdef USE_COVER +namespace cover { +class Cover; +} +#endif + +#ifdef USE_TEXT_SENSOR +namespace text_sensor { +class TextSensor; +} +#endif + +#ifdef USE_CLIMATE +namespace climate { +class Climate; +} +#endif + +#ifdef USE_NUMBER +namespace number { +class Number; +} +#endif + +#ifdef USE_DATETIME_DATE +namespace datetime { +class DateEntity; +} +#endif + +#ifdef USE_DATETIME_TIME +namespace datetime { +class TimeEntity; +} +#endif + +#ifdef USE_DATETIME_DATETIME +namespace datetime { +class DateTimeEntity; +} +#endif + +#ifdef USE_TEXT +namespace text { +class Text; +} +#endif + +#ifdef USE_SELECT +namespace select { +class Select; +} +#endif + +#ifdef USE_LOCK +namespace lock { +class Lock; +} +#endif + +#ifdef USE_VALVE +namespace valve { +class Valve; +} +#endif + +#ifdef USE_MEDIA_PLAYER +namespace media_player { +class MediaPlayer; +} +#endif + +#ifdef USE_ALARM_CONTROL_PANEL +namespace alarm_control_panel { +class AlarmControlPanel; +} +#endif + +#ifdef USE_EVENT +namespace event { +class Event; +} +#endif + +#ifdef USE_UPDATE +namespace update { +class UpdateEntity; +} +#endif + +/** Global registry for Controllers to receive entity state updates. + * + * This singleton registry allows Controllers (APIServer, WebServer) to receive + * entity state change notifications without storing per-entity callbacks. + * + * Instead of each entity maintaining controller callbacks (32 bytes overhead per entity), + * entities call ControllerRegistry::notify_*_update() which iterates the small list + * of registered controllers (typically 2: API and WebServer). + * + * Controllers read state directly from entities using existing accessors (obj->state, etc.) + * rather than receiving it as callback parameters that were being ignored anyway. + * + * Memory savings: 32 bytes per entity (2 controllers × 16 bytes std::function overhead) + * Typical config (25 entities): ~780 bytes saved + * Large config (80 entities): ~2,540 bytes saved + */ +class ControllerRegistry { + public: + /** Register a controller to receive entity state updates. + * + * Controllers should call this in their setup() method. + * Typically only APIServer and WebServer register. + */ + static void register_controller(Controller *controller); + +#ifdef USE_BINARY_SENSOR + static void notify_binary_sensor_update(binary_sensor::BinarySensor *obj); +#endif + +#ifdef USE_FAN + static void notify_fan_update(fan::Fan *obj); +#endif + +#ifdef USE_LIGHT + static void notify_light_update(light::LightState *obj); +#endif + +#ifdef USE_SENSOR + static void notify_sensor_update(sensor::Sensor *obj); +#endif + +#ifdef USE_SWITCH + static void notify_switch_update(switch_::Switch *obj); +#endif + +#ifdef USE_COVER + static void notify_cover_update(cover::Cover *obj); +#endif + +#ifdef USE_TEXT_SENSOR + static void notify_text_sensor_update(text_sensor::TextSensor *obj); +#endif + +#ifdef USE_CLIMATE + static void notify_climate_update(climate::Climate *obj); +#endif + +#ifdef USE_NUMBER + static void notify_number_update(number::Number *obj); +#endif + +#ifdef USE_DATETIME_DATE + static void notify_date_update(datetime::DateEntity *obj); +#endif + +#ifdef USE_DATETIME_TIME + static void notify_time_update(datetime::TimeEntity *obj); +#endif + +#ifdef USE_DATETIME_DATETIME + static void notify_datetime_update(datetime::DateTimeEntity *obj); +#endif + +#ifdef USE_TEXT + static void notify_text_update(text::Text *obj); +#endif + +#ifdef USE_SELECT + static void notify_select_update(select::Select *obj); +#endif + +#ifdef USE_LOCK + static void notify_lock_update(lock::Lock *obj); +#endif + +#ifdef USE_VALVE + static void notify_valve_update(valve::Valve *obj); +#endif + +#ifdef USE_MEDIA_PLAYER + static void notify_media_player_update(media_player::MediaPlayer *obj); +#endif + +#ifdef USE_ALARM_CONTROL_PANEL + static void notify_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj); +#endif + +#ifdef USE_EVENT + static void notify_event(event::Event *obj); +#endif + +#ifdef USE_UPDATE + static void notify_update(update::UpdateEntity *obj); +#endif + + protected: + static StaticVector controllers; +}; + +} // namespace esphome + +#endif // USE_CONTROLLER_REGISTRY diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 2be32058ea..8230518071 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -28,6 +28,7 @@ #define USE_BUTTON #define USE_CAMERA #define USE_CLIMATE +#define USE_CONTROLLER_REGISTRY #define USE_COVER #define USE_DATETIME #define USE_DATETIME_DATE @@ -296,6 +297,7 @@ #define USE_DASHBOARD_IMPORT // Default counts for static analysis +#define CONTROLLER_REGISTRY_MAX 2 #define ESPHOME_COMPONENT_COUNT 50 #define ESPHOME_DEVICE_COUNT 10 #define ESPHOME_AREA_COUNT 10 From fbbdad75f6ad8b10b6c8926abd7a82a6b1c55345 Mon Sep 17 00:00:00 2001 From: Paul Schulz Date: Mon, 10 Nov 2025 11:56:02 +1030 Subject: [PATCH 453/526] [sx126x] Change BUSY, RST, DIO1 pins to general GPIO (from internal) (#11782) --- esphome/components/sx126x/__init__.py | 6 +++--- esphome/components/sx126x/sx126x.h | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/esphome/components/sx126x/__init__.py b/esphome/components/sx126x/__init__.py index f8f3b9d104..1eb83b7a33 100644 --- a/esphome/components/sx126x/__init__.py +++ b/esphome/components/sx126x/__init__.py @@ -189,7 +189,7 @@ CONFIG_SCHEMA = ( cv.GenerateID(): cv.declare_id(SX126x), cv.Optional(CONF_BANDWIDTH, default="125_0kHz"): cv.enum(BW), cv.Optional(CONF_BITRATE, default=4800): cv.int_range(min=600, max=300000), - cv.Required(CONF_BUSY_PIN): pins.internal_gpio_input_pin_schema, + cv.Required(CONF_BUSY_PIN): pins.gpio_input_pin_schema, cv.Optional(CONF_CODING_RATE, default="CR_4_5"): cv.enum(CODING_RATE), cv.Optional(CONF_CRC_ENABLE, default=False): cv.boolean, cv.Optional(CONF_CRC_INVERTED, default=True): cv.boolean, @@ -201,7 +201,7 @@ CONFIG_SCHEMA = ( cv.hex_int, cv.Range(min=0, max=0xFFFF) ), cv.Optional(CONF_DEVIATION, default=5000): cv.int_range(min=0, max=100000), - cv.Required(CONF_DIO1_PIN): pins.internal_gpio_input_pin_schema, + cv.Required(CONF_DIO1_PIN): pins.gpio_input_pin_schema, cv.Required(CONF_FREQUENCY): cv.int_range(min=137000000, max=1020000000), cv.Required(CONF_HW_VERSION): cv.one_of( "sx1261", "sx1262", "sx1268", "llcc68", lower=True @@ -213,7 +213,7 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_PAYLOAD_LENGTH, default=0): cv.int_range(min=0, max=256), cv.Optional(CONF_PREAMBLE_DETECT, default=2): cv.int_range(min=0, max=4), cv.Optional(CONF_PREAMBLE_SIZE, default=8): cv.int_range(min=1, max=65535), - cv.Required(CONF_RST_PIN): pins.internal_gpio_output_pin_schema, + cv.Required(CONF_RST_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_RX_START, default=True): cv.boolean, cv.Required(CONF_RF_SWITCH): cv.boolean, cv.Optional(CONF_SHAPING, default="NONE"): cv.enum(SHAPING), diff --git a/esphome/components/sx126x/sx126x.h b/esphome/components/sx126x/sx126x.h index 47d6449738..850d7d4c77 100644 --- a/esphome/components/sx126x/sx126x.h +++ b/esphome/components/sx126x/sx126x.h @@ -64,7 +64,7 @@ class SX126x : public Component, void dump_config() override; void set_bandwidth(SX126xBw bandwidth) { this->bandwidth_ = bandwidth; } void set_bitrate(uint32_t bitrate) { this->bitrate_ = bitrate; } - void set_busy_pin(InternalGPIOPin *busy_pin) { this->busy_pin_ = busy_pin; } + void set_busy_pin(GPIOPin *busy_pin) { this->busy_pin_ = busy_pin; } void set_coding_rate(uint8_t coding_rate) { this->coding_rate_ = coding_rate; } void set_crc_enable(bool crc_enable) { this->crc_enable_ = crc_enable; } void set_crc_inverted(bool crc_inverted) { this->crc_inverted_ = crc_inverted; } @@ -72,7 +72,7 @@ class SX126x : public Component, void set_crc_polynomial(uint16_t crc_polynomial) { this->crc_polynomial_ = crc_polynomial; } void set_crc_initial(uint16_t crc_initial) { this->crc_initial_ = crc_initial; } void set_deviation(uint32_t deviation) { this->deviation_ = deviation; } - void set_dio1_pin(InternalGPIOPin *dio1_pin) { this->dio1_pin_ = dio1_pin; } + void set_dio1_pin(GPIOPin *dio1_pin) { this->dio1_pin_ = dio1_pin; } void set_frequency(uint32_t frequency) { this->frequency_ = frequency; } void set_hw_version(const std::string &hw_version) { this->hw_version_ = hw_version; } void set_mode_rx(); @@ -85,7 +85,7 @@ class SX126x : public Component, void set_payload_length(uint8_t payload_length) { this->payload_length_ = payload_length; } void set_preamble_detect(uint16_t preamble_detect) { this->preamble_detect_ = preamble_detect; } void set_preamble_size(uint16_t preamble_size) { this->preamble_size_ = preamble_size; } - void set_rst_pin(InternalGPIOPin *rst_pin) { this->rst_pin_ = rst_pin; } + void set_rst_pin(GPIOPin *rst_pin) { this->rst_pin_ = rst_pin; } void set_rx_start(bool rx_start) { this->rx_start_ = rx_start; } void set_rf_switch(bool rf_switch) { this->rf_switch_ = rf_switch; } void set_shaping(uint8_t shaping) { this->shaping_ = shaping; } @@ -115,9 +115,9 @@ class SX126x : public Component, std::vector listeners_; std::vector packet_; std::vector sync_value_; - InternalGPIOPin *busy_pin_{nullptr}; - InternalGPIOPin *dio1_pin_{nullptr}; - InternalGPIOPin *rst_pin_{nullptr}; + GPIOPin *busy_pin_{nullptr}; + GPIOPin *dio1_pin_{nullptr}; + GPIOPin *rst_pin_{nullptr}; std::string hw_version_; char version_[16]; SX126xBw bandwidth_{SX126X_BW_125000}; From c17a31a8f836503b742a63f87125b6532f3db4ee Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Nov 2025 19:28:49 -0600 Subject: [PATCH 454/526] Ensure event paths are enabled in api compile tests (#11776) --- tests/components/api/common-base.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/components/api/common-base.yaml b/tests/components/api/common-base.yaml index c90fa4dfef..fc53b8ac7e 100644 --- a/tests/components/api/common-base.yaml +++ b/tests/components/api/common-base.yaml @@ -178,6 +178,14 @@ api: - logger.log: "Skipped loops" - logger.log: "After combined test" +event: + - platform: template + name: Test Event + id: test_event + event_types: + - single_click + - double_click + globals: - id: api_continuation_test_counter type: int From b47e89a7d593d3b38a9cadf0b75ea605c666cd0a Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 10 Nov 2025 03:21:38 +0100 Subject: [PATCH 455/526] [nrf52,watchdog] do not disable watchog if it is not nesesery (#11686) --- esphome/components/debug/__init__.py | 1 + esphome/components/zephyr/core.cpp | 10 +++++++++- esphome/core/defines.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/esphome/components/debug/__init__.py b/esphome/components/debug/__init__.py index 6b4205a545..dc032f442e 100644 --- a/esphome/components/debug/__init__.py +++ b/esphome/components/debug/__init__.py @@ -59,6 +59,7 @@ async def to_code(config): zephyr_add_prj_conf("SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL", True) var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) + cg.add_define("USE_DEBUG") FILTER_SOURCE_FILES = filter_source_files_from_platform( diff --git a/esphome/components/zephyr/core.cpp b/esphome/components/zephyr/core.cpp index 365b6b8ed2..d5427a0ebf 100644 --- a/esphome/components/zephyr/core.cpp +++ b/esphome/components/zephyr/core.cpp @@ -6,6 +6,7 @@ #include #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/defines.h" namespace esphome { @@ -25,7 +26,14 @@ void arch_init() { wdt_config.window.max = 2000; wdt_channel_id = wdt_install_timeout(WDT, &wdt_config); if (wdt_channel_id >= 0) { - wdt_setup(WDT, WDT_OPT_PAUSE_HALTED_BY_DBG | WDT_OPT_PAUSE_IN_SLEEP); + uint8_t options = 0; +#ifdef USE_DEBUG + options |= WDT_OPT_PAUSE_HALTED_BY_DBG; +#endif +#ifdef USE_DEEP_SLEEP + options |= WDT_OPT_PAUSE_IN_SLEEP; +#endif + wdt_setup(WDT, options); } } } diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 8230518071..ac725fbca9 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -34,6 +34,7 @@ #define USE_DATETIME_DATE #define USE_DATETIME_DATETIME #define USE_DATETIME_TIME +#define USE_DEBUG #define USE_DEEP_SLEEP #define USE_DEVICES #define USE_DISPLAY From 2a166536426c63c3ef588e76841bbbcf24ec4f6e Mon Sep 17 00:00:00 2001 From: On Freund Date: Mon, 10 Nov 2025 15:44:27 +0200 Subject: [PATCH 456/526] HLK-FM22X Face Recognition module component (#8059) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Claude Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston --- CODEOWNERS | 1 + esphome/components/hlk_fm22x/__init__.py | 247 +++++++++++++ esphome/components/hlk_fm22x/binary_sensor.py | 21 ++ esphome/components/hlk_fm22x/hlk_fm22x.cpp | 325 ++++++++++++++++++ esphome/components/hlk_fm22x/hlk_fm22x.h | 224 ++++++++++++ esphome/components/hlk_fm22x/sensor.py | 47 +++ esphome/components/hlk_fm22x/text_sensor.py | 42 +++ .../components/hlk_fm22x/test.esp32-idf.yaml | 47 +++ .../hlk_fm22x/test.esp8266-ard.yaml | 47 +++ .../components/hlk_fm22x/test.rp2040-ard.yaml | 47 +++ 10 files changed, 1048 insertions(+) create mode 100644 esphome/components/hlk_fm22x/__init__.py create mode 100644 esphome/components/hlk_fm22x/binary_sensor.py create mode 100644 esphome/components/hlk_fm22x/hlk_fm22x.cpp create mode 100644 esphome/components/hlk_fm22x/hlk_fm22x.h create mode 100644 esphome/components/hlk_fm22x/sensor.py create mode 100644 esphome/components/hlk_fm22x/text_sensor.py create mode 100644 tests/components/hlk_fm22x/test.esp32-idf.yaml create mode 100644 tests/components/hlk_fm22x/test.esp8266-ard.yaml create mode 100644 tests/components/hlk_fm22x/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 7e785db451..393774372f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -206,6 +206,7 @@ esphome/components/hdc2010/* @optimusprimespace @ssieb esphome/components/he60r/* @clydebarrow esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal +esphome/components/hlk_fm22x/* @OnFreund esphome/components/hm3301/* @freekode esphome/components/hmac_md5/* @dwmw2 esphome/components/homeassistant/* @esphome/core @OttoWinter diff --git a/esphome/components/hlk_fm22x/__init__.py b/esphome/components/hlk_fm22x/__init__.py new file mode 100644 index 0000000000..efd64b6513 --- /dev/null +++ b/esphome/components/hlk_fm22x/__init__.py @@ -0,0 +1,247 @@ +from esphome import automation +import esphome.codegen as cg +from esphome.components import uart +import esphome.config_validation as cv +from esphome.const import ( + CONF_DIRECTION, + CONF_ID, + CONF_NAME, + CONF_ON_ENROLLMENT_DONE, + CONF_ON_ENROLLMENT_FAILED, + CONF_TRIGGER_ID, +) + +CODEOWNERS = ["@OnFreund"] +DEPENDENCIES = ["uart"] +AUTO_LOAD = ["binary_sensor", "sensor", "text_sensor"] +MULTI_CONF = True + +CONF_HLK_FM22X_ID = "hlk_fm22x_id" +CONF_FACE_ID = "face_id" +CONF_ON_FACE_SCAN_MATCHED = "on_face_scan_matched" +CONF_ON_FACE_SCAN_UNMATCHED = "on_face_scan_unmatched" +CONF_ON_FACE_SCAN_INVALID = "on_face_scan_invalid" +CONF_ON_FACE_INFO = "on_face_info" + +hlk_fm22x_ns = cg.esphome_ns.namespace("hlk_fm22x") +HlkFm22xComponent = hlk_fm22x_ns.class_( + "HlkFm22xComponent", cg.PollingComponent, uart.UARTDevice +) + +FaceScanMatchedTrigger = hlk_fm22x_ns.class_( + "FaceScanMatchedTrigger", automation.Trigger.template(cg.int16, cg.std_string) +) + +FaceScanUnmatchedTrigger = hlk_fm22x_ns.class_( + "FaceScanUnmatchedTrigger", automation.Trigger.template() +) + +FaceScanInvalidTrigger = hlk_fm22x_ns.class_( + "FaceScanInvalidTrigger", automation.Trigger.template(cg.uint8) +) + +FaceInfoTrigger = hlk_fm22x_ns.class_( + "FaceInfoTrigger", + automation.Trigger.template( + cg.int16, cg.int16, cg.int16, cg.int16, cg.int16, cg.int16, cg.int16, cg.int16 + ), +) + +EnrollmentDoneTrigger = hlk_fm22x_ns.class_( + "EnrollmentDoneTrigger", automation.Trigger.template(cg.int16, cg.uint8) +) + +EnrollmentFailedTrigger = hlk_fm22x_ns.class_( + "EnrollmentFailedTrigger", automation.Trigger.template(cg.uint8) +) + +EnrollmentAction = hlk_fm22x_ns.class_("EnrollmentAction", automation.Action) +DeleteAction = hlk_fm22x_ns.class_("DeleteAction", automation.Action) +DeleteAllAction = hlk_fm22x_ns.class_("DeleteAllAction", automation.Action) +ScanAction = hlk_fm22x_ns.class_("ScanAction", automation.Action) +ResetAction = hlk_fm22x_ns.class_("ResetAction", automation.Action) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HlkFm22xComponent), + cv.Optional(CONF_ON_FACE_SCAN_MATCHED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + FaceScanMatchedTrigger + ), + } + ), + cv.Optional(CONF_ON_FACE_SCAN_UNMATCHED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + FaceScanUnmatchedTrigger + ), + } + ), + cv.Optional(CONF_ON_FACE_SCAN_INVALID): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + FaceScanInvalidTrigger + ), + } + ), + cv.Optional(CONF_ON_FACE_INFO): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FaceInfoTrigger), + } + ), + cv.Optional(CONF_ON_ENROLLMENT_DONE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + EnrollmentDoneTrigger + ), + } + ), + cv.Optional(CONF_ON_ENROLLMENT_FAILED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + EnrollmentFailedTrigger + ), + } + ), + } + ) + .extend(cv.polling_component_schema("50ms")) + .extend(uart.UART_DEVICE_SCHEMA), +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) + + for conf in config.get(CONF_ON_FACE_SCAN_MATCHED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, [(cg.int16, "face_id"), (cg.std_string, "name")], conf + ) + + for conf in config.get(CONF_ON_FACE_SCAN_UNMATCHED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + + for conf in config.get(CONF_ON_FACE_SCAN_INVALID, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(cg.uint8, "error")], conf) + + for conf in config.get(CONF_ON_FACE_INFO, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, + [ + (cg.int16, "status"), + (cg.int16, "left"), + (cg.int16, "top"), + (cg.int16, "right"), + (cg.int16, "bottom"), + (cg.int16, "yaw"), + (cg.int16, "pitch"), + (cg.int16, "roll"), + ], + conf, + ) + + for conf in config.get(CONF_ON_ENROLLMENT_DONE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, [(cg.int16, "face_id"), (cg.uint8, "direction")], conf + ) + + for conf in config.get(CONF_ON_ENROLLMENT_FAILED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(cg.uint8, "error")], conf) + + +@automation.register_action( + "hlk_fm22x.enroll", + EnrollmentAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(HlkFm22xComponent), + cv.Required(CONF_NAME): cv.templatable(cv.string), + cv.Required(CONF_DIRECTION): cv.templatable(cv.uint8_t), + }, + key=CONF_NAME, + ), +) +async def hlk_fm22x_enroll_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + + template_ = await cg.templatable(config[CONF_NAME], args, cg.std_string) + cg.add(var.set_name(template_)) + template_ = await cg.templatable(config[CONF_DIRECTION], args, cg.uint8) + cg.add(var.set_direction(template_)) + return var + + +@automation.register_action( + "hlk_fm22x.delete", + DeleteAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(HlkFm22xComponent), + cv.Required(CONF_FACE_ID): cv.templatable(cv.uint16_t), + }, + key=CONF_FACE_ID, + ), +) +async def hlk_fm22x_delete_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + + template_ = await cg.templatable(config[CONF_FACE_ID], args, cg.int16) + cg.add(var.set_face_id(template_)) + return var + + +@automation.register_action( + "hlk_fm22x.delete_all", + DeleteAllAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(HlkFm22xComponent), + } + ), +) +async def hlk_fm22x_delete_all_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var + + +@automation.register_action( + "hlk_fm22x.scan", + ScanAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(HlkFm22xComponent), + } + ), +) +async def hlk_fm22x_scan_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var + + +@automation.register_action( + "hlk_fm22x.reset", + ResetAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(HlkFm22xComponent), + } + ), +) +async def hlk_fm22x_reset_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var diff --git a/esphome/components/hlk_fm22x/binary_sensor.py b/esphome/components/hlk_fm22x/binary_sensor.py new file mode 100644 index 0000000000..3620f33ac0 --- /dev/null +++ b/esphome/components/hlk_fm22x/binary_sensor.py @@ -0,0 +1,21 @@ +import esphome.codegen as cg +from esphome.components import binary_sensor +import esphome.config_validation as cv +from esphome.const import CONF_ICON, ICON_KEY_PLUS + +from . import CONF_HLK_FM22X_ID, HlkFm22xComponent + +DEPENDENCIES = ["hlk_fm22x"] + +CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend( + { + cv.GenerateID(CONF_HLK_FM22X_ID): cv.use_id(HlkFm22xComponent), + cv.Optional(CONF_ICON, default=ICON_KEY_PLUS): cv.icon, + } +) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_HLK_FM22X_ID]) + var = await binary_sensor.new_binary_sensor(config) + cg.add(hub.set_enrolling_binary_sensor(var)) diff --git a/esphome/components/hlk_fm22x/hlk_fm22x.cpp b/esphome/components/hlk_fm22x/hlk_fm22x.cpp new file mode 100644 index 0000000000..ab15a2340d --- /dev/null +++ b/esphome/components/hlk_fm22x/hlk_fm22x.cpp @@ -0,0 +1,325 @@ +#include "hlk_fm22x.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" +#include +#include + +namespace esphome::hlk_fm22x { + +static const char *const TAG = "hlk_fm22x"; + +void HlkFm22xComponent::setup() { + ESP_LOGCONFIG(TAG, "Setting up HLK-FM22X..."); + this->set_enrolling_(false); + while (this->available()) { + this->read(); + } + this->defer([this]() { this->send_command_(HlkFm22xCommand::GET_STATUS); }); +} + +void HlkFm22xComponent::update() { + if (this->active_command_ != HlkFm22xCommand::NONE) { + if (this->wait_cycles_ > 600) { + ESP_LOGE(TAG, "Command 0x%.2X timed out", this->active_command_); + if (HlkFm22xCommand::RESET == this->active_command_) { + this->mark_failed(); + } else { + this->reset(); + } + } + } + this->recv_command_(); +} + +void HlkFm22xComponent::enroll_face(const std::string &name, HlkFm22xFaceDirection direction) { + if (name.length() > 31) { + ESP_LOGE(TAG, "enroll_face(): name too long '%s'", name.c_str()); + return; + } + ESP_LOGI(TAG, "Starting enrollment for %s", name.c_str()); + std::array data{}; + data[0] = 0; // admin + std::copy(name.begin(), name.end(), data.begin() + 1); + // Remaining bytes are already zero-initialized + data[33] = (uint8_t) direction; + data[34] = 10; // timeout + this->send_command_(HlkFm22xCommand::ENROLL, data.data(), data.size()); + this->set_enrolling_(true); +} + +void HlkFm22xComponent::scan_face() { + ESP_LOGI(TAG, "Verify face"); + static const uint8_t DATA[] = {0, 0}; + this->send_command_(HlkFm22xCommand::VERIFY, DATA, sizeof(DATA)); +} + +void HlkFm22xComponent::delete_face(int16_t face_id) { + ESP_LOGI(TAG, "Deleting face in slot %d", face_id); + const uint8_t data[] = {(uint8_t) (face_id >> 8), (uint8_t) (face_id & 0xFF)}; + this->send_command_(HlkFm22xCommand::DELETE_FACE, data, sizeof(data)); +} + +void HlkFm22xComponent::delete_all_faces() { + ESP_LOGI(TAG, "Deleting all stored faces"); + this->send_command_(HlkFm22xCommand::DELETE_ALL_FACES); +} + +void HlkFm22xComponent::get_face_count_() { + ESP_LOGD(TAG, "Getting face count"); + this->send_command_(HlkFm22xCommand::GET_ALL_FACE_IDS); +} + +void HlkFm22xComponent::reset() { + ESP_LOGI(TAG, "Resetting module"); + this->active_command_ = HlkFm22xCommand::NONE; + this->wait_cycles_ = 0; + this->set_enrolling_(false); + this->send_command_(HlkFm22xCommand::RESET); +} + +void HlkFm22xComponent::send_command_(HlkFm22xCommand command, const uint8_t *data, size_t size) { + ESP_LOGV(TAG, "Send command: 0x%.2X", command); + if (this->active_command_ != HlkFm22xCommand::NONE) { + ESP_LOGW(TAG, "Command 0x%.2X already active", this->active_command_); + return; + } + this->wait_cycles_ = 0; + this->active_command_ = command; + while (this->available()) + this->read(); + this->write((uint8_t) (START_CODE >> 8)); + this->write((uint8_t) (START_CODE & 0xFF)); + this->write((uint8_t) command); + uint16_t data_size = size; + this->write((uint8_t) (data_size >> 8)); + this->write((uint8_t) (data_size & 0xFF)); + + uint8_t checksum = 0; + checksum ^= (uint8_t) command; + checksum ^= (data_size >> 8); + checksum ^= (data_size & 0xFF); + for (size_t i = 0; i < size; i++) { + this->write(data[i]); + checksum ^= data[i]; + } + + this->write(checksum); + this->active_command_ = command; + this->wait_cycles_ = 0; +} + +void HlkFm22xComponent::recv_command_() { + uint8_t byte, checksum = 0; + uint16_t length = 0; + + if (this->available() < 7) { + ++this->wait_cycles_; + return; + } + this->wait_cycles_ = 0; + + if ((this->read() != (uint8_t) (START_CODE >> 8)) || (this->read() != (uint8_t) (START_CODE & 0xFF))) { + ESP_LOGE(TAG, "Invalid start code"); + return; + } + + byte = this->read(); + checksum ^= byte; + HlkFm22xResponseType response_type = (HlkFm22xResponseType) byte; + + byte = this->read(); + checksum ^= byte; + length = byte << 8; + byte = this->read(); + checksum ^= byte; + length |= byte; + + std::vector data; + data.reserve(length); + for (uint16_t idx = 0; idx < length; ++idx) { + byte = this->read(); + checksum ^= byte; + data.push_back(byte); + } + + ESP_LOGV(TAG, "Recv type: 0x%.2X, data: %s", response_type, format_hex_pretty(data).c_str()); + + byte = this->read(); + if (byte != checksum) { + ESP_LOGE(TAG, "Invalid checksum for data. Calculated: 0x%.2X, Received: 0x%.2X", checksum, byte); + return; + } + switch (response_type) { + case HlkFm22xResponseType::NOTE: + this->handle_note_(data); + break; + case HlkFm22xResponseType::REPLY: + this->handle_reply_(data); + break; + default: + ESP_LOGW(TAG, "Unexpected response type: 0x%.2X", response_type); + break; + } +} + +void HlkFm22xComponent::handle_note_(const std::vector &data) { + switch (data[0]) { + case HlkFm22xNoteType::FACE_STATE: + if (data.size() < 17) { + ESP_LOGE(TAG, "Invalid face note data size: %u", data.size()); + break; + } + { + int16_t info[8]; + uint8_t offset = 1; + for (int16_t &i : info) { + i = ((int16_t) data[offset + 1] << 8) | data[offset]; + offset += 2; + } + ESP_LOGV(TAG, "Face state: status: %d, left: %d, top: %d, right: %d, bottom: %d, yaw: %d, pitch: %d, roll: %d", + info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7]); + this->face_info_callback_.call(info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7]); + } + break; + case HlkFm22xNoteType::READY: + ESP_LOGE(TAG, "Command 0x%.2X timed out", this->active_command_); + switch (this->active_command_) { + case HlkFm22xCommand::ENROLL: + this->set_enrolling_(false); + this->enrollment_failed_callback_.call(HlkFm22xResult::FAILED4_TIMEOUT); + break; + case HlkFm22xCommand::VERIFY: + this->face_scan_invalid_callback_.call(HlkFm22xResult::FAILED4_TIMEOUT); + break; + default: + break; + } + this->active_command_ = HlkFm22xCommand::NONE; + this->wait_cycles_ = 0; + break; + default: + ESP_LOGW(TAG, "Unhandled note: 0x%.2X", data[0]); + break; + } +} + +void HlkFm22xComponent::handle_reply_(const std::vector &data) { + auto expected = this->active_command_; + this->active_command_ = HlkFm22xCommand::NONE; + if (data[0] != (uint8_t) expected) { + ESP_LOGE(TAG, "Unexpected response command. Expected: 0x%.2X, Received: 0x%.2X", expected, data[0]); + return; + } + + if (data[1] != HlkFm22xResult::SUCCESS) { + ESP_LOGE(TAG, "Command <0x%.2X> failed. Error: 0x%.2X", data[0], data[1]); + switch (expected) { + case HlkFm22xCommand::ENROLL: + this->set_enrolling_(false); + this->enrollment_failed_callback_.call(data[1]); + break; + case HlkFm22xCommand::VERIFY: + if (data[1] == HlkFm22xResult::REJECTED) { + this->face_scan_unmatched_callback_.call(); + } else { + this->face_scan_invalid_callback_.call(data[1]); + } + break; + default: + break; + } + return; + } + switch (expected) { + case HlkFm22xCommand::VERIFY: { + int16_t face_id = ((int16_t) data[2] << 8) | data[3]; + std::string name(data.begin() + 4, data.begin() + 36); + ESP_LOGD(TAG, "Face verified. ID: %d, name: %s", face_id, name.c_str()); + if (this->last_face_id_sensor_ != nullptr) { + this->last_face_id_sensor_->publish_state(face_id); + } + if (this->last_face_name_text_sensor_ != nullptr) { + this->last_face_name_text_sensor_->publish_state(name); + } + this->face_scan_matched_callback_.call(face_id, name); + break; + } + case HlkFm22xCommand::ENROLL: { + int16_t face_id = ((int16_t) data[2] << 8) | data[3]; + HlkFm22xFaceDirection direction = (HlkFm22xFaceDirection) data[4]; + ESP_LOGI(TAG, "Face enrolled. ID: %d, Direction: 0x%.2X", face_id, direction); + this->enrollment_done_callback_.call(face_id, (uint8_t) direction); + this->set_enrolling_(false); + this->defer([this]() { this->get_face_count_(); }); + break; + } + case HlkFm22xCommand::GET_STATUS: + if (this->status_sensor_ != nullptr) { + this->status_sensor_->publish_state(data[2]); + } + this->defer([this]() { this->send_command_(HlkFm22xCommand::GET_VERSION); }); + break; + case HlkFm22xCommand::GET_VERSION: + if (this->version_text_sensor_ != nullptr) { + std::string version(data.begin() + 2, data.end()); + this->version_text_sensor_->publish_state(version); + } + this->defer([this]() { this->get_face_count_(); }); + break; + case HlkFm22xCommand::GET_ALL_FACE_IDS: + if (this->face_count_sensor_ != nullptr) { + this->face_count_sensor_->publish_state(data[2]); + } + break; + case HlkFm22xCommand::DELETE_FACE: + ESP_LOGI(TAG, "Deleted face"); + break; + case HlkFm22xCommand::DELETE_ALL_FACES: + ESP_LOGI(TAG, "Deleted all faces"); + break; + case HlkFm22xCommand::RESET: + ESP_LOGI(TAG, "Module reset"); + this->defer([this]() { this->send_command_(HlkFm22xCommand::GET_STATUS); }); + break; + default: + ESP_LOGW(TAG, "Unhandled command: 0x%.2X", this->active_command_); + break; + } +} + +void HlkFm22xComponent::set_enrolling_(bool enrolling) { + if (this->enrolling_binary_sensor_ != nullptr) { + this->enrolling_binary_sensor_->publish_state(enrolling); + } +} + +void HlkFm22xComponent::dump_config() { + ESP_LOGCONFIG(TAG, "HLK_FM22X:"); + LOG_UPDATE_INTERVAL(this); + if (this->version_text_sensor_) { + LOG_TEXT_SENSOR(" ", "Version", this->version_text_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %s", this->version_text_sensor_->get_state().c_str()); + } + if (this->enrolling_binary_sensor_) { + LOG_BINARY_SENSOR(" ", "Enrolling", this->enrolling_binary_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %s", this->enrolling_binary_sensor_->state ? "ON" : "OFF"); + } + if (this->face_count_sensor_) { + LOG_SENSOR(" ", "Face Count", this->face_count_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %u", (uint16_t) this->face_count_sensor_->get_state()); + } + if (this->status_sensor_) { + LOG_SENSOR(" ", "Status", this->status_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %u", (uint8_t) this->status_sensor_->get_state()); + } + if (this->last_face_id_sensor_) { + LOG_SENSOR(" ", "Last Face ID", this->last_face_id_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %u", (int16_t) this->last_face_id_sensor_->get_state()); + } + if (this->last_face_name_text_sensor_) { + LOG_TEXT_SENSOR(" ", "Last Face Name", this->last_face_name_text_sensor_); + ESP_LOGCONFIG(TAG, " Current Value: %s", this->last_face_name_text_sensor_->get_state().c_str()); + } +} + +} // namespace esphome::hlk_fm22x diff --git a/esphome/components/hlk_fm22x/hlk_fm22x.h b/esphome/components/hlk_fm22x/hlk_fm22x.h new file mode 100644 index 0000000000..5ecc715ea1 --- /dev/null +++ b/esphome/components/hlk_fm22x/hlk_fm22x.h @@ -0,0 +1,224 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/text_sensor/text_sensor.h" +#include "esphome/components/uart/uart.h" + +#include +#include + +namespace esphome::hlk_fm22x { + +static const uint16_t START_CODE = 0xEFAA; +enum HlkFm22xCommand { + NONE = 0x00, + RESET = 0x10, + GET_STATUS = 0x11, + VERIFY = 0x12, + ENROLL = 0x13, + DELETE_FACE = 0x20, + DELETE_ALL_FACES = 0x21, + GET_ALL_FACE_IDS = 0x24, + GET_VERSION = 0x30, + GET_SERIAL_NUMBER = 0x93, +}; + +enum HlkFm22xResponseType { + REPLY = 0x00, + NOTE = 0x01, + IMAGE = 0x02, +}; + +enum HlkFm22xNoteType { + READY = 0x00, + FACE_STATE = 0x01, +}; + +enum HlkFm22xResult { + SUCCESS = 0x00, + REJECTED = 0x01, + ABORTED = 0x02, + FAILED4_CAMERA = 0x04, + FAILED4_UNKNOWNREASON = 0x05, + FAILED4_INVALIDPARAM = 0x06, + FAILED4_NOMEMORY = 0x07, + FAILED4_UNKNOWNUSER = 0x08, + FAILED4_MAXUSER = 0x09, + FAILED4_FACEENROLLED = 0x0A, + FAILED4_LIVENESSCHECK = 0x0C, + FAILED4_TIMEOUT = 0x0D, + FAILED4_AUTHORIZATION = 0x0E, + FAILED4_READ_FILE = 0x13, + FAILED4_WRITE_FILE = 0x14, + FAILED4_NO_ENCRYPT = 0x15, + FAILED4_NO_RGBIMAGE = 0x17, + FAILED4_JPGPHOTO_LARGE = 0x18, + FAILED4_JPGPHOTO_SMALL = 0x19, +}; + +enum HlkFm22xFaceDirection { + FACE_DIRECTION_UNDEFINED = 0x00, + FACE_DIRECTION_MIDDLE = 0x01, + FACE_DIRECTION_RIGHT = 0x02, + FACE_DIRECTION_LEFT = 0x04, + FACE_DIRECTION_DOWN = 0x08, + FACE_DIRECTION_UP = 0x10, +}; + +class HlkFm22xComponent : public PollingComponent, public uart::UARTDevice { + public: + void setup() override; + void update() override; + void dump_config() override; + + void set_face_count_sensor(sensor::Sensor *face_count_sensor) { this->face_count_sensor_ = face_count_sensor; } + void set_status_sensor(sensor::Sensor *status_sensor) { this->status_sensor_ = status_sensor; } + void set_last_face_id_sensor(sensor::Sensor *last_face_id_sensor) { + this->last_face_id_sensor_ = last_face_id_sensor; + } + void set_last_face_name_text_sensor(text_sensor::TextSensor *last_face_name_text_sensor) { + this->last_face_name_text_sensor_ = last_face_name_text_sensor; + } + void set_enrolling_binary_sensor(binary_sensor::BinarySensor *enrolling_binary_sensor) { + this->enrolling_binary_sensor_ = enrolling_binary_sensor; + } + void set_version_text_sensor(text_sensor::TextSensor *version_text_sensor) { + this->version_text_sensor_ = version_text_sensor; + } + void add_on_face_scan_matched_callback(std::function callback) { + this->face_scan_matched_callback_.add(std::move(callback)); + } + void add_on_face_scan_unmatched_callback(std::function callback) { + this->face_scan_unmatched_callback_.add(std::move(callback)); + } + void add_on_face_scan_invalid_callback(std::function callback) { + this->face_scan_invalid_callback_.add(std::move(callback)); + } + void add_on_face_info_callback( + std::function callback) { + this->face_info_callback_.add(std::move(callback)); + } + void add_on_enrollment_done_callback(std::function callback) { + this->enrollment_done_callback_.add(std::move(callback)); + } + void add_on_enrollment_failed_callback(std::function callback) { + this->enrollment_failed_callback_.add(std::move(callback)); + } + + void enroll_face(const std::string &name, HlkFm22xFaceDirection direction); + void scan_face(); + void delete_face(int16_t face_id); + void delete_all_faces(); + void reset(); + + protected: + void get_face_count_(); + void send_command_(HlkFm22xCommand command, const uint8_t *data = nullptr, size_t size = 0); + void recv_command_(); + void handle_note_(const std::vector &data); + void handle_reply_(const std::vector &data); + void set_enrolling_(bool enrolling); + + HlkFm22xCommand active_command_ = HlkFm22xCommand::NONE; + uint16_t wait_cycles_ = 0; + sensor::Sensor *face_count_sensor_{nullptr}; + sensor::Sensor *status_sensor_{nullptr}; + sensor::Sensor *last_face_id_sensor_{nullptr}; + binary_sensor::BinarySensor *enrolling_binary_sensor_{nullptr}; + text_sensor::TextSensor *last_face_name_text_sensor_{nullptr}; + text_sensor::TextSensor *version_text_sensor_{nullptr}; + CallbackManager face_scan_invalid_callback_; + CallbackManager face_scan_matched_callback_; + CallbackManager face_scan_unmatched_callback_; + CallbackManager face_info_callback_; + CallbackManager enrollment_done_callback_; + CallbackManager enrollment_failed_callback_; +}; + +class FaceScanMatchedTrigger : public Trigger { + public: + explicit FaceScanMatchedTrigger(HlkFm22xComponent *parent) { + parent->add_on_face_scan_matched_callback( + [this](int16_t face_id, const std::string &name) { this->trigger(face_id, name); }); + } +}; + +class FaceScanUnmatchedTrigger : public Trigger<> { + public: + explicit FaceScanUnmatchedTrigger(HlkFm22xComponent *parent) { + parent->add_on_face_scan_unmatched_callback([this]() { this->trigger(); }); + } +}; + +class FaceScanInvalidTrigger : public Trigger { + public: + explicit FaceScanInvalidTrigger(HlkFm22xComponent *parent) { + parent->add_on_face_scan_invalid_callback([this](uint8_t error) { this->trigger(error); }); + } +}; + +class FaceInfoTrigger : public Trigger { + public: + explicit FaceInfoTrigger(HlkFm22xComponent *parent) { + parent->add_on_face_info_callback( + [this](int16_t status, int16_t left, int16_t top, int16_t right, int16_t bottom, int16_t yaw, int16_t pitch, + int16_t roll) { this->trigger(status, left, top, right, bottom, yaw, pitch, roll); }); + } +}; + +class EnrollmentDoneTrigger : public Trigger { + public: + explicit EnrollmentDoneTrigger(HlkFm22xComponent *parent) { + parent->add_on_enrollment_done_callback( + [this](int16_t face_id, uint8_t direction) { this->trigger(face_id, direction); }); + } +}; + +class EnrollmentFailedTrigger : public Trigger { + public: + explicit EnrollmentFailedTrigger(HlkFm22xComponent *parent) { + parent->add_on_enrollment_failed_callback([this](uint8_t error) { this->trigger(error); }); + } +}; + +template class EnrollmentAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(std::string, name) + TEMPLATABLE_VALUE(uint8_t, direction) + + void play(Ts... x) override { + auto name = this->name_.value(x...); + auto direction = (HlkFm22xFaceDirection) this->direction_.value(x...); + this->parent_->enroll_face(name, direction); + } +}; + +template class DeleteAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(int16_t, face_id) + + void play(Ts... x) override { + auto face_id = this->face_id_.value(x...); + this->parent_->delete_face(face_id); + } +}; + +template class DeleteAllAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->delete_all_faces(); } +}; + +template class ScanAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->scan_face(); } +}; + +template class ResetAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->reset(); } +}; + +} // namespace esphome::hlk_fm22x diff --git a/esphome/components/hlk_fm22x/sensor.py b/esphome/components/hlk_fm22x/sensor.py new file mode 100644 index 0000000000..e14b45599f --- /dev/null +++ b/esphome/components/hlk_fm22x/sensor.py @@ -0,0 +1,47 @@ +import esphome.codegen as cg +from esphome.components import sensor +import esphome.config_validation as cv +from esphome.const import CONF_STATUS, ENTITY_CATEGORY_DIAGNOSTIC, ICON_ACCOUNT + +from . import CONF_HLK_FM22X_ID, HlkFm22xComponent + +DEPENDENCIES = ["hlk_fm22x"] + +CONF_FACE_COUNT = "face_count" +CONF_LAST_FACE_ID = "last_face_id" +ICON_FACE = "mdi:face-recognition" + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_HLK_FM22X_ID): cv.use_id(HlkFm22xComponent), + cv.Optional(CONF_FACE_COUNT): sensor.sensor_schema( + icon=ICON_FACE, + accuracy_decimals=0, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_STATUS): sensor.sensor_schema( + accuracy_decimals=0, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_LAST_FACE_ID): sensor.sensor_schema( + icon=ICON_ACCOUNT, + accuracy_decimals=0, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } +) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_HLK_FM22X_ID]) + + for key in [ + CONF_FACE_COUNT, + CONF_STATUS, + CONF_LAST_FACE_ID, + ]: + if key not in config: + continue + conf = config[key] + sens = await sensor.new_sensor(conf) + cg.add(getattr(hub, f"set_{key}_sensor")(sens)) diff --git a/esphome/components/hlk_fm22x/text_sensor.py b/esphome/components/hlk_fm22x/text_sensor.py new file mode 100644 index 0000000000..06da61c8b3 --- /dev/null +++ b/esphome/components/hlk_fm22x/text_sensor.py @@ -0,0 +1,42 @@ +import esphome.codegen as cg +from esphome.components import text_sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_VERSION, + ENTITY_CATEGORY_DIAGNOSTIC, + ICON_ACCOUNT, + ICON_RESTART, +) + +from . import CONF_HLK_FM22X_ID, HlkFm22xComponent + +DEPENDENCIES = ["hlk_fm22x"] + +CONF_LAST_FACE_NAME = "last_face_name" + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_HLK_FM22X_ID): cv.use_id(HlkFm22xComponent), + cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema( + icon=ICON_RESTART, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_LAST_FACE_NAME): text_sensor.text_sensor_schema( + icon=ICON_ACCOUNT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } +) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_HLK_FM22X_ID]) + for key in [ + CONF_VERSION, + CONF_LAST_FACE_NAME, + ]: + if key not in config: + continue + conf = config[key] + sens = await text_sensor.new_text_sensor(conf) + cg.add(getattr(hub, f"set_{key}_text_sensor")(sens)) diff --git a/tests/components/hlk_fm22x/test.esp32-idf.yaml b/tests/components/hlk_fm22x/test.esp32-idf.yaml new file mode 100644 index 0000000000..5e7cbde664 --- /dev/null +++ b/tests/components/hlk_fm22x/test.esp32-idf.yaml @@ -0,0 +1,47 @@ +esphome: + on_boot: + then: + - hlk_fm22x.enroll: + name: "Test" + direction: 1 + - hlk_fm22x.delete_all: + +uart: + - id: uart_hlk_fm22x + tx_pin: 17 + rx_pin: 16 + baud_rate: 115200 + +hlk_fm22x: + on_face_scan_matched: + - logger.log: test_hlk_22x_face_scan_matched + on_face_scan_unmatched: + - logger.log: test_hlk_22x_face_scan_unmatched + on_face_scan_invalid: + - logger.log: test_hlk_22x_face_scan_invalid + on_face_info: + - logger.log: test_hlk_22x_face_info + on_enrollment_done: + - logger.log: test_hlk_22x_enrollment_done + on_enrollment_failed: + - logger.log: test_hlk_22x_enrollment_failed + +sensor: + - platform: hlk_fm22x + face_count: + name: "Face Count" + last_face_id: + name: "Last Face ID" + status: + name: "Face Status" + +binary_sensor: + - platform: hlk_fm22x + name: "Face Enrolling" + +text_sensor: + - platform: hlk_fm22x + version: + name: "HLK Version" + last_face_name: + name: "Last Face Name" diff --git a/tests/components/hlk_fm22x/test.esp8266-ard.yaml b/tests/components/hlk_fm22x/test.esp8266-ard.yaml new file mode 100644 index 0000000000..680047834c --- /dev/null +++ b/tests/components/hlk_fm22x/test.esp8266-ard.yaml @@ -0,0 +1,47 @@ +esphome: + on_boot: + then: + - hlk_fm22x.enroll: + name: "Test" + direction: 1 + - hlk_fm22x.delete_all: + +uart: + - id: uart_hlk_fm22x + tx_pin: 4 + rx_pin: 5 + baud_rate: 115200 + +hlk_fm22x: + on_face_scan_matched: + - logger.log: test_hlk_22x_face_scan_matched + on_face_scan_unmatched: + - logger.log: test_hlk_22x_face_scan_unmatched + on_face_scan_invalid: + - logger.log: test_hlk_22x_face_scan_invalid + on_face_info: + - logger.log: test_hlk_22x_face_info + on_enrollment_done: + - logger.log: test_hlk_22x_enrollment_done + on_enrollment_failed: + - logger.log: test_hlk_22x_enrollment_failed + +sensor: + - platform: hlk_fm22x + face_count: + name: "Face Count" + last_face_id: + name: "Last Face ID" + status: + name: "Face Status" + +binary_sensor: + - platform: hlk_fm22x + name: "Face Enrolling" + +text_sensor: + - platform: hlk_fm22x + version: + name: "HLK Version" + last_face_name: + name: "Last Face Name" diff --git a/tests/components/hlk_fm22x/test.rp2040-ard.yaml b/tests/components/hlk_fm22x/test.rp2040-ard.yaml new file mode 100644 index 0000000000..680047834c --- /dev/null +++ b/tests/components/hlk_fm22x/test.rp2040-ard.yaml @@ -0,0 +1,47 @@ +esphome: + on_boot: + then: + - hlk_fm22x.enroll: + name: "Test" + direction: 1 + - hlk_fm22x.delete_all: + +uart: + - id: uart_hlk_fm22x + tx_pin: 4 + rx_pin: 5 + baud_rate: 115200 + +hlk_fm22x: + on_face_scan_matched: + - logger.log: test_hlk_22x_face_scan_matched + on_face_scan_unmatched: + - logger.log: test_hlk_22x_face_scan_unmatched + on_face_scan_invalid: + - logger.log: test_hlk_22x_face_scan_invalid + on_face_info: + - logger.log: test_hlk_22x_face_info + on_enrollment_done: + - logger.log: test_hlk_22x_enrollment_done + on_enrollment_failed: + - logger.log: test_hlk_22x_enrollment_failed + +sensor: + - platform: hlk_fm22x + face_count: + name: "Face Count" + last_face_id: + name: "Last Face ID" + status: + name: "Face Status" + +binary_sensor: + - platform: hlk_fm22x + name: "Face Enrolling" + +text_sensor: + - platform: hlk_fm22x + version: + name: "HLK Version" + last_face_name: + name: "Last Face Name" From f32b69b8f15bb0e4208854c75f8283c51b515ac1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 10 Nov 2025 10:00:42 -0600 Subject: [PATCH 457/526] [tests] Add unit test coverage for web_port property (#11811) --- tests/unit_tests/test_core.py | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index 92b60efd93..e52cb24831 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -670,3 +670,51 @@ class TestEsphomeCore: os.environ.pop("ESPHOME_IS_HA_ADDON", None) os.environ.pop("ESPHOME_DATA_DIR", None) assert target.data_dir == Path(expected_default) + + def test_web_port__none(self, target): + """Test web_port returns None when web_server is not configured.""" + target.config = {} + assert target.web_port is None + + def test_web_port__explicit_web_server_default_port(self, target): + """Test web_port returns 80 when web_server is explicitly configured without port.""" + target.config = {const.CONF_WEB_SERVER: {}} + assert target.web_port == 80 + + def test_web_port__explicit_web_server_custom_port(self, target): + """Test web_port returns custom port when web_server is configured with port.""" + target.config = {const.CONF_WEB_SERVER: {const.CONF_PORT: 8080}} + assert target.web_port == 8080 + + def test_web_port__ota_web_server_platform_only(self, target): + """ + Test web_port returns None when ota.web_server platform is explicitly configured. + + This is a critical test for Dashboard Issue #766: + https://github.com/esphome/dashboard/issues/766 + + When ota: platform: web_server is explicitly configured (or auto-loaded by captive_portal): + - "web_server" appears in loaded_integrations (platform name added to integrations) + - "ota/web_server" appears in loaded_platforms + - But CONF_WEB_SERVER is NOT in config (only the platform is loaded, not the component) + - web_port MUST return None (no web UI available) + - Dashboard should NOT show VISIT button + + This test ensures web_port only checks CONF_WEB_SERVER in config, not loaded_integrations. + """ + # Simulate config with ota.web_server platform but no web_server component + # This happens when: + # 1. User explicitly configures: ota: - platform: web_server + # 2. OR captive_portal auto-loads ota.web_server + target.config = { + const.CONF_OTA: [ + { + "platform": "web_server", + # OTA web_server platform config would be here + } + ], + # Note: CONF_WEB_SERVER is NOT in config - only the OTA platform + } + # Even though "web_server" is in loaded_integrations due to the platform, + # web_port must return None because the full web_server component is not configured + assert target.web_port is None From 43eafbccb3f6a397e2107a005b37b8098f1880b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:28:14 -0600 Subject: [PATCH 458/526] Bump pytest-asyncio from 1.2.0 to 1.3.0 (#11815) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 81cb711eec..f845c47fc0 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -8,7 +8,7 @@ pre-commit pytest==8.4.2 pytest-cov==7.0.0 pytest-mock==3.15.1 -pytest-asyncio==1.2.0 +pytest-asyncio==1.3.0 pytest-xdist==3.8.0 asyncmock==0.4.2 hypothesis==6.92.1 From 8c5b9647223f0fe5a83b0820c98c0c05aa89252d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:28:25 -0600 Subject: [PATCH 459/526] Bump pyupgrade from 3.21.0 to 3.21.1 (#11816) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index f845c47fc0..169037753b 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,7 +1,7 @@ pylint==4.0.2 flake8==7.3.0 # also change in .pre-commit-config.yaml when updating ruff==0.14.4 # also change in .pre-commit-config.yaml when updating -pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating +pyupgrade==3.21.1 # also change in .pre-commit-config.yaml when updating pre-commit # Unit tests From e46300828e661b2b88bd3a568a7d46b0092c5c31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:45:56 -0600 Subject: [PATCH 460/526] Bump pytest from 8.4.2 to 9.0.0 (#11817) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 169037753b..35010ad52f 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -5,7 +5,7 @@ pyupgrade==3.21.1 # also change in .pre-commit-config.yaml when updating pre-commit # Unit tests -pytest==8.4.2 +pytest==9.0.0 pytest-cov==7.0.0 pytest-mock==3.15.1 pytest-asyncio==1.3.0 From 40e2976ba2f46a58bb0fe220ce0c312d74a6586e Mon Sep 17 00:00:00 2001 From: Thomas Rupprecht Date: Tue, 11 Nov 2025 00:33:34 +0100 Subject: [PATCH 461/526] [ai] simplify namespace syntax (#11824) --- .ai/instructions.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.ai/instructions.md b/.ai/instructions.md index 8d81c6cf0f..681829bae6 100644 --- a/.ai/instructions.md +++ b/.ai/instructions.md @@ -172,8 +172,7 @@ This document provides essential context for AI models interacting with this pro * **C++ Class Pattern:** ```cpp - namespace esphome { - namespace my_component { + namespace esphome::my_component { class MyComponent : public Component { public: @@ -189,8 +188,7 @@ This document provides essential context for AI models interacting with this pro int param_{0}; }; - } // namespace my_component - } // namespace esphome + } // namespace esphome::my_component ``` * **Common Component Examples:** From 0f8332fe3cd0680e9c98a04803f6b5884fe5ae47 Mon Sep 17 00:00:00 2001 From: Stuart Parmenter Date: Mon, 10 Nov 2025 16:04:03 -0800 Subject: [PATCH 462/526] [lvgl] Automatically register widget types (#11394) Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/lvgl/__init__.py | 103 ++++++-------------- esphome/components/lvgl/schemas.py | 4 + esphome/components/lvgl/types.py | 23 ++++- esphome/components/lvgl/widgets/__init__.py | 7 +- esphome/components/lvgl/widgets/spinbox.py | 16 +-- tests/components/lvgl/lvgl-package.yaml | 4 + 6 files changed, 64 insertions(+), 93 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 861999d0b7..4df68a6386 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -1,6 +1,8 @@ +import importlib import logging +import pkgutil -from esphome.automation import build_automation, register_action, validate_automation +from esphome.automation import build_automation, validate_automation import esphome.codegen as cg from esphome.components.const import CONF_COLOR_DEPTH, CONF_DRAW_ROUNDING from esphome.components.display import Display @@ -25,8 +27,8 @@ from esphome.cpp_generator import MockObj from esphome.final_validate import full_config from esphome.helpers import write_file_if_changed -from . import defines as df, helpers, lv_validation as lvalid -from .automation import disp_update, focused_widgets, refreshed_widgets, update_to_code +from . import defines as df, helpers, lv_validation as lvalid, widgets +from .automation import disp_update, focused_widgets, refreshed_widgets from .defines import add_define from .encoders import ( ENCODERS_CONFIG, @@ -45,7 +47,6 @@ from .schemas import ( WIDGET_TYPES, any_widget_schema, container_schema, - create_modify_schema, obj_schema, ) from .styles import add_top_layer, styles_to_code, theme_to_code @@ -54,7 +55,6 @@ from .trigger import add_on_boot_triggers, generate_triggers from .types import ( FontEngine, IdleTrigger, - ObjUpdateAction, PlainTrigger, lv_font_t, lv_group_t, @@ -69,33 +69,23 @@ from .widgets import ( set_obj_properties, styles_used, ) -from .widgets.animimg import animimg_spec -from .widgets.arc import arc_spec -from .widgets.button import button_spec -from .widgets.buttonmatrix import buttonmatrix_spec -from .widgets.canvas import canvas_spec -from .widgets.checkbox import checkbox_spec -from .widgets.container import container_spec -from .widgets.dropdown import dropdown_spec -from .widgets.img import img_spec -from .widgets.keyboard import keyboard_spec -from .widgets.label import label_spec -from .widgets.led import led_spec -from .widgets.line import line_spec -from .widgets.lv_bar import bar_spec -from .widgets.meter import meter_spec + +# Import only what we actually use directly in this file from .widgets.msgbox import MSGBOX_SCHEMA, msgboxes_to_code -from .widgets.obj import obj_spec -from .widgets.page import add_pages, generate_page_triggers, page_spec -from .widgets.qrcode import qr_code_spec -from .widgets.roller import roller_spec -from .widgets.slider import slider_spec -from .widgets.spinbox import spinbox_spec -from .widgets.spinner import spinner_spec -from .widgets.switch import switch_spec -from .widgets.tabview import tabview_spec -from .widgets.textarea import textarea_spec -from .widgets.tileview import tileview_spec +from .widgets.obj import obj_spec # Used in LVGL_SCHEMA +from .widgets.page import ( # page_spec used in LVGL_SCHEMA + add_pages, + generate_page_triggers, + page_spec, +) + +# Widget registration happens via WidgetType.__init__ in individual widget files +# The imports below trigger creation of the widget types +# Action registration (lvgl.{widget}.update) happens automatically +# in the WidgetType.__init__ method + +for module_info in pkgutil.iter_modules(widgets.__path__): + importlib.import_module(f".widgets.{module_info.name}", package=__package__) DOMAIN = "lvgl" DEPENDENCIES = ["display"] @@ -103,41 +93,6 @@ AUTO_LOAD = ["key_provider"] CODEOWNERS = ["@clydebarrow"] LOGGER = logging.getLogger(__name__) -for w_type in ( - label_spec, - obj_spec, - button_spec, - bar_spec, - slider_spec, - arc_spec, - line_spec, - spinner_spec, - led_spec, - animimg_spec, - checkbox_spec, - img_spec, - switch_spec, - tabview_spec, - buttonmatrix_spec, - meter_spec, - dropdown_spec, - roller_spec, - textarea_spec, - spinbox_spec, - keyboard_spec, - tileview_spec, - qr_code_spec, - canvas_spec, - container_spec, -): - WIDGET_TYPES[w_type.name] = w_type - -for w_type in WIDGET_TYPES.values(): - register_action( - f"lvgl.{w_type.name}.update", - ObjUpdateAction, - create_modify_schema(w_type), - )(update_to_code) SIMPLE_TRIGGERS = ( df.CONF_ON_PAUSE, @@ -402,6 +357,15 @@ def add_hello_world(config): return config +def _theme_schema(value): + return cv.Schema( + { + cv.Optional(name): obj_schema(w).extend(FULL_STYLE_SCHEMA) + for name, w in WIDGET_TYPES.items() + } + )(value) + + FINAL_VALIDATE_SCHEMA = final_validation LVGL_SCHEMA = cv.All( @@ -454,12 +418,7 @@ LVGL_SCHEMA = cv.All( cv.Optional( df.CONF_TRANSPARENCY_KEY, default=0x000400 ): lvalid.lv_color, - cv.Optional(df.CONF_THEME): cv.Schema( - { - cv.Optional(name): obj_schema(w).extend(FULL_STYLE_SCHEMA) - for name, w in WIDGET_TYPES.items() - } - ), + cv.Optional(df.CONF_THEME): _theme_schema, cv.Optional(df.CONF_GRADIENTS): GRADIENT_SCHEMA, cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema, cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG, diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 0dcf420f24..6b77f66abb 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -411,6 +411,10 @@ def any_widget_schema(extras=None): Dynamically generate schemas for all possible LVGL widgets. This is what implements the ability to have a list of any kind of widget under the widgets: key. + This uses lazy evaluation - the schema is built when called during validation, + not at import time. This allows external components to register widgets + before schema validation begins. + :param extras: Additional schema to be applied to each generated one :return: A validator for the Widgets key """ diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index 8c33e13934..035320b6ac 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -1,8 +1,10 @@ import sys from esphome import automation, codegen as cg +from esphome.automation import register_action from esphome.config_validation import Schema from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_TEXT, CONF_VALUE +from esphome.core import EsphomeError from esphome.cpp_generator import MockObj, MockObjClass from esphome.cpp_types import esphome_ns @@ -124,13 +126,16 @@ class WidgetType: schema=None, modify_schema=None, lv_name=None, + is_mock: bool = False, ): """ :param name: The widget name, e.g. "bar" :param w_type: The C type of the widget :param parts: What parts this widget supports :param schema: The config schema for defining a widget - :param modify_schema: A schema to update the widget + :param modify_schema: A schema to update the widget, defaults to the same as the schema + :param lv_name: The name of the LVGL widget in the LVGL library, if different from the name + :param is_mock: Whether this widget is a mock widget, i.e. not a real LVGL widget """ self.name = name self.lv_name = lv_name or name @@ -146,6 +151,22 @@ class WidgetType: self.modify_schema = modify_schema self.mock_obj = MockObj(f"lv_{self.lv_name}", "_") + # Local import to avoid circular import + from .automation import update_to_code + from .schemas import WIDGET_TYPES, create_modify_schema + + if not is_mock: + if self.name in WIDGET_TYPES: + raise EsphomeError(f"Duplicate definition of widget type '{self.name}'") + WIDGET_TYPES[self.name] = self + + # Register the update action automatically + register_action( + f"lvgl.{self.name}.update", + ObjUpdateAction, + create_modify_schema(self), + )(update_to_code) + @property def animated(self): return False diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 7d9f9cb7de..187b5828c2 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -213,17 +213,14 @@ class LvScrActType(WidgetType): """ def __init__(self): - super().__init__("lv_scr_act()", lv_obj_t, ()) + super().__init__("lv_scr_act()", lv_obj_t, (), is_mock=True) async def to_code(self, w, config: dict): return [] -lv_scr_act_spec = LvScrActType() - - def get_scr_act(lv_comp: MockObj) -> Widget: - return Widget.create(None, lv_comp.get_scr_act(), lv_scr_act_spec, {}) + return Widget.create(None, lv_comp.get_scr_act(), LvScrActType(), {}) def get_widget_generator(wid): diff --git a/esphome/components/lvgl/widgets/spinbox.py b/esphome/components/lvgl/widgets/spinbox.py index 26ad149c6f..ac23ded723 100644 --- a/esphome/components/lvgl/widgets/spinbox.py +++ b/esphome/components/lvgl/widgets/spinbox.py @@ -2,7 +2,7 @@ from esphome import automation import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_RANGE_FROM, CONF_RANGE_TO, CONF_STEP, CONF_VALUE -from ..automation import action_to_code, update_to_code +from ..automation import action_to_code from ..defines import ( CONF_CURSOR, CONF_DECIMAL_PLACES, @@ -171,17 +171,3 @@ async def spinbox_decrement(config, action_id, template_arg, args): lv.spinbox_decrement(w.obj) return await action_to_code(widgets, do_increment, action_id, template_arg, args) - - -@automation.register_action( - "lvgl.spinbox.update", - ObjUpdateAction, - cv.Schema( - { - cv.Required(CONF_ID): cv.use_id(lv_spinbox_t), - cv.Required(CONF_VALUE): lv_float, - } - ), -) -async def spinbox_update_to_code(config, action_id, template_arg, args): - return await update_to_code(config, action_id, template_arg, args) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index b122d10f04..d7c342b16e 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -700,6 +700,10 @@ lvgl: width: 100% height: 10% align: top_mid + on_value: + - lvgl.spinbox.update: + id: spinbox_id + value: !lambda return x; - button: styles: spin_button id: spin_up From 855aa32f542d5296a7febf9b388819c20a19ecf9 Mon Sep 17 00:00:00 2001 From: Beormund <75735592+Beormund@users.noreply.github.com> Date: Tue, 11 Nov 2025 00:32:59 +0000 Subject: [PATCH 463/526] Add support for RX8130 RTC Chip (#10511) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/rx8130/__init__.py | 0 esphome/components/rx8130/rx8130.cpp | 127 ++++++++++++++++++ esphome/components/rx8130/rx8130.h | 35 +++++ esphome/components/rx8130/time.py | 56 ++++++++ tests/components/rx8130/common.yaml | 8 ++ tests/components/rx8130/test.esp32-idf.yaml | 4 + tests/components/rx8130/test.esp8266-ard.yaml | 4 + tests/components/rx8130/test.rp2040-ard.yaml | 4 + 9 files changed, 239 insertions(+) create mode 100644 esphome/components/rx8130/__init__.py create mode 100644 esphome/components/rx8130/rx8130.cpp create mode 100644 esphome/components/rx8130/rx8130.h create mode 100644 esphome/components/rx8130/time.py create mode 100644 tests/components/rx8130/common.yaml create mode 100644 tests/components/rx8130/test.esp32-idf.yaml create mode 100644 tests/components/rx8130/test.esp8266-ard.yaml create mode 100644 tests/components/rx8130/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 393774372f..e6970af47c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -396,6 +396,7 @@ esphome/components/rpi_dpi_rgb/* @clydebarrow esphome/components/rtl87xx/* @kuba2k2 esphome/components/rtttl/* @glmnet esphome/components/runtime_stats/* @bdraco +esphome/components/rx8130/* @beormund esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti esphome/components/scd4x/* @martgras @sjtrny esphome/components/script/* @esphome/core diff --git a/esphome/components/rx8130/__init__.py b/esphome/components/rx8130/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/rx8130/rx8130.cpp b/esphome/components/rx8130/rx8130.cpp new file mode 100644 index 0000000000..cf6ea3e6e6 --- /dev/null +++ b/esphome/components/rx8130/rx8130.cpp @@ -0,0 +1,127 @@ +#include "rx8130.h" +#include "esphome/core/log.h" + +// https://download.epsondevice.com/td/pdf/app/RX8130CE_en.pdf + +namespace esphome { +namespace rx8130 { + +static const uint8_t RX8130_REG_SEC = 0x10; +static const uint8_t RX8130_REG_MIN = 0x11; +static const uint8_t RX8130_REG_HOUR = 0x12; +static const uint8_t RX8130_REG_WDAY = 0x13; +static const uint8_t RX8130_REG_MDAY = 0x14; +static const uint8_t RX8130_REG_MONTH = 0x15; +static const uint8_t RX8130_REG_YEAR = 0x16; +static const uint8_t RX8130_REG_EXTEN = 0x1C; +static const uint8_t RX8130_REG_FLAG = 0x1D; +static const uint8_t RX8130_REG_CTRL0 = 0x1E; +static const uint8_t RX8130_REG_CTRL1 = 0x1F; +static const uint8_t RX8130_REG_DIG_OFFSET = 0x30; +static const uint8_t RX8130_BIT_CTRL_STOP = 0x40; +static const uint8_t RX8130_BAT_FLAGS = 0x30; +static const uint8_t RX8130_CLEAR_FLAGS = 0x00; + +static const char *const TAG = "rx8130"; + +constexpr uint8_t bcd2dec(uint8_t val) { return (val >> 4) * 10 + (val & 0x0f); } +constexpr uint8_t dec2bcd(uint8_t val) { return ((val / 10) << 4) + (val % 10); } + +void RX8130Component::setup() { + // Set digital offset to disabled with no offset + if (this->write_register(RX8130_REG_DIG_OFFSET, &RX8130_CLEAR_FLAGS, 1) != i2c::ERROR_OK) { + this->mark_failed(); + return; + } + // Disable wakeup timers + if (this->write_register(RX8130_REG_EXTEN, &RX8130_CLEAR_FLAGS, 1) != i2c::ERROR_OK) { + this->mark_failed(); + return; + } + // Clear VLF flag in case there has been data loss + if (this->write_register(RX8130_REG_FLAG, &RX8130_CLEAR_FLAGS, 1) != i2c::ERROR_OK) { + this->mark_failed(); + return; + } + // Clear test flag and disable interrupts + if (this->write_register(RX8130_REG_CTRL0, &RX8130_CLEAR_FLAGS, 1) != i2c::ERROR_OK) { + this->mark_failed(); + return; + } + // Enable battery charging and switching + if (this->write_register(RX8130_REG_CTRL1, &RX8130_BAT_FLAGS, 1) != i2c::ERROR_OK) { + this->mark_failed(); + return; + } + // Clear STOP bit + this->stop_(false); +} + +void RX8130Component::update() { this->read_time(); } + +void RX8130Component::dump_config() { + ESP_LOGCONFIG(TAG, "RX8130:"); + LOG_I2C_DEVICE(this); +} + +void RX8130Component::read_time() { + uint8_t date[7]; + if (this->read_register(RX8130_REG_SEC, date, 7) != i2c::ERROR_OK) { + this->status_set_warning(ESP_LOG_MSG_COMM_FAIL); + return; + } + ESPTime rtc_time{ + .second = bcd2dec(date[0] & 0x7f), + .minute = bcd2dec(date[1] & 0x7f), + .hour = bcd2dec(date[2] & 0x3f), + .day_of_week = bcd2dec(date[3] & 0x7f), + .day_of_month = bcd2dec(date[4] & 0x3f), + .day_of_year = 1, // ignored by recalc_timestamp_utc(false) + .month = bcd2dec(date[5] & 0x1f), + .year = static_cast(bcd2dec(date[6]) + 2000), + .is_dst = false, // not used + .timestamp = 0 // overwritten by recalc_timestamp_utc(false) + }; + rtc_time.recalc_timestamp_utc(false); + if (!rtc_time.is_valid()) { + ESP_LOGE(TAG, "Invalid RTC time, not syncing to system clock."); + return; + } + ESP_LOGD(TAG, "Read UTC time: %04d-%02d-%02d %02d:%02d:%02d", rtc_time.year, rtc_time.month, rtc_time.day_of_month, + rtc_time.hour, rtc_time.minute, rtc_time.second); + time::RealTimeClock::synchronize_epoch_(rtc_time.timestamp); +} + +void RX8130Component::write_time() { + auto now = time::RealTimeClock::utcnow(); + if (!now.is_valid()) { + ESP_LOGE(TAG, "Invalid system time, not syncing to RTC."); + return; + } + uint8_t buff[7]; + buff[0] = dec2bcd(now.second); + buff[1] = dec2bcd(now.minute); + buff[2] = dec2bcd(now.hour); + buff[3] = dec2bcd(now.day_of_week); + buff[4] = dec2bcd(now.day_of_month); + buff[5] = dec2bcd(now.month); + buff[6] = dec2bcd(now.year % 100); + this->stop_(true); + if (this->write_register(RX8130_REG_SEC, buff, 7) != i2c::ERROR_OK) { + this->status_set_warning(ESP_LOG_MSG_COMM_FAIL); + } else { + ESP_LOGD(TAG, "Wrote UTC time: %04d-%02d-%02d %02d:%02d:%02d", now.year, now.month, now.day_of_month, now.hour, + now.minute, now.second); + } + this->stop_(false); +} + +void RX8130Component::stop_(bool stop) { + const uint8_t data = stop ? RX8130_BIT_CTRL_STOP : RX8130_CLEAR_FLAGS; + if (this->write_register(RX8130_REG_CTRL0, &data, 1) != i2c::ERROR_OK) { + this->status_set_warning(ESP_LOG_MSG_COMM_FAIL); + } +} + +} // namespace rx8130 +} // namespace esphome diff --git a/esphome/components/rx8130/rx8130.h b/esphome/components/rx8130/rx8130.h new file mode 100644 index 0000000000..6694c763cd --- /dev/null +++ b/esphome/components/rx8130/rx8130.h @@ -0,0 +1,35 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/time/real_time_clock.h" + +namespace esphome { +namespace rx8130 { + +class RX8130Component : public time::RealTimeClock, public i2c::I2CDevice { + public: + void setup() override; + void update() override; + void dump_config() override; + void read_time(); + void write_time(); + /// Ensure RTC is initialized at the correct time in the setup sequence + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: + void stop_(bool stop); +}; + +template class WriteAction : public Action, public Parented { + public: + void play(const Ts... x) override { this->parent_->write_time(); } +}; + +template class ReadAction : public Action, public Parented { + public: + void play(const Ts... x) override { this->parent_->read_time(); } +}; + +} // namespace rx8130 +} // namespace esphome diff --git a/esphome/components/rx8130/time.py b/esphome/components/rx8130/time.py new file mode 100644 index 0000000000..cb0402bd32 --- /dev/null +++ b/esphome/components/rx8130/time.py @@ -0,0 +1,56 @@ +from esphome import automation +import esphome.codegen as cg +from esphome.components import i2c, time +import esphome.config_validation as cv +from esphome.const import CONF_ID + +CODEOWNERS = ["@beormund"] +DEPENDENCIES = ["i2c"] +rx8130_ns = cg.esphome_ns.namespace("rx8130") +RX8130Component = rx8130_ns.class_("RX8130Component", time.RealTimeClock, i2c.I2CDevice) +WriteAction = rx8130_ns.class_("WriteAction", automation.Action) +ReadAction = rx8130_ns.class_("ReadAction", automation.Action) + + +CONFIG_SCHEMA = time.TIME_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(RX8130Component), + } +).extend(i2c.i2c_device_schema(0x32)) + + +@automation.register_action( + "rx8130.write_time", + WriteAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(RX8130Component), + } + ), +) +async def rx8130_write_time_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var + + +@automation.register_action( + "rx8130.read_time", + ReadAction, + automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(RX8130Component), + } + ), +) +async def rx8130_read_time_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + await time.register_time(var, config) diff --git a/tests/components/rx8130/common.yaml b/tests/components/rx8130/common.yaml new file mode 100644 index 0000000000..e6b849e25b --- /dev/null +++ b/tests/components/rx8130/common.yaml @@ -0,0 +1,8 @@ +esphome: + on_boot: + - rx8130.read_time + - rx8130.write_time + +time: + - platform: rx8130 + i2c_id: i2c_bus diff --git a/tests/components/rx8130/test.esp32-idf.yaml b/tests/components/rx8130/test.esp32-idf.yaml new file mode 100644 index 0000000000..b47e39c389 --- /dev/null +++ b/tests/components/rx8130/test.esp32-idf.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/rx8130/test.esp8266-ard.yaml b/tests/components/rx8130/test.esp8266-ard.yaml new file mode 100644 index 0000000000..4a98b9388a --- /dev/null +++ b/tests/components/rx8130/test.esp8266-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + +<<: !include common.yaml diff --git a/tests/components/rx8130/test.rp2040-ard.yaml b/tests/components/rx8130/test.rp2040-ard.yaml new file mode 100644 index 0000000000..319a7c71a6 --- /dev/null +++ b/tests/components/rx8130/test.rp2040-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + +<<: !include common.yaml From 1cccfdd2b92ffb12c7f6a578258481f8c232351e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 10 Nov 2025 18:40:23 -0600 Subject: [PATCH 464/526] [wifi] Fix mesh network failover and improve retry logic reliability (#11805) --- esphome/components/wifi/wifi_component.cpp | 847 +++++++++++++++++---- esphome/components/wifi/wifi_component.h | 70 +- 2 files changed, 776 insertions(+), 141 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 789c22bae1..885288f46a 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -42,6 +42,258 @@ namespace wifi { static const char *const TAG = "wifi"; +/// WiFi Retry Logic - Priority-Based BSSID Selection +/// +/// The WiFi component uses a state machine with priority degradation to handle connection failures +/// and automatically cycle through different BSSIDs in mesh networks or multiple configured networks. +/// +/// Connection Flow: +/// ┌──────────────────────────────────────────────────────────────────────┐ +/// │ Fast Connect Path (Optional) │ +/// ├──────────────────────────────────────────────────────────────────────┤ +/// │ Entered if: configuration has 'fast_connect: true' │ +/// │ Optimization to skip scanning when possible: │ +/// │ │ +/// │ 1. INITIAL_CONNECT → Try one of: │ +/// │ a) Saved BSSID+channel (from previous boot) │ +/// │ b) First configured non-hidden network (any BSSID) │ +/// │ ↓ │ +/// │ [FAILED] → Check if more configured networks available │ +/// │ ↓ │ +/// │ 2. FAST_CONNECT_CYCLING_APS → Try remaining configured networks │ +/// │ (1 attempt each, any BSSID) │ +/// │ ↓ │ +/// │ [All Failed] → Fall through to explicit hidden or scanning │ +/// │ │ +/// │ Note: Fast connect data saved from previous successful connection │ +/// └──────────────────────────────────────────────────────────────────────┘ +/// ↓ +/// ┌──────────────────────────────────────────────────────────────────────┐ +/// │ Explicit Hidden Networks Path (Optional) │ +/// ├──────────────────────────────────────────────────────────────────────┤ +/// │ Entered if: first configured network has 'hidden: true' │ +/// │ │ +/// │ 1. EXPLICIT_HIDDEN → Try consecutive hidden networks (1 attempt) │ +/// │ Stop when visible network reached │ +/// │ ↓ │ +/// │ Example: Hidden1, Hidden2, Visible1, Hidden3, Visible2 │ +/// │ Try: Hidden1, Hidden2 (stop at Visible1) │ +/// │ ↓ │ +/// │ [All Failed] → Fall back to scan-based connection │ +/// │ │ +/// │ Note: Fast connect saves BSSID after first successful connection, │ +/// │ so subsequent boots use fast path instead of hidden mode │ +/// └──────────────────────────────────────────────────────────────────────┘ +/// ↓ +/// ┌──────────────────────────────────────────────────────────────────────┐ +/// │ Scan-Based Connection Path │ +/// ├──────────────────────────────────────────────────────────────────────┤ +/// │ │ +/// │ 1. SCAN → Sort by priority (highest first), then RSSI │ +/// │ ┌─────────────────────────────────────────────────┐ │ +/// │ │ scan_result_[0] = Best BSSID (highest priority) │ │ +/// │ │ scan_result_[1] = Second best │ │ +/// │ │ scan_result_[2] = Third best │ │ +/// │ └─────────────────────────────────────────────────┘ │ +/// │ ↓ │ +/// │ 2. SCAN_CONNECTING → Try scan_result_[0] (2 attempts) │ +/// │ (Visible1, Visible2 from example above) │ +/// │ ↓ │ +/// │ 3. FAILED → Decrease priority: 0.0 → -1.0 → -2.0 │ +/// │ (stored in persistent sta_priorities_) │ +/// │ ↓ │ +/// │ 4. Check for hidden networks: │ +/// │ - If found → RETRY_HIDDEN (try SSIDs not in scan, 1 attempt) │ +/// │ Skip hidden networks before first visible one │ +/// │ (Skip Hidden1/Hidden2, try Hidden3 from example) │ +/// │ - If none → Skip RETRY_HIDDEN, go to step 5 │ +/// │ ↓ │ +/// │ 5. FAILED → RESTARTING_ADAPTER (skipped if AP/improv active) │ +/// │ ↓ │ +/// │ 6. Loop back to start: │ +/// │ - If first network is hidden → EXPLICIT_HIDDEN (retry cycle) │ +/// │ - Otherwise → SCAN_CONNECTING (rescan) │ +/// │ ↓ │ +/// │ 7. RESCAN → Apply stored priorities, sort again │ +/// │ ┌─────────────────────────────────────────────────┐ │ +/// │ │ scan_result_[0] = BSSID B (priority 0.0) ← NEW │ │ +/// │ │ scan_result_[1] = BSSID C (priority 0.0) │ │ +/// │ │ scan_result_[2] = BSSID A (priority -2.0) ← OLD │ │ +/// │ └─────────────────────────────────────────────────┘ │ +/// │ ↓ │ +/// │ 8. SCAN_CONNECTING → Try scan_result_[0] (next best) │ +/// │ │ +/// │ Key: Priority system cycles through BSSIDs ACROSS scan cycles │ +/// │ Full retry cycle: EXPLICIT_HIDDEN → SCAN → RETRY_HIDDEN │ +/// │ Always try best available BSSID (scan_result_[0]) │ +/// └──────────────────────────────────────────────────────────────────────┘ +/// +/// Retry Phases: +/// - INITIAL_CONNECT: Try saved BSSID+channel (fast_connect), or fall back to normal flow +/// - FAST_CONNECT_CYCLING_APS: Cycle through remaining configured networks (1 attempt each, fast_connect only) +/// - EXPLICIT_HIDDEN: Try consecutive networks marked hidden:true before scanning (1 attempt per SSID) +/// - SCAN_CONNECTING: Connect using scan results (2 attempts per BSSID) +/// - RETRY_HIDDEN: Try networks not found in scan (1 attempt per SSID, skipped if none found) +/// - RESTARTING_ADAPTER: Restart WiFi adapter to clear stuck state +/// +/// Hidden Network Handling: +/// - Networks marked 'hidden: true' before first non-hidden → Tried in EXPLICIT_HIDDEN phase +/// - Networks marked 'hidden: true' after first non-hidden → Tried in RETRY_HIDDEN phase +/// - After successful connection, fast_connect saves BSSID → subsequent boots use fast path +/// - Networks not in scan results → Tried in RETRY_HIDDEN phase +/// - Networks visible in scan + not marked hidden → Skipped in RETRY_HIDDEN phase +/// - Networks marked 'hidden: true' always use hidden mode, even if broadcasting SSID + +static const LogString *retry_phase_to_log_string(WiFiRetryPhase phase) { + switch (phase) { + case WiFiRetryPhase::INITIAL_CONNECT: + return LOG_STR("INITIAL_CONNECT"); +#ifdef USE_WIFI_FAST_CONNECT + case WiFiRetryPhase::FAST_CONNECT_CYCLING_APS: + return LOG_STR("FAST_CONNECT_CYCLING"); +#endif + case WiFiRetryPhase::EXPLICIT_HIDDEN: + return LOG_STR("EXPLICIT_HIDDEN"); + case WiFiRetryPhase::SCAN_CONNECTING: + return LOG_STR("SCAN_CONNECTING"); + case WiFiRetryPhase::RETRY_HIDDEN: + return LOG_STR("RETRY_HIDDEN"); + case WiFiRetryPhase::RESTARTING_ADAPTER: + return LOG_STR("RESTARTING"); + default: + return LOG_STR("UNKNOWN"); + } +} + +bool WiFiComponent::went_through_explicit_hidden_phase_() const { + // If first configured network is marked hidden, we went through EXPLICIT_HIDDEN phase + // This means those networks were already tried and should be skipped in RETRY_HIDDEN + return !this->sta_.empty() && this->sta_[0].get_hidden(); +} + +int8_t WiFiComponent::find_first_non_hidden_index_() const { + // Find the first network that is NOT marked hidden:true + // This is where EXPLICIT_HIDDEN phase would have stopped + for (size_t i = 0; i < this->sta_.size(); i++) { + if (!this->sta_[i].get_hidden()) { + return static_cast(i); + } + } + return -1; // All networks are hidden +} + +// 2 attempts per BSSID in SCAN_CONNECTING phase +// Rationale: This is the ONLY phase where we decrease BSSID priority, so we must be very sure. +// Auth failures are common immediately after scan due to WiFi stack state transitions. +// Trying twice filters out false positives and prevents unnecessarily marking a good BSSID as bad. +// After 2 genuine failures, priority degradation ensures we skip this BSSID on subsequent scans. +static constexpr uint8_t WIFI_RETRY_COUNT_PER_BSSID = 2; + +// 1 attempt per SSID in RETRY_HIDDEN phase +// Rationale: Try hidden mode once, then rescan to get next best BSSID via priority system +static constexpr uint8_t WIFI_RETRY_COUNT_PER_SSID = 1; + +// 1 attempt per AP in fast_connect mode (INITIAL_CONNECT and FAST_CONNECT_CYCLING_APS) +// Rationale: Fast connect prioritizes speed - try each AP once to find a working one quickly +static constexpr uint8_t WIFI_RETRY_COUNT_PER_AP = 1; + +static constexpr uint8_t get_max_retries_for_phase(WiFiRetryPhase phase) { + switch (phase) { + case WiFiRetryPhase::INITIAL_CONNECT: +#ifdef USE_WIFI_FAST_CONNECT + case WiFiRetryPhase::FAST_CONNECT_CYCLING_APS: +#endif + // INITIAL_CONNECT and FAST_CONNECT_CYCLING_APS both use 1 attempt per AP (fast_connect mode) + return WIFI_RETRY_COUNT_PER_AP; + case WiFiRetryPhase::EXPLICIT_HIDDEN: + // Explicitly hidden network: 1 attempt (user marked as hidden, try once then scan) + return WIFI_RETRY_COUNT_PER_SSID; + case WiFiRetryPhase::SCAN_CONNECTING: + // Scan-based phase: 2 attempts per BSSID (handles transient auth failures after scan) + return WIFI_RETRY_COUNT_PER_BSSID; + case WiFiRetryPhase::RETRY_HIDDEN: + // Hidden network mode: 1 attempt per SSID + return WIFI_RETRY_COUNT_PER_SSID; + default: + return WIFI_RETRY_COUNT_PER_BSSID; + } +} + +static void apply_scan_result_to_params(WiFiAP ¶ms, const WiFiScanResult &scan) { + params.set_hidden(false); + params.set_ssid(scan.get_ssid()); + params.set_bssid(scan.get_bssid()); + params.set_channel(scan.get_channel()); +} + +bool WiFiComponent::needs_scan_results_() const { + // Only SCAN_CONNECTING phase needs scan results + if (this->retry_phase_ != WiFiRetryPhase::SCAN_CONNECTING) { + return false; + } + // Need scan if we have no results or no matching networks + return this->scan_result_.empty() || !this->scan_result_[0].get_matches(); +} + +bool WiFiComponent::ssid_was_seen_in_scan_(const std::string &ssid) const { + // Check if this SSID is configured as hidden + // If explicitly marked hidden, we should always try hidden mode regardless of scan results + for (const auto &conf : this->sta_) { + if (conf.get_ssid() == ssid && conf.get_hidden()) { + return false; // Treat as not seen - force hidden mode attempt + } + } + + // Otherwise, check if we saw it in scan results + for (const auto &scan : this->scan_result_) { + if (scan.get_ssid() == ssid) { + return true; + } + } + return false; +} + +int8_t WiFiComponent::find_next_hidden_sta_(int8_t start_index, bool include_explicit_hidden) { + // Find next SSID that wasn't in scan results (might be hidden) + // Start searching from start_index + 1 + for (size_t i = start_index + 1; i < this->sta_.size(); i++) { + const auto &sta = this->sta_[i]; + + // Skip networks that were already tried in EXPLICIT_HIDDEN phase + // Those are: networks marked hidden:true that appear before the first non-hidden network + if (!include_explicit_hidden && sta.get_hidden()) { + int8_t first_non_hidden_idx = this->find_first_non_hidden_index_(); + if (first_non_hidden_idx >= 0 && static_cast(i) < first_non_hidden_idx) { + ESP_LOGD(TAG, "Skipping " LOG_SECRET("'%s'") " (explicit hidden, already tried)", sta.get_ssid().c_str()); + continue; + } + } + + if (!this->ssid_was_seen_in_scan_(sta.get_ssid())) { + ESP_LOGD(TAG, "Hidden candidate " LOG_SECRET("'%s'") " at index %d", sta.get_ssid().c_str(), static_cast(i)); + return static_cast(i); + } + ESP_LOGD(TAG, "Skipping " LOG_SECRET("'%s'") " (visible in scan)", sta.get_ssid().c_str()); + } + // No hidden SSIDs found + return -1; +} + +void WiFiComponent::start_initial_connection_() { + // If first network (highest priority) is explicitly marked hidden, try it first before scanning + // This respects user's priority order when they explicitly configure hidden networks + if (!this->sta_.empty() && this->sta_[0].get_hidden()) { + ESP_LOGI(TAG, "Starting with explicit hidden network (highest priority)"); + this->selected_sta_index_ = 0; + this->retry_phase_ = WiFiRetryPhase::EXPLICIT_HIDDEN; + WiFiAP params = this->build_params_for_current_phase_(); + this->start_connecting(params, false); + } else { + ESP_LOGI(TAG, "Starting scan"); + this->start_scanning(); + } +} + #if defined(USE_ESP32) && defined(USE_WIFI_WPA2_EAP) && ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE static const char *eap_phase2_to_str(esp_eap_ttls_phase2_types type) { switch (type) { @@ -109,18 +361,28 @@ void WiFiComponent::start() { ESP_LOGV(TAG, "Setting Power Save Option failed"); } + this->transition_to_phase_(WiFiRetryPhase::INITIAL_CONNECT); #ifdef USE_WIFI_FAST_CONNECT WiFiAP params; - this->trying_loaded_ap_ = this->load_fast_connect_settings_(params); - if (!this->trying_loaded_ap_) { - // FAST CONNECT FALLBACK: No saved settings available - // Use first config (will use SSID from config) + bool loaded_fast_connect = this->load_fast_connect_settings_(params); + // Fast connect optimization: only use when we have saved BSSID+channel data + // Without saved data, try first configured network or use normal flow + if (loaded_fast_connect) { + ESP_LOGI(TAG, "Starting fast_connect (saved) " LOG_SECRET("'%s'"), params.get_ssid().c_str()); + this->start_connecting(params, false); + } else if (!this->sta_.empty() && !this->sta_[0].get_hidden()) { + // No saved data, but have configured networks - try first non-hidden network + ESP_LOGI(TAG, "Starting fast_connect (config) " LOG_SECRET("'%s'"), this->sta_[0].get_ssid().c_str()); this->selected_sta_index_ = 0; - params = this->build_wifi_ap_from_selected_(); + params = this->build_params_for_current_phase_(); + this->start_connecting(params, false); + } else { + // No saved data and (no networks OR first is hidden) - use normal flow + this->start_initial_connection_(); } - this->start_connecting(params, false); #else - this->start_scanning(); + // Without fast_connect: go straight to scanning (or hidden mode if all networks are hidden) + this->start_initial_connection_(); #endif #ifdef USE_WIFI_AP } else if (this->has_ap()) { @@ -150,8 +412,7 @@ void WiFiComponent::restart_adapter() { ESP_LOGW(TAG, "Restarting adapter"); this->wifi_mode_(false, {}); delay(100); // NOLINT - this->num_retried_ = 0; - this->retry_hidden_ = false; + // Don't set retry_phase_ or num_retried_ here - state machine handles transitions } void WiFiComponent::loop() { @@ -172,21 +433,19 @@ void WiFiComponent::loop() { case WIFI_COMPONENT_STATE_COOLDOWN: { this->status_set_warning(LOG_STR("waiting to reconnect")); if (millis() - this->action_started_ > 5000) { -#ifdef USE_WIFI_FAST_CONNECT - // Safety check: Ensure selected_sta_index_ is valid before retrying - // (should already be set by retry_connect(), but check for robustness) + // After cooldown, connect based on current retry phase this->reset_selected_ap_to_first_if_invalid_(); - WiFiAP params = this->build_wifi_ap_from_selected_(); - this->start_connecting(params, false); -#else - if (this->retry_hidden_) { - this->reset_selected_ap_to_first_if_invalid_(); - WiFiAP params = this->build_wifi_ap_from_selected_(); - this->start_connecting(params, false); - } else { + + // Check if we need to trigger a scan first + if (this->needs_scan_results_() && !this->all_networks_hidden_()) { + // Need scan results or no matching networks found - scan/rescan + ESP_LOGD(TAG, "Scanning required for phase %s", LOG_STR_ARG(retry_phase_to_log_string(this->retry_phase_))); this->start_scanning(); + } else { + // Have everything we need to connect (or all networks are hidden, skip scanning) + WiFiAP params = this->build_params_for_current_phase_(); + this->start_connecting(params, false); } -#endif } break; } @@ -344,30 +603,44 @@ void WiFiComponent::set_sta(const WiFiAP &ap) { this->selected_sta_index_ = 0; } -WiFiAP WiFiComponent::build_wifi_ap_from_selected_() const { - // PRECONDITION: selected_sta_index_ must be valid (ensured by all callers) +WiFiAP WiFiComponent::build_params_for_current_phase_() { const WiFiAP *config = this->get_selected_sta_(); - assert(config != nullptr); + if (config == nullptr) { + ESP_LOGE(TAG, "No valid network config (selected_sta_index_=%d, sta_.size()=%zu)", + static_cast(this->selected_sta_index_), this->sta_.size()); + // Return empty params - caller should handle this gracefully + return WiFiAP(); + } + WiFiAP params = *config; - // SYNCHRONIZATION: selected_sta_index_ and scan_result_[0] are kept in sync after wifi_scan_done(): - // - wifi_scan_done() sorts all scan results by priority/RSSI (best first) - // - It then finds which sta_[i] config matches scan_result_[0] - // - Sets selected_sta_index_ = i to record that matching config - // This sync holds until scan_result_ is cleared (e.g., after connection or in reset_for_next_ap_attempt_()) - if (!this->scan_result_.empty()) { - // Override with scan data - network is visible - const WiFiScanResult &scan = this->scan_result_[0]; - params.set_hidden(false); - params.set_ssid(scan.get_ssid()); - params.set_bssid(scan.get_bssid()); - params.set_channel(scan.get_channel()); - } else if (params.get_hidden()) { - // Hidden network - clear BSSID and channel even if set in config - // There might be multiple hidden networks with same SSID but we can't know which is correct - // Rely on probe-req with just SSID. Empty channel triggers ALL_CHANNEL_SCAN. - params.set_bssid(optional{}); - params.set_channel(optional{}); + switch (this->retry_phase_) { + case WiFiRetryPhase::INITIAL_CONNECT: +#ifdef USE_WIFI_FAST_CONNECT + case WiFiRetryPhase::FAST_CONNECT_CYCLING_APS: +#endif + // Fast connect phases: use config-only (no scan results) + // BSSID/channel from config if user specified them, otherwise empty + break; + + case WiFiRetryPhase::EXPLICIT_HIDDEN: + case WiFiRetryPhase::RETRY_HIDDEN: + // Hidden network mode: clear BSSID/channel to trigger probe request + // (both explicit hidden and retry hidden use same behavior) + params.set_bssid(optional{}); + params.set_channel(optional{}); + break; + + case WiFiRetryPhase::SCAN_CONNECTING: + // Scan-based phase: always use best scan result (index 0 - highest priority after sorting) + if (!this->scan_result_.empty()) { + apply_scan_result_to_params(params, this->scan_result_[0]); + } + break; + + case WiFiRetryPhase::RESTARTING_ADAPTER: + // Should not be building params during restart + break; } return params; @@ -392,7 +665,21 @@ void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &pa } void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { - ESP_LOGI(TAG, "Connecting to '%s'", ap.get_ssid().c_str()); + // Log connection attempt at INFO level with priority + std::string bssid_formatted; + float priority = 0.0f; + + if (ap.get_bssid().has_value()) { + bssid_formatted = format_mac_address_pretty(ap.get_bssid().value().data()); + priority = this->get_sta_priority(ap.get_bssid().value()); + } + + ESP_LOGI(TAG, + "Connecting to " LOG_SECRET("'%s'") " " LOG_SECRET("(%s)") " (priority %.1f, attempt %u/%u in phase %s)...", + ap.get_ssid().c_str(), ap.get_bssid().has_value() ? bssid_formatted.c_str() : LOG_STR_LITERAL("any"), + priority, this->num_retried_ + 1, get_max_retries_for_phase(this->retry_phase_), + LOG_STR_ARG(retry_phase_to_log_string(this->retry_phase_))); + #ifdef ESPHOME_LOG_HAS_VERBOSE ESP_LOGV(TAG, "Connection Params:"); ESP_LOGV(TAG, " SSID: '%s'", ap.get_ssid().c_str()); @@ -565,8 +852,39 @@ void WiFiComponent::start_scanning() { this->state_ = WIFI_COMPONENT_STATE_STA_SCANNING; } -// Helper function for WiFi scan result comparison -// Returns true if 'a' should be placed before 'b' in the sorted order +/// Comparator for WiFi scan result sorting - determines which network should be tried first +/// Returns true if 'a' should be placed before 'b' in the sorted order (a is "better" than b) +/// +/// Sorting logic (in priority order): +/// 1. Matching networks always ranked before non-matching networks +/// 2. For matching networks: Priority first (CRITICAL - tracks failure history) +/// 3. RSSI as tiebreaker for equal priority or non-matching networks +/// +/// WHY PRIORITY MUST BE CHECKED FIRST: +/// The priority field tracks connection failure history via priority degradation: +/// - Initial priority: 0.0 (from config or default) +/// - Each connection failure: priority -= 1.0 (becomes -1.0, -2.0, -3.0, etc.) +/// - Failed BSSIDs sorted lower → naturally try different BSSID on next scan +/// +/// This enables automatic BSSID cycling for various real-world failure scenarios: +/// - Crashed/hung AP (visible but not responding) +/// - Misconfigured mesh node (accepts auth but no DHCP/routing) +/// - Capacity limits (AP refuses new clients) +/// - Rogue AP (same SSID, wrong password or malicious) +/// - Intermittent hardware issues (flaky radio, overheating) +/// +/// Example mesh network: 3 APs with same SSID "home", all at priority 0.0 initially +/// - Try strongest BSSID A (sorted by RSSI) → fails → priority A becomes -1.0 +/// - Next scan: BSSID B and C (priority 0.0) sorted BEFORE A (priority -1.0) +/// - Try next strongest BSSID B → succeeds or fails and gets deprioritized +/// - System naturally cycles through all BSSIDs via priority degradation +/// - Eventually finds working AP or tries all options before restarting adapter +/// +/// If we checked RSSI first (Bug in PR #9963): +/// - Same failed BSSID would keep being selected if it has strongest signal +/// - Device stuck connecting to crashed AP with -30dBm while working AP at -50dBm ignored +/// - Priority degradation would be useless +/// - Mesh networks would never recover from single AP failure [[nodiscard]] inline static bool wifi_scan_result_is_better(const WiFiScanResult &a, const WiFiScanResult &b) { // Matching networks always come before non-matching if (a.get_matches() && !b.get_matches()) @@ -574,21 +892,13 @@ void WiFiComponent::start_scanning() { if (!a.get_matches() && b.get_matches()) return false; - if (a.get_matches() && b.get_matches()) { - // 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(); + // Both matching: check priority first (tracks connection failures via priority degradation) + // Priority is decreased when a BSSID fails to connect, so lower priority = previously failed + if (a.get_matches() && b.get_matches() && a.get_priority() != b.get_priority()) { + return a.get_priority() > b.get_priority(); } - // Both don't match - sort by signal strength + // Use RSSI as tiebreaker (for equal-priority matching networks or all non-matching networks) return a.get_rssi() > b.get_rssi(); } @@ -623,10 +933,8 @@ __attribute__((noinline)) static void log_scan_result(const WiFiScanResult &res) ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), res.get_is_hidden() ? LOG_STR_LITERAL("(HIDDEN) ") : LOG_STR_LITERAL(""), bssid_s, LOG_STR_ARG(get_signal_bars(res.get_rssi()))); - ESP_LOGD(TAG, - " Channel: %u\n" - " RSSI: %d dB", - res.get_channel(), res.get_rssi()); + ESP_LOGD(TAG, " Channel: %2u, RSSI: %3d dB, Priority: %4.1f", res.get_channel(), res.get_rssi(), + res.get_priority()); } else { ESP_LOGD(TAG, "- " LOG_SECRET("'%s'") " " LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), bssid_s, LOG_STR_ARG(get_signal_bars(res.get_rssi()))); @@ -675,34 +983,36 @@ void WiFiComponent::check_scanning_finished() { // SYNCHRONIZATION POINT: Establish link between scan_result_[0] and selected_sta_index_ // After sorting, scan_result_[0] contains the best network. Now find which sta_[i] config // matches that network and record it in selected_sta_index_. This keeps the two indices - // synchronized so build_wifi_ap_from_selected_() can safely use both to build connection parameters. + // synchronized so build_params_for_current_phase_() can safely use both to build connection parameters. const WiFiScanResult &scan_res = this->scan_result_[0]; - if (!scan_res.get_matches()) { - ESP_LOGW(TAG, "No matching network found"); - this->retry_connect(); - return; - } - bool found_match = false; - for (size_t i = 0; i < this->sta_.size(); i++) { - if (scan_res.matches(this->sta_[i])) { - // Safe cast: sta_.size() limited to MAX_WIFI_NETWORKS (127) in __init__.py validation - // No overflow check needed - YAML validation prevents >127 networks - this->selected_sta_index_ = static_cast(i); // Links scan_result_[0] with sta_[i] - found_match = true; - break; + if (scan_res.get_matches()) { + for (size_t i = 0; i < this->sta_.size(); i++) { + if (scan_res.matches(this->sta_[i])) { + // Safe cast: sta_.size() limited to MAX_WIFI_NETWORKS (127) in __init__.py validation + // No overflow check needed - YAML validation prevents >127 networks + this->selected_sta_index_ = static_cast(i); // Links scan_result_[0] with sta_[i] + found_match = true; + break; + } } } if (!found_match) { ESP_LOGW(TAG, "No matching network found"); - this->retry_connect(); - return; + // No scan results matched our configured networks - transition directly to hidden mode + // Don't call retry_connect() since we never attempted a connection (no BSSID to penalize) + this->transition_to_phase_(WiFiRetryPhase::RETRY_HIDDEN); + // Now start connection attempt in hidden mode + } else if (this->transition_to_phase_(WiFiRetryPhase::SCAN_CONNECTING)) { + return; // scan started, wait for next loop iteration } yield(); - WiFiAP params = this->build_wifi_ap_from_selected_(); + WiFiAP params = this->build_params_for_current_phase_(); + // Ensure we're in SCAN_CONNECTING phase when connecting with scan results + // (needed when scan was started directly without transition_to_phase_, e.g., initial scan) this->start_connecting(params, false); } @@ -724,11 +1034,14 @@ void WiFiComponent::check_connecting_finished() { ESP_LOGI(TAG, "Connected"); // Warn if we had to retry with hidden network mode for a network that's not marked hidden // Only warn if we actually connected without scan data (SSID only), not if scan succeeded on retry - if (const WiFiAP *config = this->get_selected_sta_(); - this->retry_hidden_ && config && !config->get_hidden() && this->scan_result_.empty()) { - ESP_LOGW(TAG, "Network '%s' should be marked as hidden", config->get_ssid().c_str()); + if (const WiFiAP *config = this->get_selected_sta_(); this->retry_phase_ == WiFiRetryPhase::RETRY_HIDDEN && + config && !config->get_hidden() && + this->scan_result_.empty()) { + ESP_LOGW(TAG, LOG_SECRET("'%s'") " should be marked hidden", config->get_ssid().c_str()); } - this->retry_hidden_ = false; + // Reset to initial phase on successful connection (don't log transition, just reset state) + this->retry_phase_ = WiFiRetryPhase::INITIAL_CONNECT; + this->num_retried_ = 0; this->print_connect_params_(); @@ -796,58 +1109,334 @@ void WiFiComponent::check_connecting_finished() { this->retry_connect(); } -void WiFiComponent::retry_connect() { - if (const WiFiAP *config = this->get_selected_sta_(); config && config->get_bssid()) { - auto bssid = *config->get_bssid(); - float priority = this->get_sta_priority(bssid); - this->set_sta_priority(bssid, priority - 1.0f); +/// Determine the next retry phase based on current state and failure conditions +/// This function examines the current retry phase, number of retries, and failure reasons +/// to decide what phase to move to next. It does not modify any state - it only returns +/// the recommended next phase. +/// +/// @return The next WiFiRetryPhase to transition to (may be same as current phase if should retry) +WiFiRetryPhase WiFiComponent::determine_next_phase_() { + switch (this->retry_phase_) { + case WiFiRetryPhase::INITIAL_CONNECT: +#ifdef USE_WIFI_FAST_CONNECT + case WiFiRetryPhase::FAST_CONNECT_CYCLING_APS: + // INITIAL_CONNECT and FAST_CONNECT_CYCLING_APS: no retries, try next AP or fall back to scan + if (this->selected_sta_index_ < static_cast(this->sta_.size()) - 1) { + return WiFiRetryPhase::FAST_CONNECT_CYCLING_APS; // Move to next AP + } +#endif + // No more APs to try, fall back to scan + return WiFiRetryPhase::SCAN_CONNECTING; + + case WiFiRetryPhase::EXPLICIT_HIDDEN: { + // Try all explicitly hidden networks before scanning + if (this->num_retried_ + 1 < WIFI_RETRY_COUNT_PER_SSID) { + return WiFiRetryPhase::EXPLICIT_HIDDEN; // Keep retrying same SSID + } + + // Exhausted retries on current SSID - check for more explicitly hidden networks + // Stop when we reach a visible network (proceed to scanning) + size_t next_index = this->selected_sta_index_ + 1; + if (next_index < this->sta_.size() && this->sta_[next_index].get_hidden()) { + // Found another explicitly hidden network + return WiFiRetryPhase::EXPLICIT_HIDDEN; + } + + // No more consecutive explicitly hidden networks - proceed to scanning + return WiFiRetryPhase::SCAN_CONNECTING; + } + + case WiFiRetryPhase::SCAN_CONNECTING: + // If scan found no matching networks, skip to hidden network mode + if (!this->scan_result_.empty() && !this->scan_result_[0].get_matches()) { + return WiFiRetryPhase::RETRY_HIDDEN; + } + + if (this->num_retried_ + 1 < WIFI_RETRY_COUNT_PER_BSSID) { + return WiFiRetryPhase::SCAN_CONNECTING; // Keep retrying same BSSID + } + + // Exhausted retries on current BSSID (scan_result_[0]) + // Its priority has been decreased, so on next scan it will be sorted lower + // and we'll try the next best BSSID. + // Check if there are any potentially hidden networks to try + if (this->find_next_hidden_sta_(-1, !this->went_through_explicit_hidden_phase_()) >= 0) { + return WiFiRetryPhase::RETRY_HIDDEN; // Found hidden networks to try + } + // No hidden networks - skip directly to restart/rescan + if (this->is_captive_portal_active_() || this->is_esp32_improv_active_()) { + return this->went_through_explicit_hidden_phase_() ? WiFiRetryPhase::EXPLICIT_HIDDEN + : WiFiRetryPhase::SCAN_CONNECTING; + } + return WiFiRetryPhase::RESTARTING_ADAPTER; + + case WiFiRetryPhase::RETRY_HIDDEN: + // If no hidden SSIDs to try (selected_sta_index_ == -1), skip directly to rescan + if (this->selected_sta_index_ >= 0) { + if (this->num_retried_ + 1 < WIFI_RETRY_COUNT_PER_SSID) { + return WiFiRetryPhase::RETRY_HIDDEN; // Keep retrying same SSID + } + + // Exhausted retries on current SSID - check if there are more potentially hidden SSIDs to try + if (this->selected_sta_index_ < static_cast(this->sta_.size()) - 1) { + // More SSIDs available - stay in RETRY_HIDDEN, advance will happen in retry_connect() + return WiFiRetryPhase::RETRY_HIDDEN; + } + } + // Exhausted all potentially hidden SSIDs - rescan to try next BSSID + // If captive portal/improv is active, skip adapter restart and go back to start + // Otherwise restart adapter to clear any stuck state + if (this->is_captive_portal_active_() || this->is_esp32_improv_active_()) { + // Go back to explicit hidden if we went through it initially, otherwise scan + return this->went_through_explicit_hidden_phase_() ? WiFiRetryPhase::EXPLICIT_HIDDEN + : WiFiRetryPhase::SCAN_CONNECTING; + } + + // Restart adapter + return WiFiRetryPhase::RESTARTING_ADAPTER; + + case WiFiRetryPhase::RESTARTING_ADAPTER: + // After restart, go back to explicit hidden if we went through it initially, otherwise scan + return this->went_through_explicit_hidden_phase_() ? WiFiRetryPhase::EXPLICIT_HIDDEN + : WiFiRetryPhase::SCAN_CONNECTING; } - delay(10); - if (!this->is_captive_portal_active_() && !this->is_esp32_improv_active_() && - (this->num_retried_ > 3 || this->error_from_callback_)) { -#ifdef USE_WIFI_FAST_CONNECT - // No empty check needed - YAML validation requires at least one network for fast_connect - if (this->trying_loaded_ap_) { - this->trying_loaded_ap_ = false; - this->selected_sta_index_ = 0; // Retry from the first configured AP - this->reset_for_next_ap_attempt_(); - } else if (this->selected_sta_index_ >= static_cast(this->sta_.size()) - 1) { - // Safe cast: sta_.size() limited to MAX_WIFI_NETWORKS (127) in __init__.py validation - // Exhausted all configured APs, restart adapter and cycle back to first - // Restart clears any stuck WiFi driver state - // Each AP is tried with config data only (SSID + optional BSSID/channel if user configured them) - // Typically SSID only, which triggers ESP-IDF internal scanning - ESP_LOGW(TAG, "No more APs to try"); - this->selected_sta_index_ = 0; - this->reset_for_next_ap_attempt_(); - this->restart_adapter(); - } else { - // Try next AP - this->selected_sta_index_++; - this->reset_for_next_ap_attempt_(); - } -#else - if (this->num_retried_ > 5) { - // If retry failed for more than 5 times, let's restart STA - this->restart_adapter(); - } else { - // Try hidden networks after 3 failed retries - ESP_LOGD(TAG, "Retrying with hidden networks"); - this->retry_hidden_ = true; - this->num_retried_++; - } -#endif - } else { - this->num_retried_++; + // Should never reach here + return WiFiRetryPhase::SCAN_CONNECTING; +} + +/// Transition from current retry phase to a new phase with logging and phase-specific setup +/// This function handles the actual state change, including: +/// - Logging the phase transition +/// - Resetting the retry counter +/// - Performing phase-specific initialization (e.g., advancing AP index, starting scans) +/// +/// @param new_phase The phase we're transitioning TO +/// @return true if an async scan was started (caller should wait for completion) +/// false if no scan started (caller can proceed with connection attempt) +bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) { + WiFiRetryPhase old_phase = this->retry_phase_; + + // No-op if staying in same phase + if (old_phase == new_phase) { + return false; } + + ESP_LOGD(TAG, "Retry phase: %s → %s", LOG_STR_ARG(retry_phase_to_log_string(old_phase)), + LOG_STR_ARG(retry_phase_to_log_string(new_phase))); + + this->retry_phase_ = new_phase; + this->num_retried_ = 0; // Reset retry counter on phase change + + // Phase-specific setup + switch (new_phase) { +#ifdef USE_WIFI_FAST_CONNECT + case WiFiRetryPhase::FAST_CONNECT_CYCLING_APS: + // Move to next configured AP - clear old scan data so new AP is tried with config only + this->selected_sta_index_++; + this->scan_result_.clear(); + break; +#endif + + case WiFiRetryPhase::EXPLICIT_HIDDEN: + // Starting explicit hidden phase - reset to first network + this->selected_sta_index_ = 0; + break; + + case WiFiRetryPhase::SCAN_CONNECTING: + // Transitioning to scan-based connection +#ifdef USE_WIFI_FAST_CONNECT + if (old_phase == WiFiRetryPhase::FAST_CONNECT_CYCLING_APS) { + ESP_LOGI(TAG, "Fast connect exhausted, falling back to scan"); + } +#endif + // Trigger scan if we don't have scan results OR if transitioning from phases that need fresh scan + if (this->scan_result_.empty() || old_phase == WiFiRetryPhase::EXPLICIT_HIDDEN || + old_phase == WiFiRetryPhase::RETRY_HIDDEN || old_phase == WiFiRetryPhase::RESTARTING_ADAPTER) { + this->selected_sta_index_ = -1; // Will be set after scan completes + this->start_scanning(); + return true; // Started scan, wait for completion + } + // Already have scan results - selected_sta_index_ should already be synchronized + // (set in check_scanning_finished() when scan completed) + // No need to reset it here + break; + + case WiFiRetryPhase::RETRY_HIDDEN: + // Starting hidden mode - find first SSID that wasn't in scan results + if (old_phase == WiFiRetryPhase::SCAN_CONNECTING) { + // Keep scan results so we can skip SSIDs that were visible in the scan + // Don't clear scan_result_ - we need it to know which SSIDs are NOT hidden + + // If first network is marked hidden, we went through EXPLICIT_HIDDEN phase + // In that case, skip networks marked hidden:true (already tried) + // Otherwise, include them (they haven't been tried yet) + this->selected_sta_index_ = this->find_next_hidden_sta_(-1, !this->went_through_explicit_hidden_phase_()); + + if (this->selected_sta_index_ == -1) { + ESP_LOGD(TAG, "All SSIDs visible or already tried, skipping hidden mode"); + } + } + break; + + case WiFiRetryPhase::RESTARTING_ADAPTER: + this->restart_adapter(); + // Return true to indicate we should wait (go to COOLDOWN) instead of immediately connecting + return true; + + default: + break; + } + + return false; // Did not start scan, can proceed with connection +} + +/// Log failed connection attempt and decrease BSSID priority to avoid repeated failures +/// This function identifies which BSSID was attempted (from scan results or config), +/// decreases its priority by 1.0 to discourage future attempts, and logs the change. +/// +/// The priority degradation system ensures that failed BSSIDs are automatically sorted +/// lower in subsequent scans, naturally cycling through different APs without explicit +/// BSSID tracking within a scan cycle. +/// +/// Priority sources: +/// - SCAN_CONNECTING phase: Uses BSSID from scan_result_[0] (best match after sorting) +/// - Other phases: Uses BSSID from config if explicitly specified by user or fast_connect +/// +/// If no BSSID is available (SSID-only connection), priority adjustment is skipped. +void WiFiComponent::log_and_adjust_priority_for_failed_connect_() { + // Determine which BSSID we tried to connect to + optional failed_bssid; + + if (this->retry_phase_ == WiFiRetryPhase::SCAN_CONNECTING && !this->scan_result_.empty()) { + // Scan-based phase: always use best result (index 0) + failed_bssid = this->scan_result_[0].get_bssid(); + } else if (const WiFiAP *config = this->get_selected_sta_(); config && config->get_bssid()) { + // Config has specific BSSID (fast_connect or user-specified) + failed_bssid = *config->get_bssid(); + } + + if (!failed_bssid.has_value()) { + return; // No BSSID to penalize + } + + // Decrease priority to avoid repeatedly trying the same failed BSSID + float old_priority = this->get_sta_priority(failed_bssid.value()); + float new_priority = old_priority - 1.0f; + this->set_sta_priority(failed_bssid.value(), new_priority); + + // Get SSID for logging + std::string ssid; + if (this->retry_phase_ == WiFiRetryPhase::SCAN_CONNECTING && !this->scan_result_.empty()) { + ssid = this->scan_result_[0].get_ssid(); + } else if (const WiFiAP *config = this->get_selected_sta_()) { + ssid = config->get_ssid(); + } + + ESP_LOGD(TAG, "Failed " LOG_SECRET("'%s'") " " LOG_SECRET("(%s)") ", priority %.1f → %.1f", ssid.c_str(), + format_mac_address_pretty(failed_bssid.value().data()).c_str(), old_priority, new_priority); +} + +/// Handle target advancement or retry counter increment when staying in the same phase +/// This function is called when a connection attempt fails and determine_next_phase_() indicates +/// we should stay in the current phase. It decides whether to: +/// - Advance to the next target (AP in fast_connect, SSID in hidden mode) +/// - Or increment the retry counter to try the same target again +/// +/// Phase-specific behavior: +/// - FAST_CONNECT_CYCLING_APS: Always advance to next AP (no retries per AP) +/// - RETRY_HIDDEN: Advance to next SSID after exhausting retries on current SSID +/// - Other phases: Increment retry counter (will retry same target) +void WiFiComponent::advance_to_next_target_or_increment_retry_() { + WiFiRetryPhase current_phase = this->retry_phase_; + + // Check if we need to advance to next AP/SSID within the same phase +#ifdef USE_WIFI_FAST_CONNECT + if (current_phase == WiFiRetryPhase::FAST_CONNECT_CYCLING_APS) { + // Fast connect: always advance to next AP (no retries per AP) + this->selected_sta_index_++; + this->num_retried_ = 0; + ESP_LOGD(TAG, "Next AP in %s", LOG_STR_ARG(retry_phase_to_log_string(this->retry_phase_))); + return; + } +#endif + + if (current_phase == WiFiRetryPhase::EXPLICIT_HIDDEN && this->num_retried_ + 1 >= WIFI_RETRY_COUNT_PER_SSID) { + // Explicit hidden: exhausted retries on current SSID, find next explicitly hidden network + // Stop when we reach a visible network (proceed to scanning) + size_t next_index = this->selected_sta_index_ + 1; + if (next_index < this->sta_.size() && this->sta_[next_index].get_hidden()) { + this->selected_sta_index_ = static_cast(next_index); + this->num_retried_ = 0; + ESP_LOGD(TAG, "Next explicit hidden network at index %d", static_cast(next_index)); + return; + } + // No more consecutive explicit hidden networks found - fall through to trigger phase change + } + + if (current_phase == WiFiRetryPhase::RETRY_HIDDEN && this->num_retried_ + 1 >= WIFI_RETRY_COUNT_PER_SSID) { + // Hidden mode: exhausted retries on current SSID, find next potentially hidden SSID + // If first network is marked hidden, we went through EXPLICIT_HIDDEN phase + // In that case, skip networks marked hidden:true (already tried) + // Otherwise, include them (they haven't been tried yet) + int8_t next_index = + this->find_next_hidden_sta_(this->selected_sta_index_, !this->went_through_explicit_hidden_phase_()); + if (next_index != -1) { + // Found another potentially hidden SSID + this->selected_sta_index_ = next_index; + this->num_retried_ = 0; + return; + } + // No more potentially hidden SSIDs - set selected_sta_index_ to -1 to trigger phase change + // This ensures determine_next_phase_() will skip the RETRY_HIDDEN logic and transition out + this->selected_sta_index_ = -1; + // Return early - phase change will happen on next wifi_loop() iteration + return; + } + + // Don't increment retry counter if we're in a scan phase with no valid targets + if (this->needs_scan_results_()) { + return; + } + + // Increment retry counter to try the same target again + this->num_retried_++; + ESP_LOGD(TAG, "Retry attempt %u/%u in phase %s", this->num_retried_ + 1, + get_max_retries_for_phase(this->retry_phase_), LOG_STR_ARG(retry_phase_to_log_string(this->retry_phase_))); +} + +void WiFiComponent::retry_connect() { + this->log_and_adjust_priority_for_failed_connect_(); + + delay(10); + + // Determine next retry phase based on current state + WiFiRetryPhase current_phase = this->retry_phase_; + WiFiRetryPhase next_phase = this->determine_next_phase_(); + + // Handle phase transitions (transition_to_phase_ handles same-phase no-op internally) + if (this->transition_to_phase_(next_phase)) { + return; // Wait for scan to complete + } + + if (next_phase == current_phase) { + this->advance_to_next_target_or_increment_retry_(); + } + this->error_from_callback_ = false; + if (this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTING) { yield(); - this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING_2; - WiFiAP params = this->build_wifi_ap_from_selected_(); - this->start_connecting(params, true); - return; + // Check if we have a valid target before building params + // After exhausting all networks in a phase, selected_sta_index_ may be -1 + // In that case, skip connection and let next wifi_loop() handle phase transition + if (this->selected_sta_index_ >= 0) { + this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING_2; + WiFiAP params = this->build_params_for_current_phase_(); + this->start_connecting(params, true); + return; + } + // No valid target - fall through to set state to allow phase transition } this->state_ = WIFI_COMPONENT_STATE_COOLDOWN; diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index cb75edf5a0..1cdf3234c7 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -94,6 +94,24 @@ enum class WiFiSTAConnectStatus : int { ERROR_CONNECT_FAILED, }; +/// Tracks the current retry strategy/phase for WiFi connection attempts +enum class WiFiRetryPhase : uint8_t { + /// Initial connection attempt (varies based on fast_connect setting) + INITIAL_CONNECT, +#ifdef USE_WIFI_FAST_CONNECT + /// Fast connect mode: cycling through configured APs (config-only, no scan) + FAST_CONNECT_CYCLING_APS, +#endif + /// Explicitly hidden networks (user marked as hidden, try before scanning) + EXPLICIT_HIDDEN, + /// Scan-based: connecting to best AP from scan results + SCAN_CONNECTING, + /// Retry networks not found in scan (might be hidden) + RETRY_HIDDEN, + /// Restarting WiFi adapter to clear stuck state + RESTARTING_ADAPTER, +}; + /// Struct for setting static IPs in WiFiComponent. struct ManualIP { network::IPAddress static_ip; @@ -341,8 +359,37 @@ class WiFiComponent : public Component { #endif // USE_WIFI_AP void print_connect_params_(); - WiFiAP build_wifi_ap_from_selected_() const; + WiFiAP build_params_for_current_phase_(); + /// Determine next retry phase based on current state and failure conditions + WiFiRetryPhase determine_next_phase_(); + /// Transition to a new retry phase with logging + /// Returns true if a scan was started (caller should wait), false otherwise + bool transition_to_phase_(WiFiRetryPhase new_phase); + /// Check if we need valid scan results for the current phase but don't have any + /// Returns true if the phase requires scan results but they're missing or don't match + bool needs_scan_results_() const; + /// Check if we went through EXPLICIT_HIDDEN phase (first network is marked hidden) + /// Used in RETRY_HIDDEN to determine whether to skip explicitly hidden networks + bool went_through_explicit_hidden_phase_() const; + /// Find the index of the first non-hidden network + /// Returns where EXPLICIT_HIDDEN phase would have stopped, or -1 if all networks are hidden + int8_t find_first_non_hidden_index_() const; + /// Check if an SSID was seen in the most recent scan results + /// Used to skip hidden mode for SSIDs we know are visible + bool ssid_was_seen_in_scan_(const std::string &ssid) const; + /// Find next SSID that wasn't in scan results (might be hidden) + /// Returns index of next potentially hidden SSID, or -1 if none found + /// @param start_index Start searching from index after this (-1 to start from beginning) + /// @param include_explicit_hidden If true, include SSIDs marked hidden:true. If false, only find truly hidden SSIDs. + int8_t find_next_hidden_sta_(int8_t start_index, bool include_explicit_hidden = true); + /// Log failed connection and decrease BSSID priority to avoid repeated attempts + void log_and_adjust_priority_for_failed_connect_(); + /// Advance to next target (AP/SSID) within current phase, or increment retry counter + /// Called when staying in the same phase after a failed connection attempt + void advance_to_next_target_or_increment_retry_(); + /// Start initial connection - either scan or connect directly to hidden networks + void start_initial_connection_(); const WiFiAP *get_selected_sta_() const { if (this->selected_sta_index_ >= 0 && static_cast(this->selected_sta_index_) < this->sta_.size()) { return &this->sta_[this->selected_sta_index_]; @@ -356,14 +403,15 @@ class WiFiComponent : public Component { } } -#ifdef USE_WIFI_FAST_CONNECT - // Reset state for next fast connect AP attempt - // Clears old scan data so the new AP is tried with config only (SSID without specific BSSID/channel) - void reset_for_next_ap_attempt_() { - this->num_retried_ = 0; - this->scan_result_.clear(); + bool all_networks_hidden_() const { + if (this->sta_.empty()) + return false; + for (const auto &ap : this->sta_) { + if (!ap.get_hidden()) + return false; + } + return true; } -#endif void wifi_loop_(); bool wifi_mode_(optional sta, optional ap); @@ -443,20 +491,18 @@ class WiFiComponent : public Component { // Group all 8-bit values together WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF}; WiFiPowerSaveMode power_save_{WIFI_POWER_SAVE_NONE}; + WiFiRetryPhase retry_phase_{WiFiRetryPhase::INITIAL_CONNECT}; uint8_t num_retried_{0}; // Index into sta_ array for the currently selected AP configuration (-1 = none selected) // Used to access password, manual_ip, priority, EAP settings, and hidden flag // int8_t limits to 127 APs (enforced in __init__.py via MAX_WIFI_NETWORKS) int8_t selected_sta_index_{-1}; + #if USE_NETWORK_IPV6 uint8_t num_ipv6_addresses_{0}; #endif /* USE_NETWORK_IPV6 */ // Group all boolean values together -#ifdef USE_WIFI_FAST_CONNECT - bool trying_loaded_ap_{false}; -#endif - bool retry_hidden_{false}; bool has_ap_{false}; bool handled_connected_state_{false}; bool error_from_callback_{false}; From 82692d7053ec4713a4eadb802ec33619d91ce57f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 10 Nov 2025 19:00:54 -0600 Subject: [PATCH 465/526] [tests] Migrate components to shared packages and fix ID ambiguity (#11819) --- script/analyze_component_buses.py | 1 + tests/components/chsc6x/test.rp2040-ard.yaml | 1 + tests/components/hlk_fm22x/common.yaml | 41 ++++++++++++++++ .../components/hlk_fm22x/test.esp32-idf.yaml | 49 ++----------------- .../hlk_fm22x/test.esp8266-ard.yaml | 49 ++----------------- .../components/hlk_fm22x/test.rp2040-ard.yaml | 49 ++----------------- tests/components/speaker/common.yaml | 30 +++++++++--- tests/components/toshiba/common_ras2819t.yaml | 8 --- .../toshiba/test_ras2819t.esp32-ard.yaml | 6 +-- .../toshiba/test_ras2819t.esp32-c3-ard.yaml | 6 +-- .../toshiba/test_ras2819t.esp32-idf.yaml | 6 +-- .../toshiba/test_ras2819t.esp8266-ard.yaml | 6 +-- .../common/remote_receiver/esp32-ard.yaml | 12 +++++ .../common/remote_receiver/esp32-c3-ard.yaml | 12 +++++ 14 files changed, 111 insertions(+), 165 deletions(-) create mode 100644 tests/components/hlk_fm22x/common.yaml create mode 100644 tests/test_build_components/common/remote_receiver/esp32-ard.yaml create mode 100644 tests/test_build_components/common/remote_receiver/esp32-c3-ard.yaml diff --git a/script/analyze_component_buses.py b/script/analyze_component_buses.py index 38d1f8c2b7..27a36f889f 100755 --- a/script/analyze_component_buses.py +++ b/script/analyze_component_buses.py @@ -86,6 +86,7 @@ ISOLATED_COMPONENTS = { "modbus_controller": "Defines multiple modbus buses for testing client/server functionality - conflicts with package modbus bus", "neopixelbus": "RMT type conflict with ESP32 Arduino/ESP-IDF headers (enum vs struct rmt_channel_t)", "packages": "cannot merge packages", + "tinyusb": "Conflicts with usb_host component - cannot be used together", } diff --git a/tests/components/chsc6x/test.rp2040-ard.yaml b/tests/components/chsc6x/test.rp2040-ard.yaml index 89cc1b7477..2e3613a4a3 100644 --- a/tests/components/chsc6x/test.rp2040-ard.yaml +++ b/tests/components/chsc6x/test.rp2040-ard.yaml @@ -16,5 +16,6 @@ display: touchscreen: - platform: chsc6x + i2c_id: i2c_bus display: ili9xxx_display interrupt_pin: 22 diff --git a/tests/components/hlk_fm22x/common.yaml b/tests/components/hlk_fm22x/common.yaml new file mode 100644 index 0000000000..6fcd9af594 --- /dev/null +++ b/tests/components/hlk_fm22x/common.yaml @@ -0,0 +1,41 @@ +esphome: + on_boot: + then: + - hlk_fm22x.enroll: + name: "Test" + direction: 1 + - hlk_fm22x.delete_all: + +hlk_fm22x: + on_face_scan_matched: + - logger.log: test_hlk_22x_face_scan_matched + on_face_scan_unmatched: + - logger.log: test_hlk_22x_face_scan_unmatched + on_face_scan_invalid: + - logger.log: test_hlk_22x_face_scan_invalid + on_face_info: + - logger.log: test_hlk_22x_face_info + on_enrollment_done: + - logger.log: test_hlk_22x_enrollment_done + on_enrollment_failed: + - logger.log: test_hlk_22x_enrollment_failed + +sensor: + - platform: hlk_fm22x + face_count: + name: "Face Count" + last_face_id: + name: "Last Face ID" + status: + name: "Face Status" + +binary_sensor: + - platform: hlk_fm22x + name: "Face Enrolling" + +text_sensor: + - platform: hlk_fm22x + version: + name: "HLK Version" + last_face_name: + name: "Last Face Name" diff --git a/tests/components/hlk_fm22x/test.esp32-idf.yaml b/tests/components/hlk_fm22x/test.esp32-idf.yaml index 5e7cbde664..2d29656c94 100644 --- a/tests/components/hlk_fm22x/test.esp32-idf.yaml +++ b/tests/components/hlk_fm22x/test.esp32-idf.yaml @@ -1,47 +1,4 @@ -esphome: - on_boot: - then: - - hlk_fm22x.enroll: - name: "Test" - direction: 1 - - hlk_fm22x.delete_all: +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml -uart: - - id: uart_hlk_fm22x - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 - -hlk_fm22x: - on_face_scan_matched: - - logger.log: test_hlk_22x_face_scan_matched - on_face_scan_unmatched: - - logger.log: test_hlk_22x_face_scan_unmatched - on_face_scan_invalid: - - logger.log: test_hlk_22x_face_scan_invalid - on_face_info: - - logger.log: test_hlk_22x_face_info - on_enrollment_done: - - logger.log: test_hlk_22x_enrollment_done - on_enrollment_failed: - - logger.log: test_hlk_22x_enrollment_failed - -sensor: - - platform: hlk_fm22x - face_count: - name: "Face Count" - last_face_id: - name: "Last Face ID" - status: - name: "Face Status" - -binary_sensor: - - platform: hlk_fm22x - name: "Face Enrolling" - -text_sensor: - - platform: hlk_fm22x - version: - name: "HLK Version" - last_face_name: - name: "Last Face Name" +<<: !include common.yaml diff --git a/tests/components/hlk_fm22x/test.esp8266-ard.yaml b/tests/components/hlk_fm22x/test.esp8266-ard.yaml index 680047834c..5a05efa259 100644 --- a/tests/components/hlk_fm22x/test.esp8266-ard.yaml +++ b/tests/components/hlk_fm22x/test.esp8266-ard.yaml @@ -1,47 +1,4 @@ -esphome: - on_boot: - then: - - hlk_fm22x.enroll: - name: "Test" - direction: 1 - - hlk_fm22x.delete_all: +packages: + uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml -uart: - - id: uart_hlk_fm22x - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 - -hlk_fm22x: - on_face_scan_matched: - - logger.log: test_hlk_22x_face_scan_matched - on_face_scan_unmatched: - - logger.log: test_hlk_22x_face_scan_unmatched - on_face_scan_invalid: - - logger.log: test_hlk_22x_face_scan_invalid - on_face_info: - - logger.log: test_hlk_22x_face_info - on_enrollment_done: - - logger.log: test_hlk_22x_enrollment_done - on_enrollment_failed: - - logger.log: test_hlk_22x_enrollment_failed - -sensor: - - platform: hlk_fm22x - face_count: - name: "Face Count" - last_face_id: - name: "Last Face ID" - status: - name: "Face Status" - -binary_sensor: - - platform: hlk_fm22x - name: "Face Enrolling" - -text_sensor: - - platform: hlk_fm22x - version: - name: "HLK Version" - last_face_name: - name: "Last Face Name" +<<: !include common.yaml diff --git a/tests/components/hlk_fm22x/test.rp2040-ard.yaml b/tests/components/hlk_fm22x/test.rp2040-ard.yaml index 680047834c..f1df2daf83 100644 --- a/tests/components/hlk_fm22x/test.rp2040-ard.yaml +++ b/tests/components/hlk_fm22x/test.rp2040-ard.yaml @@ -1,47 +1,4 @@ -esphome: - on_boot: - then: - - hlk_fm22x.enroll: - name: "Test" - direction: 1 - - hlk_fm22x.delete_all: +packages: + uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml -uart: - - id: uart_hlk_fm22x - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 - -hlk_fm22x: - on_face_scan_matched: - - logger.log: test_hlk_22x_face_scan_matched - on_face_scan_unmatched: - - logger.log: test_hlk_22x_face_scan_unmatched - on_face_scan_invalid: - - logger.log: test_hlk_22x_face_scan_invalid - on_face_info: - - logger.log: test_hlk_22x_face_info - on_enrollment_done: - - logger.log: test_hlk_22x_enrollment_done - on_enrollment_failed: - - logger.log: test_hlk_22x_enrollment_failed - -sensor: - - platform: hlk_fm22x - face_count: - name: "Face Count" - last_face_id: - name: "Last Face ID" - status: - name: "Face Status" - -binary_sensor: - - platform: hlk_fm22x - name: "Face Enrolling" - -text_sensor: - - platform: hlk_fm22x - version: - name: "HLK Version" - last_face_name: - name: "Last Face Name" +<<: !include common.yaml diff --git a/tests/components/speaker/common.yaml b/tests/components/speaker/common.yaml index fa54fa7e39..9aaf639162 100644 --- a/tests/components/speaker/common.yaml +++ b/tests/components/speaker/common.yaml @@ -11,26 +11,42 @@ esphome: on_boot: then: - speaker.mute_on: + id: speaker_id - speaker.mute_off: + id: speaker_id - if: - condition: speaker.is_stopped + condition: + speaker.is_stopped: + id: speaker_id then: - - speaker.play: [0, 1, 2, 3] - - speaker.volume_set: 0.9 + - speaker.play: + id: speaker_id + data: [0, 1, 2, 3] + - speaker.volume_set: + id: speaker_id + volume: 0.9 - if: - condition: speaker.is_playing + condition: + speaker.is_playing: + id: speaker_id then: - speaker.finish: + id: speaker_id - speaker.stop: + id: speaker_id button: - platform: template name: "Speaker Button" on_press: then: - - speaker.play: [0x10, 0x20, 0x30, 0x40] - - speaker.play: !lambda |- - return {0x01, 0x02, (uint8_t)id(my_number).state}; + - speaker.play: + id: speaker_id + data: [0x10, 0x20, 0x30, 0x40] + - speaker.play: + id: speaker_id + data: !lambda |- + return {0x01, 0x02, (uint8_t)id(my_number).state}; i2s_audio: i2s_lrclk_pin: ${i2s_bclk_pin} diff --git a/tests/components/toshiba/common_ras2819t.yaml b/tests/components/toshiba/common_ras2819t.yaml index 32081fca98..157456ba81 100644 --- a/tests/components/toshiba/common_ras2819t.yaml +++ b/tests/components/toshiba/common_ras2819t.yaml @@ -1,11 +1,3 @@ -remote_transmitter: - pin: ${tx_pin} - carrier_duty_percent: 50% - -remote_receiver: - id: rcvr - pin: ${rx_pin} - climate: - platform: toshiba name: "RAS-2819T Climate" diff --git a/tests/components/toshiba/test_ras2819t.esp32-ard.yaml b/tests/components/toshiba/test_ras2819t.esp32-ard.yaml index 00805baa01..d82ba54897 100644 --- a/tests/components/toshiba/test_ras2819t.esp32-ard.yaml +++ b/tests/components/toshiba/test_ras2819t.esp32-ard.yaml @@ -1,5 +1,5 @@ -substitutions: - tx_pin: GPIO5 - rx_pin: GPIO4 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-ard.yaml + remote_receiver: !include ../../test_build_components/common/remote_receiver/esp32-ard.yaml <<: !include common_ras2819t.yaml diff --git a/tests/components/toshiba/test_ras2819t.esp32-c3-ard.yaml b/tests/components/toshiba/test_ras2819t.esp32-c3-ard.yaml index 00805baa01..6858dd587f 100644 --- a/tests/components/toshiba/test_ras2819t.esp32-c3-ard.yaml +++ b/tests/components/toshiba/test_ras2819t.esp32-c3-ard.yaml @@ -1,5 +1,5 @@ -substitutions: - tx_pin: GPIO5 - rx_pin: GPIO4 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-ard.yaml + remote_receiver: !include ../../test_build_components/common/remote_receiver/esp32-c3-ard.yaml <<: !include common_ras2819t.yaml diff --git a/tests/components/toshiba/test_ras2819t.esp32-idf.yaml b/tests/components/toshiba/test_ras2819t.esp32-idf.yaml index 00805baa01..3facc5bbb3 100644 --- a/tests/components/toshiba/test_ras2819t.esp32-idf.yaml +++ b/tests/components/toshiba/test_ras2819t.esp32-idf.yaml @@ -1,5 +1,5 @@ -substitutions: - tx_pin: GPIO5 - rx_pin: GPIO4 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp32-idf.yaml + remote_receiver: !include ../../test_build_components/common/remote_receiver/esp32-idf.yaml <<: !include common_ras2819t.yaml diff --git a/tests/components/toshiba/test_ras2819t.esp8266-ard.yaml b/tests/components/toshiba/test_ras2819t.esp8266-ard.yaml index 00805baa01..3976dcc739 100644 --- a/tests/components/toshiba/test_ras2819t.esp8266-ard.yaml +++ b/tests/components/toshiba/test_ras2819t.esp8266-ard.yaml @@ -1,5 +1,5 @@ -substitutions: - tx_pin: GPIO5 - rx_pin: GPIO4 +packages: + remote_transmitter: !include ../../test_build_components/common/remote_transmitter/esp8266-ard.yaml + remote_receiver: !include ../../test_build_components/common/remote_receiver/esp8266-ard.yaml <<: !include common_ras2819t.yaml diff --git a/tests/test_build_components/common/remote_receiver/esp32-ard.yaml b/tests/test_build_components/common/remote_receiver/esp32-ard.yaml new file mode 100644 index 0000000000..af5c2f2409 --- /dev/null +++ b/tests/test_build_components/common/remote_receiver/esp32-ard.yaml @@ -0,0 +1,12 @@ +# Common remote_receiver configuration for ESP32 Arduino tests +# Provides a shared remote receiver that all components can use +# Components will auto-use this receiver if they don't specify receiver_id + +substitutions: + remote_receiver_pin: GPIO32 + +remote_receiver: + - id: rcvr + pin: ${remote_receiver_pin} + dump: all + tolerance: 25% diff --git a/tests/test_build_components/common/remote_receiver/esp32-c3-ard.yaml b/tests/test_build_components/common/remote_receiver/esp32-c3-ard.yaml new file mode 100644 index 0000000000..26b288b427 --- /dev/null +++ b/tests/test_build_components/common/remote_receiver/esp32-c3-ard.yaml @@ -0,0 +1,12 @@ +# Common remote_receiver configuration for ESP32-C3 Arduino tests +# Provides a shared remote receiver that all components can use +# Components will auto-use this receiver if they don't specify receiver_id + +substitutions: + remote_receiver_pin: GPIO10 + +remote_receiver: + - id: rcvr + pin: ${remote_receiver_pin} + dump: all + tolerance: 25% From 463a00b1aca9fd7a44bb91a50418ce5a0bef48a3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:10:29 +1300 Subject: [PATCH 466/526] [CI] Don't request codeowners review in forks (#11827) --- .github/workflows/codeowner-review-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeowner-review-request.yml b/.github/workflows/codeowner-review-request.yml index 563d55f42b..6f4351b298 100644 --- a/.github/workflows/codeowner-review-request.yml +++ b/.github/workflows/codeowner-review-request.yml @@ -21,7 +21,7 @@ permissions: jobs: request-codeowner-reviews: name: Run - if: ${{ !github.event.pull_request.draft }} + if: ${{ github.repository == 'esphome/esphome' && !github.event.pull_request.draft }} runs-on: ubuntu-latest steps: - name: Request reviews from component codeowners From 1539b4307469b59fe654b940920a2863dad44d8a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:17:16 +1000 Subject: [PATCH 467/526] [wifi][ethernet] Don't block setup until connected (#9823) Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston --- esphome/components/ethernet/ethernet_component.cpp | 2 -- esphome/components/ethernet/ethernet_component.h | 1 - esphome/components/wifi/wifi_component.cpp | 6 ------ esphome/components/wifi/wifi_component.h | 2 -- 4 files changed, 11 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 893d0285be..5888ddce60 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -418,8 +418,6 @@ void EthernetComponent::dump_config() { float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; } -bool EthernetComponent::can_proceed() { return this->is_connected(); } - network::IPAddresses EthernetComponent::get_ip_addresses() { network::IPAddresses addresses; esp_netif_ip_info_t ip; diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 31f9fa360a..f1f0ac9cb8 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -58,7 +58,6 @@ class EthernetComponent : public Component { void loop() override; void dump_config() override; float get_setup_priority() const override; - bool can_proceed() override; void on_powerdown() override { powerdown(); } bool is_connected(); diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 885288f46a..7279e0c783 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1443,12 +1443,6 @@ void WiFiComponent::retry_connect() { this->action_started_ = millis(); } -bool WiFiComponent::can_proceed() { - if (!this->has_sta() || this->state_ == WIFI_COMPONENT_STATE_DISABLED || this->ap_setup_) { - return true; - } - return this->is_connected(); -} void WiFiComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; } bool WiFiComponent::is_connected() { return this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTED && diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 1cdf3234c7..ed049544cf 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -269,8 +269,6 @@ class WiFiComponent : public Component { void retry_connect(); - bool can_proceed() override; - void set_reboot_timeout(uint32_t reboot_timeout); bool is_connected(); From 7a700ca0779367b6558489af364915f7bfd4eb9f Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:15:44 +1000 Subject: [PATCH 468/526] [core] Update clamp functions to allow mixed but comparable types (#11828) --- esphome/core/helpers.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 48af7f674a..52a0746057 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1174,12 +1174,18 @@ template using ExternalRAMAllocator = RAMAllocator; * Functions to constrain the range of arithmetic values. */ -template T clamp_at_least(T value, T min) { +template +concept comparable_with = requires(T a, U b) { + { a > b } -> std::convertible_to; + { a < b } -> std::convertible_to; +}; + +template U> T clamp_at_least(T value, U min) { if (value < min) return min; return value; } -template T clamp_at_most(T value, T max) { +template U> T clamp_at_most(T value, U max) { if (value > max) return max; return value; From a6b7c1f18c933a8f9d0dc2b94ea492cfff70ef39 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 11 Nov 2025 16:17:25 +0100 Subject: [PATCH 469/526] [nrf52,gpio] add gpio levels for high voltage mode (#9858) Co-authored-by: J. Nick Koston --- esphome/components/nrf52/__init__.py | 26 +++++ esphome/components/nrf52/uicr.cpp | 110 ++++++++++++++++++ esphome/core/defines.h | 2 + .../components/nrf52/test.nrf52-adafruit.yaml | 3 + tests/components/nrf52/test.nrf52-mcumgr.yaml | 4 + .../components/nrf52/test.nrf52-xiao-ble.yaml | 2 + 6 files changed, 147 insertions(+) create mode 100644 esphome/components/nrf52/uicr.cpp diff --git a/esphome/components/nrf52/__init__.py b/esphome/components/nrf52/__init__.py index ace324c1f5..9566263c7c 100644 --- a/esphome/components/nrf52/__init__.py +++ b/esphome/components/nrf52/__init__.py @@ -25,6 +25,7 @@ from esphome.const import ( CONF_FRAMEWORK, CONF_ID, CONF_RESET_PIN, + CONF_VOLTAGE, KEY_CORE, KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, @@ -102,6 +103,11 @@ nrf52_ns = cg.esphome_ns.namespace("nrf52") DeviceFirmwareUpdate = nrf52_ns.class_("DeviceFirmwareUpdate", cg.Component) CONF_DFU = "dfu" +CONF_REG0 = "reg0" +CONF_UICR_ERASE = "uicr_erase" + +VOLTAGE_LEVELS = [1.8, 2.1, 2.4, 2.7, 3.0, 3.3] +DEFAULT_VOLTAGE_LEVEL = "default" CONFIG_SCHEMA = cv.All( _detect_bootloader, @@ -116,6 +122,18 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema, } ), + cv.Optional(CONF_REG0): cv.Schema( + { + cv.Required(CONF_VOLTAGE): cv.Any( + cv.All( + cv.voltage, + cv.one_of(*VOLTAGE_LEVELS, float=True), + ), + cv.one_of(*[DEFAULT_VOLTAGE_LEVEL], lower=True), + ), + cv.Optional(CONF_UICR_ERASE, default=False): cv.boolean, + } + ), } ), ) @@ -183,6 +201,14 @@ async def to_code(config: ConfigType) -> None: if dfu_config := config.get(CONF_DFU): CORE.add_job(_dfu_to_code, dfu_config) + if reg0_config := config.get(CONF_REG0): + value = 7 # DEFAULT_VOLTAGE_LEVEL + if reg0_config[CONF_VOLTAGE] in VOLTAGE_LEVELS: + value = VOLTAGE_LEVELS.index(reg0_config[CONF_VOLTAGE]) + cg.add_define("USE_NRF52_REG0_VOUT", value) + if reg0_config[CONF_UICR_ERASE]: + cg.add_define("USE_NRF52_UICR_ERASE") + @coroutine_with_priority(CoroPriority.DIAGNOSTICS) async def _dfu_to_code(dfu_config): diff --git a/esphome/components/nrf52/uicr.cpp b/esphome/components/nrf52/uicr.cpp new file mode 100644 index 0000000000..22714b7e50 --- /dev/null +++ b/esphome/components/nrf52/uicr.cpp @@ -0,0 +1,110 @@ +#include "esphome/core/defines.h" + +#ifdef USE_NRF52_REG0_VOUT +#include +#include +#include + +extern "C" { +void nvmc_config(uint32_t mode); +void nvmc_wait(); +nrfx_err_t nrfx_nvmc_uicr_erase(); +} + +namespace esphome::nrf52 { + +enum class StatusFlags : uint8_t { + OK = 0x00, + NEED_RESET = 0x01, + NEED_ERASE = 0x02, +}; + +constexpr StatusFlags &operator|=(StatusFlags &a, StatusFlags b) { + a = static_cast(static_cast(a) | static_cast(b)); + return a; +} + +constexpr bool operator&(StatusFlags a, StatusFlags b) { + return (static_cast(a) & static_cast(b)) != 0; +} + +static bool regout0_ok() { + return (NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) == (USE_NRF52_REG0_VOUT << UICR_REGOUT0_VOUT_Pos); +} + +static StatusFlags set_regout0() { + /* If the board is powered from USB (high voltage mode), + * GPIO output voltage is set to 1.8 volts by default. + */ + if (!regout0_ok()) { + nvmc_config(NVMC_CONFIG_WEN_Wen); + NRF_UICR->REGOUT0 = + (NRF_UICR->REGOUT0 & ~((uint32_t) UICR_REGOUT0_VOUT_Msk)) | (USE_NRF52_REG0_VOUT << UICR_REGOUT0_VOUT_Pos); + nvmc_wait(); + nvmc_config(NVMC_CONFIG_WEN_Ren); + return regout0_ok() ? StatusFlags::NEED_RESET : StatusFlags::NEED_ERASE; + } + return StatusFlags::OK; +} + +#ifndef USE_BOOTLOADER_MCUBOOT +// https://github.com/adafruit/Adafruit_nRF52_Bootloader/blob/6a9a6a3e6d0f86918e9286188426a279976645bd/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h#L61 +constexpr uint32_t BOOTLOADER_REGION_START = 0x000F4000; +constexpr uint32_t BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS = 0x000FE000; + +static bool bootloader_ok() { + return NRF_UICR->NRFFW[0] == BOOTLOADER_REGION_START && NRF_UICR->NRFFW[1] == BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS; +} + +static StatusFlags fix_bootloader() { + if (!bootloader_ok()) { + nvmc_config(NVMC_CONFIG_WEN_Wen); + NRF_UICR->NRFFW[0] = BOOTLOADER_REGION_START; + NRF_UICR->NRFFW[1] = BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS; + nvmc_wait(); + nvmc_config(NVMC_CONFIG_WEN_Ren); + return bootloader_ok() ? StatusFlags::NEED_RESET : StatusFlags::NEED_ERASE; + } + return StatusFlags::OK; +} +#endif + +static StatusFlags set_uicr() { + StatusFlags status = StatusFlags::OK; + status |= set_regout0(); +#ifndef USE_BOOTLOADER_MCUBOOT + status |= fix_bootloader(); +#endif + return status; +} + +static int board_esphome_init() { + StatusFlags status = set_uicr(); + +#ifdef USE_NRF52_UICR_ERASE + if (status & StatusFlags::NEED_ERASE) { + nrfx_err_t ret = nrfx_nvmc_uicr_erase(); + if (ret != NRFX_SUCCESS) { +#ifdef CONFIG_PRINTK + printk("nrfx_nvmc_uicr_erase failed %d\n", ret); +#endif + } else { + status |= set_uicr(); + } + } +#endif + + if (status & StatusFlags::NEED_RESET) { + /* a reset is required for changes to take effect */ + NVIC_SystemReset(); + } + + return 0; +} +} // namespace esphome::nrf52 + +static int board_esphome_init() { return esphome::nrf52::board_esphome_init(); } + +SYS_INIT(board_esphome_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); + +#endif diff --git a/esphome/core/defines.h b/esphome/core/defines.h index ac725fbca9..c522a8ec62 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -287,6 +287,8 @@ #ifdef USE_NRF52 #define USE_NRF52_DFU +#define USE_NRF52_REG0_VOUT 5 +#define USE_NRF52_UICR_ERASE #define USE_SOFTDEVICE_ID 7 #define USE_SOFTDEVICE_VERSION 1 #endif diff --git a/tests/components/nrf52/test.nrf52-adafruit.yaml b/tests/components/nrf52/test.nrf52-adafruit.yaml index cf704ecceb..72fd015953 100644 --- a/tests/components/nrf52/test.nrf52-adafruit.yaml +++ b/tests/components/nrf52/test.nrf52-adafruit.yaml @@ -15,3 +15,6 @@ nrf52: inverted: true mode: output: true + reg0: + voltage: 2.1V + uicr_erase: true diff --git a/tests/components/nrf52/test.nrf52-mcumgr.yaml b/tests/components/nrf52/test.nrf52-mcumgr.yaml index e69de29bb2..89ec637db6 100644 --- a/tests/components/nrf52/test.nrf52-mcumgr.yaml +++ b/tests/components/nrf52/test.nrf52-mcumgr.yaml @@ -0,0 +1,4 @@ +nrf52: + reg0: + voltage: 3.3V + uicr_erase: true diff --git a/tests/components/nrf52/test.nrf52-xiao-ble.yaml b/tests/components/nrf52/test.nrf52-xiao-ble.yaml index 3fe80209b6..c3c44902f0 100644 --- a/tests/components/nrf52/test.nrf52-xiao-ble.yaml +++ b/tests/components/nrf52/test.nrf52-xiao-ble.yaml @@ -5,3 +5,5 @@ nrf52: inverted: true mode: output: true + reg0: + voltage: default From a6b905e1488fb5bd9eefef426d45ddfd2b38700c Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 11 Nov 2025 18:50:07 +0100 Subject: [PATCH 470/526] [nrf52,pcf8563] fix build error (#11846) Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston --- esphome/components/ds1307/ds1307.cpp | 2 +- .../components/homeassistant/time/homeassistant_time.cpp | 6 ++---- esphome/components/pcf85063/pcf85063.cpp | 2 +- esphome/components/pcf8563/pcf8563.cpp | 2 +- esphome/components/rx8130/rx8130.cpp | 1 + esphome/components/sntp/sntp_component.cpp | 1 + esphome/components/time/real_time_clock.cpp | 7 +++++++ esphome/components/time/real_time_clock.h | 2 ++ tests/components/ds1307/test.nrf52-adafruit.yaml | 4 ++++ tests/components/pcf85063/test.nrf52-adafruit.yaml | 4 ++++ tests/components/pcf8563/test.nrf52-adafruit.yaml | 4 ++++ tests/components/rx8130/test.nrf52-adafruit.yaml | 4 ++++ 12 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 tests/components/ds1307/test.nrf52-adafruit.yaml create mode 100644 tests/components/pcf85063/test.nrf52-adafruit.yaml create mode 100644 tests/components/pcf8563/test.nrf52-adafruit.yaml create mode 100644 tests/components/rx8130/test.nrf52-adafruit.yaml diff --git a/esphome/components/ds1307/ds1307.cpp b/esphome/components/ds1307/ds1307.cpp index 077db497b1..adbd7b5487 100644 --- a/esphome/components/ds1307/ds1307.cpp +++ b/esphome/components/ds1307/ds1307.cpp @@ -23,7 +23,7 @@ void DS1307Component::dump_config() { if (this->is_failed()) { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } - ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); + RealTimeClock::dump_config(); } float DS1307Component::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/homeassistant/time/homeassistant_time.cpp b/esphome/components/homeassistant/time/homeassistant_time.cpp index 0a91a2f63d..e72c5a21f5 100644 --- a/esphome/components/homeassistant/time/homeassistant_time.cpp +++ b/esphome/components/homeassistant/time/homeassistant_time.cpp @@ -7,10 +7,8 @@ namespace homeassistant { static const char *const TAG = "homeassistant.time"; void HomeassistantTime::dump_config() { - ESP_LOGCONFIG(TAG, - "Home Assistant Time:\n" - " Timezone: '%s'", - this->timezone_.c_str()); + ESP_LOGCONFIG(TAG, "Home Assistant Time"); + RealTimeClock::dump_config(); } float HomeassistantTime::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/pcf85063/pcf85063.cpp b/esphome/components/pcf85063/pcf85063.cpp index cb987c6129..f38b60b55d 100644 --- a/esphome/components/pcf85063/pcf85063.cpp +++ b/esphome/components/pcf85063/pcf85063.cpp @@ -23,7 +23,7 @@ void PCF85063Component::dump_config() { if (this->is_failed()) { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } - ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); + RealTimeClock::dump_config(); } float PCF85063Component::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/pcf8563/pcf8563.cpp b/esphome/components/pcf8563/pcf8563.cpp index 27020378a6..2090936bb6 100644 --- a/esphome/components/pcf8563/pcf8563.cpp +++ b/esphome/components/pcf8563/pcf8563.cpp @@ -23,7 +23,7 @@ void PCF8563Component::dump_config() { if (this->is_failed()) { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } - ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); + RealTimeClock::dump_config(); } float PCF8563Component::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/rx8130/rx8130.cpp b/esphome/components/rx8130/rx8130.cpp index cf6ea3e6e6..ba092a4834 100644 --- a/esphome/components/rx8130/rx8130.cpp +++ b/esphome/components/rx8130/rx8130.cpp @@ -62,6 +62,7 @@ void RX8130Component::update() { this->read_time(); } void RX8130Component::dump_config() { ESP_LOGCONFIG(TAG, "RX8130:"); LOG_I2C_DEVICE(this); + RealTimeClock::dump_config(); } void RX8130Component::read_time() { diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 331a9b3509..c4d78b6e0b 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -61,6 +61,7 @@ void SNTPComponent::dump_config() { for (auto &server : this->servers_) { ESP_LOGCONFIG(TAG, " Server %zu: '%s'", i++, server); } + RealTimeClock::dump_config(); } void SNTPComponent::update() { #if !defined(USE_ESP32) diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index 42c564659f..175cee0c1f 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -23,6 +23,13 @@ namespace time { static const char *const TAG = "time"; RealTimeClock::RealTimeClock() = default; + +void RealTimeClock::dump_config() { +#ifdef USE_TIME_TIMEZONE + ESP_LOGCONFIG(TAG, "Timezone: '%s'", this->timezone_.c_str()); +#endif +} + void RealTimeClock::synchronize_epoch_(uint32_t epoch) { ESP_LOGVV(TAG, "Got epoch %" PRIu32, epoch); // Update UTC epoch time. diff --git a/esphome/components/time/real_time_clock.h b/esphome/components/time/real_time_clock.h index bbcecaa628..2f17bd86d6 100644 --- a/esphome/components/time/real_time_clock.h +++ b/esphome/components/time/real_time_clock.h @@ -52,6 +52,8 @@ class RealTimeClock : public PollingComponent { this->time_sync_callback_.add(std::move(callback)); }; + void dump_config() override; + protected: /// Report a unix epoch as current time. void synchronize_epoch_(uint32_t epoch); diff --git a/tests/components/ds1307/test.nrf52-adafruit.yaml b/tests/components/ds1307/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..2a0de6241c --- /dev/null +++ b/tests/components/ds1307/test.nrf52-adafruit.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml diff --git a/tests/components/pcf85063/test.nrf52-adafruit.yaml b/tests/components/pcf85063/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..2a0de6241c --- /dev/null +++ b/tests/components/pcf85063/test.nrf52-adafruit.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml diff --git a/tests/components/pcf8563/test.nrf52-adafruit.yaml b/tests/components/pcf8563/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..2a0de6241c --- /dev/null +++ b/tests/components/pcf8563/test.nrf52-adafruit.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml diff --git a/tests/components/rx8130/test.nrf52-adafruit.yaml b/tests/components/rx8130/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..2a0de6241c --- /dev/null +++ b/tests/components/rx8130/test.nrf52-adafruit.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml From 661920c51edcaaf3c8c62d86499f79388a063d8d Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 11 Nov 2025 19:18:17 +0100 Subject: [PATCH 471/526] [nrf52,ssd1306_i2c] fix build error (#11847) --- esphome/core/macros.h | 4 ++++ tests/components/ssd1306_i2c/test.nrf52-xiao-ble.yaml | 7 +++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/components/ssd1306_i2c/test.nrf52-xiao-ble.yaml diff --git a/esphome/core/macros.h b/esphome/core/macros.h index 8b2383321b..2e47453c40 100644 --- a/esphome/core/macros.h +++ b/esphome/core/macros.h @@ -6,3 +6,7 @@ #ifdef USE_ARDUINO #include #endif + +#ifdef USE_ZEPHYR +#define M_PI 3.14159265358979323846 +#endif diff --git a/tests/components/ssd1306_i2c/test.nrf52-xiao-ble.yaml b/tests/components/ssd1306_i2c/test.nrf52-xiao-ble.yaml new file mode 100644 index 0000000000..28254e4af5 --- /dev/null +++ b/tests/components/ssd1306_i2c/test.nrf52-xiao-ble.yaml @@ -0,0 +1,7 @@ +substitutions: + reset_pin: P0.10 + +packages: + i2c: !include ../../test_build_components/common/i2c/nrf52.yaml + +<<: !include common.yaml From 7a92565a0c4d0290f55a63f3cfdad9da9ec5fe17 Mon Sep 17 00:00:00 2001 From: CzBiX Date: Wed, 12 Nov 2025 03:24:52 +0800 Subject: [PATCH 472/526] [lvgl] Fix compile when using transform_zoom (#11845) --- esphome/components/lvgl/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 4df68a6386..2a24f343c3 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -331,7 +331,7 @@ async def to_code(configs): # This must be done after all widgets are created for comp in helpers.lvgl_components_required: cg.add_define(f"USE_LVGL_{comp.upper()}") - if "transform_angle" in styles_used: + if {"transform_angle", "transform_zoom"} & styles_used: add_define("LV_COLOR_SCREEN_TRANSP", "1") for use in helpers.lv_uses: add_define(f"LV_USE_{use.upper()}") From 80a7c6d3c31b172a34ba0203785b128b146d92a6 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 11 Nov 2025 21:52:41 +0100 Subject: [PATCH 473/526] [nrf52,debug] add partition dump (#11839) Co-authored-by: J. Nick Koston --- esphome/components/debug/debug_component.cpp | 6 ++-- esphome/components/debug/debug_component.h | 8 ++--- esphome/components/debug/debug_zephyr.cpp | 32 +++++++++++++++++++ .../components/debug/test.nrf52-xiao-ble.yaml | 1 + 4 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 tests/components/debug/test.nrf52-xiao-ble.yaml diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index ade0968e08..f54bf82eae 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -49,9 +49,9 @@ void DebugComponent::dump_config() { } #endif // USE_TEXT_SENSOR -#ifdef USE_ESP32 - this->log_partition_info_(); // Log partition information for ESP32 -#endif // USE_ESP32 +#if defined(USE_ESP32) || defined(USE_ZEPHYR) + this->log_partition_info_(); // Log partition information +#endif } void DebugComponent::loop() { diff --git a/esphome/components/debug/debug_component.h b/esphome/components/debug/debug_component.h index efd0dafab0..96306f7cdf 100644 --- a/esphome/components/debug/debug_component.h +++ b/esphome/components/debug/debug_component.h @@ -62,19 +62,19 @@ class DebugComponent : public PollingComponent { sensor::Sensor *cpu_frequency_sensor_{nullptr}; #endif // USE_SENSOR -#ifdef USE_ESP32 +#if defined(USE_ESP32) || defined(USE_ZEPHYR) /** * @brief Logs information about the device's partition table. * - * This function iterates through the ESP32's partition table and logs details + * This function iterates through the partition table and logs details * about each partition, including its name, type, subtype, starting address, * and size. The information is useful for diagnosing issues related to flash * memory or verifying the partition configuration dynamically at runtime. * - * Only available when compiled for ESP32 platforms. + * Only available when compiled for ESP32 and ZEPHYR platforms. */ void log_partition_info_(); -#endif // USE_ESP32 +#endif #ifdef USE_TEXT_SENSOR text_sensor::TextSensor *device_info_{nullptr}; diff --git a/esphome/components/debug/debug_zephyr.cpp b/esphome/components/debug/debug_zephyr.cpp index 62fa391e5f..c888c41a78 100644 --- a/esphome/components/debug/debug_zephyr.cpp +++ b/esphome/components/debug/debug_zephyr.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #define BOOTLOADER_VERSION_REGISTER NRF_TIMER2->CC[0] @@ -86,6 +87,37 @@ std::string DebugComponent::get_reset_reason_() { uint32_t DebugComponent::get_free_heap_() { return INT_MAX; } +static void fa_cb(const struct flash_area *fa, void *user_data) { +#if CONFIG_FLASH_MAP_LABELS + const char *fa_label = flash_area_label(fa); + + if (fa_label == nullptr) { + fa_label = "-"; + } + ESP_LOGCONFIG(TAG, "%2d 0x%0*" PRIxPTR " %-26s %-24.24s 0x%-10x 0x%-12x", (int) fa->fa_id, + sizeof(uintptr_t) * 2, (uintptr_t) fa->fa_dev, fa->fa_dev->name, fa_label, (uint32_t) fa->fa_off, + fa->fa_size); +#else + ESP_LOGCONFIG(TAG, "%2d 0x%0*" PRIxPTR " %-26s 0x%-10x 0x%-12x", (int) fa->fa_id, sizeof(uintptr_t) * 2, + (uintptr_t) fa->fa_dev, fa->fa_dev->name, (uint32_t) fa->fa_off, fa->fa_size); +#endif +} + +void DebugComponent::log_partition_info_() { +#if CONFIG_FLASH_MAP_LABELS + ESP_LOGCONFIG(TAG, "ID | Device | Device Name " + "| Label | Offset | Size"); + ESP_LOGCONFIG(TAG, "--------------------------------------------" + "-----------------------------------------------"); +#else + ESP_LOGCONFIG(TAG, "ID | Device | Device Name " + "| Offset | Size"); + ESP_LOGCONFIG(TAG, "-----------------------------------------" + "------------------------------"); +#endif + flash_area_foreach(fa_cb, nullptr); +} + void DebugComponent::get_device_info_(std::string &device_info) { std::string supply = "Main supply status: "; if (nrf_power_mainregstatus_get(NRF_POWER) == NRF_POWER_MAINREGSTATUS_NORMAL) { diff --git a/tests/components/debug/test.nrf52-xiao-ble.yaml b/tests/components/debug/test.nrf52-xiao-ble.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/debug/test.nrf52-xiao-ble.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 2f91e7bd47e96a9542e73764b67d4b77ad48f29e Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 11 Nov 2025 22:33:53 +0100 Subject: [PATCH 474/526] [nrf52] fix boot loop (#11854) --- esphome/components/nrf52/__init__.py | 14 ++++---------- esphome/components/nrf52/uicr.cpp | 13 ++++++++++++- tests/components/nrf52/test.nrf52-xiao-ble.yaml | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/esphome/components/nrf52/__init__.py b/esphome/components/nrf52/__init__.py index 9566263c7c..a3b79bf139 100644 --- a/esphome/components/nrf52/__init__.py +++ b/esphome/components/nrf52/__init__.py @@ -107,7 +107,6 @@ CONF_REG0 = "reg0" CONF_UICR_ERASE = "uicr_erase" VOLTAGE_LEVELS = [1.8, 2.1, 2.4, 2.7, 3.0, 3.3] -DEFAULT_VOLTAGE_LEVEL = "default" CONFIG_SCHEMA = cv.All( _detect_bootloader, @@ -124,12 +123,9 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_REG0): cv.Schema( { - cv.Required(CONF_VOLTAGE): cv.Any( - cv.All( - cv.voltage, - cv.one_of(*VOLTAGE_LEVELS, float=True), - ), - cv.one_of(*[DEFAULT_VOLTAGE_LEVEL], lower=True), + cv.Required(CONF_VOLTAGE): cv.All( + cv.voltage, + cv.one_of(*VOLTAGE_LEVELS, float=True), ), cv.Optional(CONF_UICR_ERASE, default=False): cv.boolean, } @@ -202,9 +198,7 @@ async def to_code(config: ConfigType) -> None: CORE.add_job(_dfu_to_code, dfu_config) if reg0_config := config.get(CONF_REG0): - value = 7 # DEFAULT_VOLTAGE_LEVEL - if reg0_config[CONF_VOLTAGE] in VOLTAGE_LEVELS: - value = VOLTAGE_LEVELS.index(reg0_config[CONF_VOLTAGE]) + value = VOLTAGE_LEVELS.index(reg0_config[CONF_VOLTAGE]) cg.add_define("USE_NRF52_REG0_VOUT", value) if reg0_config[CONF_UICR_ERASE]: cg.add_define("USE_NRF52_UICR_ERASE") diff --git a/esphome/components/nrf52/uicr.cpp b/esphome/components/nrf52/uicr.cpp index 22714b7e50..4c0beeb503 100644 --- a/esphome/components/nrf52/uicr.cpp +++ b/esphome/components/nrf52/uicr.cpp @@ -69,9 +69,20 @@ static StatusFlags fix_bootloader() { } #endif +#define BOOTLOADER_VERSION_REGISTER NRF_TIMER2->CC[0] + static StatusFlags set_uicr() { StatusFlags status = StatusFlags::OK; - status |= set_regout0(); +#ifndef USE_BOOTLOADER_MCUBOOT + if (BOOTLOADER_VERSION_REGISTER <= 0x902) { +#ifdef CONFIG_PRINTK + printk("cannot control regout0 for %#x\n", BOOTLOADER_VERSION_REGISTER); +#endif + } else +#endif + { + status |= set_regout0(); + } #ifndef USE_BOOTLOADER_MCUBOOT status |= fix_bootloader(); #endif diff --git a/tests/components/nrf52/test.nrf52-xiao-ble.yaml b/tests/components/nrf52/test.nrf52-xiao-ble.yaml index c3c44902f0..d53c692001 100644 --- a/tests/components/nrf52/test.nrf52-xiao-ble.yaml +++ b/tests/components/nrf52/test.nrf52-xiao-ble.yaml @@ -6,4 +6,4 @@ nrf52: mode: output: true reg0: - voltage: default + voltage: 1.8V From a2ec7f622c861f7e21843d26a10b573515be43f6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 11 Nov 2025 16:04:37 -0600 Subject: [PATCH 475/526] [wifi] Fix infinite retry loop when no hidden networks and captive portal active (#11831) --- esphome/components/wifi/wifi_component.cpp | 28 ++++++++++------------ 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 7279e0c783..49e433b468 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1163,11 +1163,9 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() { if (this->find_next_hidden_sta_(-1, !this->went_through_explicit_hidden_phase_()) >= 0) { return WiFiRetryPhase::RETRY_HIDDEN; // Found hidden networks to try } - // No hidden networks - skip directly to restart/rescan - if (this->is_captive_portal_active_() || this->is_esp32_improv_active_()) { - return this->went_through_explicit_hidden_phase_() ? WiFiRetryPhase::EXPLICIT_HIDDEN - : WiFiRetryPhase::SCAN_CONNECTING; - } + // No hidden networks - always go through RESTARTING_ADAPTER phase + // This ensures num_retried_ gets reset and a fresh scan is triggered + // The actual adapter restart will be skipped if captive portal/improv is active return WiFiRetryPhase::RESTARTING_ADAPTER; case WiFiRetryPhase::RETRY_HIDDEN: @@ -1183,16 +1181,9 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() { return WiFiRetryPhase::RETRY_HIDDEN; } } - // Exhausted all potentially hidden SSIDs - rescan to try next BSSID - // If captive portal/improv is active, skip adapter restart and go back to start - // Otherwise restart adapter to clear any stuck state - if (this->is_captive_portal_active_() || this->is_esp32_improv_active_()) { - // Go back to explicit hidden if we went through it initially, otherwise scan - return this->went_through_explicit_hidden_phase_() ? WiFiRetryPhase::EXPLICIT_HIDDEN - : WiFiRetryPhase::SCAN_CONNECTING; - } - - // Restart adapter + // Exhausted all potentially hidden SSIDs - always go through RESTARTING_ADAPTER + // This ensures num_retried_ gets reset and a fresh scan is triggered + // The actual adapter restart will be skipped if captive portal/improv is active return WiFiRetryPhase::RESTARTING_ADAPTER; case WiFiRetryPhase::RESTARTING_ADAPTER: @@ -1280,7 +1271,12 @@ bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) { break; case WiFiRetryPhase::RESTARTING_ADAPTER: - this->restart_adapter(); + // Skip actual adapter restart if captive portal/improv is active + // This allows state machine to reset num_retried_ and trigger fresh scan + // without disrupting the captive portal/improv connection + if (!this->is_captive_portal_active_() && !this->is_esp32_improv_active_()) { + this->restart_adapter(); + } // Return true to indicate we should wait (go to COOLDOWN) instead of immediately connecting return true; From ef04903a7a5e4665c13a2b4208e99ef976db1ab6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 11 Nov 2025 16:10:17 -0600 Subject: [PATCH 476/526] [wifi] Change priority type from float to int8_t (#11830) --- esphome/components/wifi/__init__.py | 2 +- esphome/components/wifi/wifi_component.cpp | 53 ++++++++++++++++++---- esphome/components/wifi/wifi_component.h | 24 +++++----- tests/components/wifi/common.yaml | 5 ++ 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 5f4190a933..358f920c2c 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -174,7 +174,7 @@ WIFI_NETWORK_STA = WIFI_NETWORK_BASE.extend( { cv.Optional(CONF_BSSID): cv.mac_address, cv.Optional(CONF_HIDDEN): cv.boolean, - cv.Optional(CONF_PRIORITY, default=0.0): cv.float_, + cv.Optional(CONF_PRIORITY, default=0): cv.int_range(min=-128, max=127), cv.Optional(CONF_EAP): EAP_AUTH_SCHEMA, } ) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 49e433b468..681555431e 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -667,7 +667,7 @@ void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &pa void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { // Log connection attempt at INFO level with priority std::string bssid_formatted; - float priority = 0.0f; + int8_t priority = 0; if (ap.get_bssid().has_value()) { bssid_formatted = format_mac_address_pretty(ap.get_bssid().value().data()); @@ -675,7 +675,7 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { } ESP_LOGI(TAG, - "Connecting to " LOG_SECRET("'%s'") " " LOG_SECRET("(%s)") " (priority %.1f, attempt %u/%u in phase %s)...", + "Connecting to " LOG_SECRET("'%s'") " " LOG_SECRET("(%s)") " (priority %d, attempt %u/%u in phase %s)...", ap.get_ssid().c_str(), ap.get_bssid().has_value() ? bssid_formatted.c_str() : LOG_STR_LITERAL("any"), priority, this->num_retried_ + 1, get_max_retries_for_phase(this->retry_phase_), LOG_STR_ARG(retry_phase_to_log_string(this->retry_phase_))); @@ -812,7 +812,7 @@ void WiFiComponent::print_connect_params_() { wifi_gateway_ip_().str().c_str(), wifi_dns_ip_(0).str().c_str(), wifi_dns_ip_(1).str().c_str()); #ifdef ESPHOME_LOG_HAS_VERBOSE if (const WiFiAP *config = this->get_selected_sta_(); config && config->get_bssid().has_value()) { - ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*config->get_bssid())); + ESP_LOGV(TAG, " Priority: %d", this->get_sta_priority(*config->get_bssid())); } #endif #ifdef USE_WIFI_11KV_SUPPORT @@ -933,8 +933,7 @@ __attribute__((noinline)) static void log_scan_result(const WiFiScanResult &res) ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), res.get_is_hidden() ? LOG_STR_LITERAL("(HIDDEN) ") : LOG_STR_LITERAL(""), bssid_s, LOG_STR_ARG(get_signal_bars(res.get_rssi()))); - ESP_LOGD(TAG, " Channel: %2u, RSSI: %3d dB, Priority: %4.1f", res.get_channel(), res.get_rssi(), - res.get_priority()); + ESP_LOGD(TAG, " Channel: %2u, RSSI: %3d dB, Priority: %4d", res.get_channel(), res.get_rssi(), res.get_priority()); } else { ESP_LOGD(TAG, "- " LOG_SECRET("'%s'") " " LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), bssid_s, LOG_STR_ARG(get_signal_bars(res.get_rssi()))); @@ -1063,6 +1062,9 @@ void WiFiComponent::check_connecting_finished() { this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTED; this->num_retried_ = 0; + // Clear priority tracking if all priorities are at minimum + this->clear_priorities_if_all_min_(); + #ifdef USE_WIFI_FAST_CONNECT this->save_fast_connect_settings_(); #endif @@ -1287,6 +1289,34 @@ bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) { return false; // Did not start scan, can proceed with connection } +/// Clear BSSID priority tracking if all priorities are at minimum (saves memory) +/// At minimum priority, all BSSIDs are equally bad, so priority tracking is useless +/// Called after successful connection or after failed connection attempts +void WiFiComponent::clear_priorities_if_all_min_() { + if (this->sta_priorities_.empty()) { + return; + } + + int8_t first_priority = this->sta_priorities_[0].priority; + + // Only clear if all priorities have been decremented to the minimum value + // At this point, all BSSIDs have been equally penalized and priority info is useless + if (first_priority != std::numeric_limits::min()) { + return; + } + + for (const auto &pri : this->sta_priorities_) { + if (pri.priority != first_priority) { + return; // Not all same, nothing to do + } + } + + // All priorities are at minimum - clear the vector to save memory and reset + ESP_LOGD(TAG, "Clearing BSSID priorities (all at minimum)"); + this->sta_priorities_.clear(); + this->sta_priorities_.shrink_to_fit(); +} + /// Log failed connection attempt and decrease BSSID priority to avoid repeated failures /// This function identifies which BSSID was attempted (from scan results or config), /// decreases its priority by 1.0 to discourage future attempts, and logs the change. @@ -1317,8 +1347,9 @@ void WiFiComponent::log_and_adjust_priority_for_failed_connect_() { } // Decrease priority to avoid repeatedly trying the same failed BSSID - float old_priority = this->get_sta_priority(failed_bssid.value()); - float new_priority = old_priority - 1.0f; + int8_t old_priority = this->get_sta_priority(failed_bssid.value()); + int8_t new_priority = + (old_priority > std::numeric_limits::min()) ? (old_priority - 1) : std::numeric_limits::min(); this->set_sta_priority(failed_bssid.value(), new_priority); // Get SSID for logging @@ -1329,8 +1360,12 @@ void WiFiComponent::log_and_adjust_priority_for_failed_connect_() { ssid = config->get_ssid(); } - ESP_LOGD(TAG, "Failed " LOG_SECRET("'%s'") " " LOG_SECRET("(%s)") ", priority %.1f → %.1f", ssid.c_str(), + ESP_LOGD(TAG, "Failed " LOG_SECRET("'%s'") " " LOG_SECRET("(%s)") ", priority %d → %d", ssid.c_str(), format_mac_address_pretty(failed_bssid.value().data()).c_str(), old_priority, new_priority); + + // After adjusting priority, check if all priorities are now at minimum + // If so, clear the vector to save memory and reset for fresh start + this->clear_priorities_if_all_min_(); } /// Handle target advancement or retry counter increment when staying in the same phase @@ -1543,9 +1578,9 @@ bool WiFiAP::get_hidden() const { return this->hidden_; } WiFiScanResult::WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth, bool is_hidden) : bssid_(bssid), - ssid_(std::move(ssid)), channel_(channel), rssi_(rssi), + ssid_(std::move(ssid)), with_auth_(with_auth), is_hidden_(is_hidden) {} bool WiFiScanResult::matches(const WiFiAP &config) const { diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index ed049544cf..b8223e8dc8 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -157,7 +157,7 @@ class WiFiAP { void set_eap(optional eap_auth); #endif // USE_WIFI_WPA2_EAP void set_channel(optional channel); - void set_priority(float priority) { priority_ = priority; } + void set_priority(int8_t priority) { priority_ = priority; } void set_manual_ip(optional manual_ip); void set_hidden(bool hidden); const std::string &get_ssid() const; @@ -167,7 +167,7 @@ class WiFiAP { const optional &get_eap() const; #endif // USE_WIFI_WPA2_EAP const optional &get_channel() const; - float get_priority() const { return priority_; } + int8_t get_priority() const { return priority_; } const optional &get_manual_ip() const; bool get_hidden() const; @@ -179,8 +179,8 @@ class WiFiAP { optional eap_; #endif // USE_WIFI_WPA2_EAP optional manual_ip_; - float priority_{0}; optional channel_; + int8_t priority_{0}; bool hidden_{false}; }; @@ -198,17 +198,17 @@ class WiFiScanResult { int8_t get_rssi() const; bool get_with_auth() const; bool get_is_hidden() const; - float get_priority() const { return priority_; } - void set_priority(float priority) { priority_ = priority; } + int8_t get_priority() const { return priority_; } + void set_priority(int8_t priority) { priority_ = priority; } bool operator==(const WiFiScanResult &rhs) const; protected: bssid_t bssid_; - std::string ssid_; - float priority_{0.0f}; uint8_t channel_; int8_t rssi_; + std::string ssid_; + int8_t priority_{0}; bool matches_{false}; bool with_auth_; bool is_hidden_; @@ -216,7 +216,7 @@ class WiFiScanResult { struct WiFiSTAPriority { bssid_t bssid; - float priority; + int8_t priority; }; enum WiFiPowerSaveMode : uint8_t { @@ -317,14 +317,14 @@ class WiFiComponent : public Component { } return false; } - float get_sta_priority(const bssid_t bssid) { + int8_t get_sta_priority(const bssid_t bssid) { for (auto &it : this->sta_priorities_) { if (it.bssid == bssid) return it.priority; } - return 0.0f; + return 0; } - void set_sta_priority(const bssid_t bssid, float priority) { + void set_sta_priority(const bssid_t bssid, int8_t priority) { for (auto &it : this->sta_priorities_) { if (it.bssid == bssid) { it.priority = priority; @@ -383,6 +383,8 @@ class WiFiComponent : public Component { int8_t find_next_hidden_sta_(int8_t start_index, bool include_explicit_hidden = true); /// Log failed connection and decrease BSSID priority to avoid repeated attempts void log_and_adjust_priority_for_failed_connect_(); + /// Clear BSSID priority tracking if all priorities are at minimum (saves memory) + void clear_priorities_if_all_min_(); /// Advance to next target (AP/SSID) within current phase, or increment retry counter /// Called when staying in the same phase after a failed connection attempt void advance_to_next_target_or_increment_retry_(); diff --git a/tests/components/wifi/common.yaml b/tests/components/wifi/common.yaml index af27f85092..5d9973cbc8 100644 --- a/tests/components/wifi/common.yaml +++ b/tests/components/wifi/common.yaml @@ -15,5 +15,10 @@ wifi: networks: - ssid: MySSID password: password1 + priority: 10 - ssid: MySSID2 password: password2 + priority: 5 + - ssid: MySSID3 + password: password3 + priority: 0 From 00c71b7236a1ce891f8cc6721240b72897757a46 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 11 Nov 2025 16:33:37 -0600 Subject: [PATCH 477/526] [wifi] Fix all-hidden networks duplicate attempts and scan skipping (#11848) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/wifi/wifi_component.cpp | 39 ++++++++++++++++------ esphome/components/wifi/wifi_component.h | 3 +- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 681555431e..d75ac971eb 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -253,17 +253,19 @@ bool WiFiComponent::ssid_was_seen_in_scan_(const std::string &ssid) const { return false; } -int8_t WiFiComponent::find_next_hidden_sta_(int8_t start_index, bool include_explicit_hidden) { +int8_t WiFiComponent::find_next_hidden_sta_(int8_t start_index) { // Find next SSID that wasn't in scan results (might be hidden) + bool include_explicit_hidden = !this->went_through_explicit_hidden_phase_(); // Start searching from start_index + 1 for (size_t i = start_index + 1; i < this->sta_.size(); i++) { const auto &sta = this->sta_[i]; // Skip networks that were already tried in EXPLICIT_HIDDEN phase // Those are: networks marked hidden:true that appear before the first non-hidden network + // If all networks are hidden (first_non_hidden_idx == -1), skip all of them if (!include_explicit_hidden && sta.get_hidden()) { int8_t first_non_hidden_idx = this->find_first_non_hidden_index_(); - if (first_non_hidden_idx >= 0 && static_cast(i) < first_non_hidden_idx) { + if (first_non_hidden_idx < 0 || static_cast(i) < first_non_hidden_idx) { ESP_LOGD(TAG, "Skipping " LOG_SECRET("'%s'") " (explicit hidden, already tried)", sta.get_ssid().c_str()); continue; } @@ -1002,6 +1004,12 @@ void WiFiComponent::check_scanning_finished() { // No scan results matched our configured networks - transition directly to hidden mode // Don't call retry_connect() since we never attempted a connection (no BSSID to penalize) this->transition_to_phase_(WiFiRetryPhase::RETRY_HIDDEN); + // If no hidden networks to try, skip connection attempt (will be handled on next loop) + if (this->selected_sta_index_ == -1) { + this->state_ = WIFI_COMPONENT_STATE_COOLDOWN; + this->action_started_ = millis(); + return; + } // Now start connection attempt in hidden mode } else if (this->transition_to_phase_(WiFiRetryPhase::SCAN_CONNECTING)) { return; // scan started, wait for next loop iteration @@ -1144,7 +1152,12 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() { return WiFiRetryPhase::EXPLICIT_HIDDEN; } - // No more consecutive explicitly hidden networks - proceed to scanning + // No more consecutive explicitly hidden networks + // If ALL networks are hidden, skip scanning and go directly to restart + if (this->find_first_non_hidden_index_() < 0) { + return WiFiRetryPhase::RESTARTING_ADAPTER; + } + // Otherwise proceed to scanning for non-hidden networks return WiFiRetryPhase::SCAN_CONNECTING; } @@ -1162,7 +1175,7 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() { // Its priority has been decreased, so on next scan it will be sorted lower // and we'll try the next best BSSID. // Check if there are any potentially hidden networks to try - if (this->find_next_hidden_sta_(-1, !this->went_through_explicit_hidden_phase_()) >= 0) { + if (this->find_next_hidden_sta_(-1) >= 0) { return WiFiRetryPhase::RETRY_HIDDEN; // Found hidden networks to try } // No hidden networks - always go through RESTARTING_ADAPTER phase @@ -1179,8 +1192,13 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() { // Exhausted retries on current SSID - check if there are more potentially hidden SSIDs to try if (this->selected_sta_index_ < static_cast(this->sta_.size()) - 1) { - // More SSIDs available - stay in RETRY_HIDDEN, advance will happen in retry_connect() - return WiFiRetryPhase::RETRY_HIDDEN; + // Check if find_next_hidden_sta_() would actually find another hidden SSID + // as it might have been seen in the scan results and we want to skip those + // otherwise we will get stuck in RETRY_HIDDEN phase + if (this->find_next_hidden_sta_(this->selected_sta_index_) != -1) { + // More hidden SSIDs available - stay in RETRY_HIDDEN, advance will happen in retry_connect() + return WiFiRetryPhase::RETRY_HIDDEN; + } } } // Exhausted all potentially hidden SSIDs - always go through RESTARTING_ADAPTER @@ -1205,8 +1223,8 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() { /// - Performing phase-specific initialization (e.g., advancing AP index, starting scans) /// /// @param new_phase The phase we're transitioning TO -/// @return true if an async scan was started (caller should wait for completion) -/// false if no scan started (caller can proceed with connection attempt) +/// @return true if connection attempt should be skipped (scan started or no networks to try) +/// false if caller can proceed with connection attempt bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) { WiFiRetryPhase old_phase = this->retry_phase_; @@ -1264,7 +1282,7 @@ bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) { // If first network is marked hidden, we went through EXPLICIT_HIDDEN phase // In that case, skip networks marked hidden:true (already tried) // Otherwise, include them (they haven't been tried yet) - this->selected_sta_index_ = this->find_next_hidden_sta_(-1, !this->went_through_explicit_hidden_phase_()); + this->selected_sta_index_ = this->find_next_hidden_sta_(-1); if (this->selected_sta_index_ == -1) { ESP_LOGD(TAG, "All SSIDs visible or already tried, skipping hidden mode"); @@ -1410,8 +1428,7 @@ void WiFiComponent::advance_to_next_target_or_increment_retry_() { // If first network is marked hidden, we went through EXPLICIT_HIDDEN phase // In that case, skip networks marked hidden:true (already tried) // Otherwise, include them (they haven't been tried yet) - int8_t next_index = - this->find_next_hidden_sta_(this->selected_sta_index_, !this->went_through_explicit_hidden_phase_()); + int8_t next_index = this->find_next_hidden_sta_(this->selected_sta_index_); if (next_index != -1) { // Found another potentially hidden SSID this->selected_sta_index_ = next_index; diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index b8223e8dc8..e786708b08 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -379,8 +379,7 @@ class WiFiComponent : public Component { /// Find next SSID that wasn't in scan results (might be hidden) /// Returns index of next potentially hidden SSID, or -1 if none found /// @param start_index Start searching from index after this (-1 to start from beginning) - /// @param include_explicit_hidden If true, include SSIDs marked hidden:true. If false, only find truly hidden SSIDs. - int8_t find_next_hidden_sta_(int8_t start_index, bool include_explicit_hidden = true); + int8_t find_next_hidden_sta_(int8_t start_index); /// Log failed connection and decrease BSSID priority to avoid repeated attempts void log_and_adjust_priority_for_failed_connect_(); /// Clear BSSID priority tracking if all priorities are at minimum (saves memory) From 65a303d48f92c1706dc6f464f88c41fbc9aa5c9f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 11 Nov 2025 16:39:55 -0600 Subject: [PATCH 478/526] [wifi] Add min_auth_mode configuration option (#11814) --- esphome/components/wifi/__init__.py | 42 +++++++++++++++++++ esphome/components/wifi/wifi_component.h | 8 ++++ .../wifi/wifi_component_esp8266.cpp | 13 +++++- .../wifi/wifi_component_esp_idf.cpp | 15 +++++-- tests/components/wifi/test.esp32-idf.yaml | 1 + tests/components/wifi/test.esp8266-ard.yaml | 6 ++- 6 files changed, 79 insertions(+), 6 deletions(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 358f920c2c..c42af23252 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -1,3 +1,5 @@ +import logging + from esphome import automation from esphome.automation import Condition import esphome.codegen as cg @@ -42,6 +44,7 @@ from esphome.const import ( CONF_TTLS_PHASE_2, CONF_USE_ADDRESS, CONF_USERNAME, + Platform, PlatformFramework, ) from esphome.core import CORE, CoroPriority, HexInt, coroutine_with_priority @@ -49,10 +52,13 @@ import esphome.final_validate as fv from . import wpa2_eap +_LOGGER = logging.getLogger(__name__) + AUTO_LOAD = ["network"] NO_WIFI_VARIANTS = [const.VARIANT_ESP32H2, const.VARIANT_ESP32P4] CONF_SAVE = "save" +CONF_MIN_AUTH_MODE = "min_auth_mode" # Maximum number of WiFi networks that can be configured # Limited to 127 because selected_sta_index_ is int8_t in C++ @@ -70,6 +76,14 @@ WIFI_POWER_SAVE_MODES = { "LIGHT": WiFiPowerSaveMode.WIFI_POWER_SAVE_LIGHT, "HIGH": WiFiPowerSaveMode.WIFI_POWER_SAVE_HIGH, } + +WifiMinAuthMode = wifi_ns.enum("WifiMinAuthMode") +WIFI_MIN_AUTH_MODES = { + "WPA": WifiMinAuthMode.WIFI_MIN_AUTH_MODE_WPA, + "WPA2": WifiMinAuthMode.WIFI_MIN_AUTH_MODE_WPA2, + "WPA3": WifiMinAuthMode.WIFI_MIN_AUTH_MODE_WPA3, +} +VALIDATE_WIFI_MIN_AUTH_MODE = cv.enum(WIFI_MIN_AUTH_MODES, upper=True) WiFiConnectedCondition = wifi_ns.class_("WiFiConnectedCondition", Condition) WiFiEnabledCondition = wifi_ns.class_("WiFiEnabledCondition", Condition) WiFiEnableAction = wifi_ns.class_("WiFiEnableAction", automation.Action) @@ -187,6 +201,27 @@ def validate_variant(_): raise cv.Invalid(f"WiFi requires component esp32_hosted on {variant}") +def _apply_min_auth_mode_default(config): + """Apply platform-specific default for min_auth_mode and warn ESP8266 users.""" + # Only apply defaults for platforms that support min_auth_mode + if CONF_MIN_AUTH_MODE not in config and (CORE.is_esp8266 or CORE.is_esp32): + if CORE.is_esp8266: + _LOGGER.warning( + "The minimum WiFi authentication mode (wifi -> min_auth_mode) is not set. " + "This controls the weakest encryption your device will accept when connecting to WiFi. " + "Currently defaults to WPA (less secure), but will change to WPA2 (more secure) in 2026.6.0. " + "WPA uses TKIP encryption which has known security vulnerabilities and should be avoided. " + "WPA2 uses AES encryption which is significantly more secure. " + "To silence this warning, explicitly set min_auth_mode under 'wifi:'. " + "If your router supports WPA2 or WPA3, set 'min_auth_mode: WPA2'. " + "If your router only supports WPA, set 'min_auth_mode: WPA'." + ) + config[CONF_MIN_AUTH_MODE] = VALIDATE_WIFI_MIN_AUTH_MODE("WPA") + elif CORE.is_esp32: + config[CONF_MIN_AUTH_MODE] = VALIDATE_WIFI_MIN_AUTH_MODE("WPA2") + return config + + def final_validate(config): has_sta = bool(config.get(CONF_NETWORKS, True)) has_ap = CONF_AP in config @@ -287,6 +322,10 @@ CONFIG_SCHEMA = cv.All( ): cv.enum(WIFI_POWER_SAVE_MODES, upper=True), cv.Optional(CONF_FAST_CONNECT, default=False): cv.boolean, cv.Optional(CONF_USE_ADDRESS): cv.string_strict, + cv.Optional(CONF_MIN_AUTH_MODE): cv.All( + VALIDATE_WIFI_MIN_AUTH_MODE, + cv.only_on([Platform.ESP32, Platform.ESP8266]), + ), cv.SplitDefault(CONF_OUTPUT_POWER, esp8266=20.0): cv.All( cv.decibel, cv.float_range(min=8.5, max=20.5) ), @@ -311,6 +350,7 @@ CONFIG_SCHEMA = cv.All( ), } ), + _apply_min_auth_mode_default, _validate, ) @@ -420,6 +460,8 @@ async def to_code(config): cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE])) + if CONF_MIN_AUTH_MODE in config: + cg.add(var.set_min_auth_mode(config[CONF_MIN_AUTH_MODE])) if config[CONF_FAST_CONNECT]: cg.add_define("USE_WIFI_FAST_CONNECT") cg.add(var.set_passive_scan(config[CONF_PASSIVE_SCAN])) diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index e786708b08..02d6d984f1 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -225,6 +225,12 @@ enum WiFiPowerSaveMode : uint8_t { WIFI_POWER_SAVE_HIGH, }; +enum WifiMinAuthMode : uint8_t { + WIFI_MIN_AUTH_MODE_WPA = 0, + WIFI_MIN_AUTH_MODE_WPA2, + WIFI_MIN_AUTH_MODE_WPA3, +}; + #ifdef USE_ESP32 struct IDFWiFiEvent; #endif @@ -274,6 +280,7 @@ class WiFiComponent : public Component { bool is_connected(); void set_power_save_mode(WiFiPowerSaveMode power_save); + void set_min_auth_mode(WifiMinAuthMode min_auth_mode) { min_auth_mode_ = min_auth_mode; } void set_output_power(float output_power) { output_power_ = output_power; } void set_passive_scan(bool passive); @@ -490,6 +497,7 @@ class WiFiComponent : public Component { // Group all 8-bit values together WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF}; WiFiPowerSaveMode power_save_{WIFI_POWER_SAVE_NONE}; + WifiMinAuthMode min_auth_mode_{WIFI_MIN_AUTH_MODE_WPA2}; WiFiRetryPhase retry_phase_{WiFiRetryPhase::INITIAL_CONNECT}; uint8_t num_retried_{0}; // Index into sta_ array for the currently selected AP configuration (-1 = none selected) diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 4e17c42f41..56e071404b 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -258,8 +258,17 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { if (ap.get_password().empty()) { conf.threshold.authmode = AUTH_OPEN; } else { - // Only allow auth modes with at least WPA - conf.threshold.authmode = AUTH_WPA_PSK; + // Set threshold based on configured minimum auth mode + // Note: ESP8266 doesn't support WPA3 + switch (this->min_auth_mode_) { + case WIFI_MIN_AUTH_MODE_WPA: + conf.threshold.authmode = AUTH_WPA_PSK; + break; + case WIFI_MIN_AUTH_MODE_WPA2: + case WIFI_MIN_AUTH_MODE_WPA3: // Fall back to WPA2 for ESP8266 + conf.threshold.authmode = AUTH_WPA2_PSK; + break; + } } conf.threshold.rssi = -127; #endif diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 08ecba3598..d3088c9a10 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -308,7 +308,18 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { if (ap.get_password().empty()) { conf.sta.threshold.authmode = WIFI_AUTH_OPEN; } else { - conf.sta.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK; + // Set threshold based on configured minimum auth mode + switch (this->min_auth_mode_) { + case WIFI_MIN_AUTH_MODE_WPA: + conf.sta.threshold.authmode = WIFI_AUTH_WPA_PSK; + break; + case WIFI_MIN_AUTH_MODE_WPA2: + conf.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; + break; + case WIFI_MIN_AUTH_MODE_WPA3: + conf.sta.threshold.authmode = WIFI_AUTH_WPA3_PSK; + break; + } } #ifdef USE_WIFI_WPA2_EAP @@ -347,8 +358,6 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // The minimum rssi to accept in the fast scan mode conf.sta.threshold.rssi = -127; - conf.sta.threshold.authmode = WIFI_AUTH_OPEN; - wifi_config_t current_conf; esp_err_t err; err = esp_wifi_get_config(WIFI_IF_STA, ¤t_conf); diff --git a/tests/components/wifi/test.esp32-idf.yaml b/tests/components/wifi/test.esp32-idf.yaml index 91e235b9ce..827e4b17f7 100644 --- a/tests/components/wifi/test.esp32-idf.yaml +++ b/tests/components/wifi/test.esp32-idf.yaml @@ -2,6 +2,7 @@ psram: wifi: use_psram: true + min_auth_mode: WPA packages: - !include common.yaml diff --git a/tests/components/wifi/test.esp8266-ard.yaml b/tests/components/wifi/test.esp8266-ard.yaml index dade44d145..9cb0e3cf48 100644 --- a/tests/components/wifi/test.esp8266-ard.yaml +++ b/tests/components/wifi/test.esp8266-ard.yaml @@ -1 +1,5 @@ -<<: !include common.yaml +wifi: + min_auth_mode: WPA2 + +packages: + - !include common.yaml From 5dafaaced465413fb363f1896d79ea392ff0abc4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 11 Nov 2025 17:12:10 -0600 Subject: [PATCH 479/526] [wifi] Fix scan and connection failures after adapter restart (#11851) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../esp32_improv/esp32_improv_component.cpp | 2 +- .../improv_serial/improv_serial_component.cpp | 2 +- esphome/components/wifi/wifi_component.cpp | 85 ++++++++----------- esphome/components/wifi/wifi_component.h | 10 +-- 4 files changed, 39 insertions(+), 60 deletions(-) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 2fa9d8f523..398b1d4251 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -336,7 +336,7 @@ void ESP32ImprovComponent::process_incoming_data_() { this->connecting_sta_ = sta; wifi::global_wifi_component->set_sta(sta); - wifi::global_wifi_component->start_connecting(sta, false); + wifi::global_wifi_component->start_connecting(sta); this->set_state_(improv::STATE_PROVISIONING); ESP_LOGD(TAG, "Received Improv Wi-Fi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(), command.password.c_str()); diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 9d080ea98e..70260eeab3 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -231,7 +231,7 @@ bool ImprovSerialComponent::parse_improv_payload_(improv::ImprovCommand &command this->connecting_sta_ = sta; wifi::global_wifi_component->set_sta(sta); - wifi::global_wifi_component->start_connecting(sta, false); + wifi::global_wifi_component->start_connecting(sta); this->set_state_(improv::STATE_PROVISIONING); ESP_LOGD(TAG, "Received settings: SSID=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(), command.password.c_str()); diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index d75ac971eb..ddba0558b4 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -197,6 +197,10 @@ static constexpr uint8_t WIFI_RETRY_COUNT_PER_SSID = 1; // Rationale: Fast connect prioritizes speed - try each AP once to find a working one quickly static constexpr uint8_t WIFI_RETRY_COUNT_PER_AP = 1; +/// Cooldown duration in milliseconds after adapter restart or repeated failures +/// Allows WiFi hardware to stabilize before next connection attempt +static constexpr uint32_t WIFI_COOLDOWN_DURATION_MS = 1000; + static constexpr uint8_t get_max_retries_for_phase(WiFiRetryPhase phase) { switch (phase) { case WiFiRetryPhase::INITIAL_CONNECT: @@ -275,7 +279,7 @@ int8_t WiFiComponent::find_next_hidden_sta_(int8_t start_index) { ESP_LOGD(TAG, "Hidden candidate " LOG_SECRET("'%s'") " at index %d", sta.get_ssid().c_str(), static_cast(i)); return static_cast(i); } - ESP_LOGD(TAG, "Skipping " LOG_SECRET("'%s'") " (visible in scan)", sta.get_ssid().c_str()); + ESP_LOGD(TAG, "Skipping hidden retry for visible network " LOG_SECRET("'%s'"), sta.get_ssid().c_str()); } // No hidden SSIDs found return -1; @@ -289,7 +293,7 @@ void WiFiComponent::start_initial_connection_() { this->selected_sta_index_ = 0; this->retry_phase_ = WiFiRetryPhase::EXPLICIT_HIDDEN; WiFiAP params = this->build_params_for_current_phase_(); - this->start_connecting(params, false); + this->start_connecting(params); } else { ESP_LOGI(TAG, "Starting scan"); this->start_scanning(); @@ -371,13 +375,13 @@ void WiFiComponent::start() { // Without saved data, try first configured network or use normal flow if (loaded_fast_connect) { ESP_LOGI(TAG, "Starting fast_connect (saved) " LOG_SECRET("'%s'"), params.get_ssid().c_str()); - this->start_connecting(params, false); + this->start_connecting(params); } else if (!this->sta_.empty() && !this->sta_[0].get_hidden()) { // No saved data, but have configured networks - try first non-hidden network ESP_LOGI(TAG, "Starting fast_connect (config) " LOG_SECRET("'%s'"), this->sta_[0].get_ssid().c_str()); this->selected_sta_index_ = 0; params = this->build_params_for_current_phase_(); - this->start_connecting(params, false); + this->start_connecting(params); } else { // No saved data and (no networks OR first is hidden) - use normal flow this->start_initial_connection_(); @@ -413,8 +417,11 @@ void WiFiComponent::start() { void WiFiComponent::restart_adapter() { ESP_LOGW(TAG, "Restarting adapter"); this->wifi_mode_(false, {}); - delay(100); // NOLINT + // Enter cooldown state to allow WiFi hardware to stabilize after restart // Don't set retry_phase_ or num_retried_ here - state machine handles transitions + this->state_ = WIFI_COMPONENT_STATE_COOLDOWN; + this->action_started_ = millis(); + this->error_from_callback_ = false; } void WiFiComponent::loop() { @@ -434,20 +441,12 @@ void WiFiComponent::loop() { switch (this->state_) { case WIFI_COMPONENT_STATE_COOLDOWN: { this->status_set_warning(LOG_STR("waiting to reconnect")); - if (millis() - this->action_started_ > 5000) { - // After cooldown, connect based on current retry phase - this->reset_selected_ap_to_first_if_invalid_(); - - // Check if we need to trigger a scan first - if (this->needs_scan_results_() && !this->all_networks_hidden_()) { - // Need scan results or no matching networks found - scan/rescan - ESP_LOGD(TAG, "Scanning required for phase %s", LOG_STR_ARG(retry_phase_to_log_string(this->retry_phase_))); - this->start_scanning(); - } else { - // Have everything we need to connect (or all networks are hidden, skip scanning) - WiFiAP params = this->build_params_for_current_phase_(); - this->start_connecting(params, false); - } + if (now - this->action_started_ > WIFI_COOLDOWN_DURATION_MS) { + // After cooldown we either restarted the adapter because of + // a failure, or something tried to connect over and over + // so we entered cooldown. In both cases we call + // check_connecting_finished to continue the state machine. + this->check_connecting_finished(); } break; } @@ -456,8 +455,7 @@ void WiFiComponent::loop() { this->check_scanning_finished(); break; } - case WIFI_COMPONENT_STATE_STA_CONNECTING: - case WIFI_COMPONENT_STATE_STA_CONNECTING_2: { + case WIFI_COMPONENT_STATE_STA_CONNECTING: { this->status_set_warning(LOG_STR("associating to network")); this->check_connecting_finished(); break; @@ -666,7 +664,7 @@ void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &pa this->set_sta(sta); } -void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { +void WiFiComponent::start_connecting(const WiFiAP &ap) { // Log connection attempt at INFO level with priority std::string bssid_formatted; int8_t priority = 0; @@ -730,14 +728,11 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { if (!this->wifi_sta_connect_(ap)) { ESP_LOGE(TAG, "wifi_sta_connect_ failed"); - this->retry_connect(); - return; - } - - if (!two) { - this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING; + // Enter cooldown to allow WiFi hardware to stabilize + // (immediate failure suggests hardware not ready, different from connection timeout) + this->state_ = WIFI_COMPONENT_STATE_COOLDOWN; } else { - this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING_2; + this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING; } this->action_started_ = millis(); } @@ -1006,8 +1001,6 @@ void WiFiComponent::check_scanning_finished() { this->transition_to_phase_(WiFiRetryPhase::RETRY_HIDDEN); // If no hidden networks to try, skip connection attempt (will be handled on next loop) if (this->selected_sta_index_ == -1) { - this->state_ = WIFI_COMPONENT_STATE_COOLDOWN; - this->action_started_ = millis(); return; } // Now start connection attempt in hidden mode @@ -1020,7 +1013,7 @@ void WiFiComponent::check_scanning_finished() { WiFiAP params = this->build_params_for_current_phase_(); // Ensure we're in SCAN_CONNECTING phase when connecting with scan results // (needed when scan was started directly without transition_to_phase_, e.g., initial scan) - this->start_connecting(params, false); + this->start_connecting(params); } void WiFiComponent::dump_config() { @@ -1094,7 +1087,7 @@ void WiFiComponent::check_connecting_finished() { } if (this->error_from_callback_) { - ESP_LOGW(TAG, "Connecting to network failed"); + ESP_LOGW(TAG, "Connecting to network failed (callback)"); this->retry_connect(); return; } @@ -1456,15 +1449,13 @@ void WiFiComponent::advance_to_next_target_or_increment_retry_() { void WiFiComponent::retry_connect() { this->log_and_adjust_priority_for_failed_connect_(); - delay(10); - // Determine next retry phase based on current state WiFiRetryPhase current_phase = this->retry_phase_; WiFiRetryPhase next_phase = this->determine_next_phase_(); // Handle phase transitions (transition_to_phase_ handles same-phase no-op internally) if (this->transition_to_phase_(next_phase)) { - return; // Wait for scan to complete + return; // Scan started or adapter restarted (which sets its own state) } if (next_phase == current_phase) { @@ -1473,22 +1464,14 @@ void WiFiComponent::retry_connect() { this->error_from_callback_ = false; - if (this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTING) { - yield(); - // Check if we have a valid target before building params - // After exhausting all networks in a phase, selected_sta_index_ may be -1 - // In that case, skip connection and let next wifi_loop() handle phase transition - if (this->selected_sta_index_ >= 0) { - this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING_2; - WiFiAP params = this->build_params_for_current_phase_(); - this->start_connecting(params, true); - return; - } - // No valid target - fall through to set state to allow phase transition + yield(); + // Check if we have a valid target before building params + // After exhausting all networks in a phase, selected_sta_index_ may be -1 + // In that case, skip connection and let next wifi_loop() handle phase transition + if (this->selected_sta_index_ >= 0) { + WiFiAP params = this->build_params_for_current_phase_(); + this->start_connecting(params); } - - this->state_ = WIFI_COMPONENT_STATE_COOLDOWN; - this->action_started_ = millis(); } void WiFiComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 02d6d984f1..ef0372535a 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -74,12 +74,6 @@ enum WiFiComponentState : uint8_t { WIFI_COMPONENT_STATE_STA_SCANNING, /** WiFi is in STA(+AP) mode and currently connecting to an AP. */ WIFI_COMPONENT_STATE_STA_CONNECTING, - /** WiFi is in STA(+AP) mode and currently connecting to an AP a second time. - * - * This is required because for some reason ESPs don't like to connect to WiFi APs directly after - * a scan. - * */ - WIFI_COMPONENT_STATE_STA_CONNECTING_2, /** WiFi is in STA(+AP) mode and successfully connected. */ WIFI_COMPONENT_STATE_STA_CONNECTED, /** WiFi is in AP-only mode and internal AP is already enabled. */ @@ -269,7 +263,9 @@ class WiFiComponent : public Component { bool is_disabled(); void start_scanning(); void check_scanning_finished(); - void start_connecting(const WiFiAP &ap, bool two); + void start_connecting(const WiFiAP &ap); + // Backward compatibility overload - ignores 'two' parameter + void start_connecting(const WiFiAP &ap, bool /* two */) { this->start_connecting(ap); } void check_connecting_finished(); From 572fae5c7d49c479f0006420e8bb656d89948480 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 11 Nov 2025 17:12:53 -0600 Subject: [PATCH 480/526] [wifi] Restore two-attempt BSSID filtering for mesh networks (#11844) --- esphome/components/wifi/wifi_component.cpp | 26 +++++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index ddba0558b4..e79d821ba7 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1341,6 +1341,11 @@ void WiFiComponent::clear_priorities_if_all_min_() { /// - Other phases: Uses BSSID from config if explicitly specified by user or fast_connect /// /// If no BSSID is available (SSID-only connection), priority adjustment is skipped. +/// +/// IMPORTANT: Priority is only decreased on the LAST attempt for a BSSID in SCAN_CONNECTING phase. +/// This prevents false positives from transient WiFi stack state issues after scanning. +/// Single failures don't necessarily mean the AP is bad - two genuine failures provide +/// higher confidence before degrading priority and skipping the BSSID in future scans. void WiFiComponent::log_and_adjust_priority_for_failed_connect_() { // Determine which BSSID we tried to connect to optional failed_bssid; @@ -1357,12 +1362,6 @@ void WiFiComponent::log_and_adjust_priority_for_failed_connect_() { return; // No BSSID to penalize } - // Decrease priority to avoid repeatedly trying the same failed BSSID - int8_t old_priority = this->get_sta_priority(failed_bssid.value()); - int8_t new_priority = - (old_priority > std::numeric_limits::min()) ? (old_priority - 1) : std::numeric_limits::min(); - this->set_sta_priority(failed_bssid.value(), new_priority); - // Get SSID for logging std::string ssid; if (this->retry_phase_ == WiFiRetryPhase::SCAN_CONNECTING && !this->scan_result_.empty()) { @@ -1371,6 +1370,21 @@ void WiFiComponent::log_and_adjust_priority_for_failed_connect_() { ssid = config->get_ssid(); } + // Only decrease priority on the last attempt for this phase + // This prevents false positives from transient WiFi stack issues + uint8_t max_retries = get_max_retries_for_phase(this->retry_phase_); + bool is_last_attempt = (this->num_retried_ + 1 >= max_retries); + + // Decrease priority only on last attempt to avoid false positives from transient failures + int8_t old_priority = this->get_sta_priority(failed_bssid.value()); + int8_t new_priority = old_priority; + + if (is_last_attempt) { + // Decrease priority, but clamp to int8_t::min to prevent overflow + new_priority = + (old_priority > std::numeric_limits::min()) ? (old_priority - 1) : std::numeric_limits::min(); + this->set_sta_priority(failed_bssid.value(), new_priority); + } ESP_LOGD(TAG, "Failed " LOG_SECRET("'%s'") " " LOG_SECRET("(%s)") ", priority %d → %d", ssid.c_str(), format_mac_address_pretty(failed_bssid.value().data()).c_str(), old_priority, new_priority); From 79a44449283407456694354e7dd3f4c8d22773ad Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 11 Nov 2025 17:27:08 -0600 Subject: [PATCH 481/526] [wifi] Conditionally compile manual_ip to save 24-72 bytes RAM (#11833) --- esphome/components/wifi/__init__.py | 10 ++++++++++ esphome/components/wifi/wifi_component.cpp | 11 ++++++++++- esphome/components/wifi/wifi_component.h | 6 ++++++ .../components/wifi/wifi_component_esp8266.cpp | 13 +++++++++++++ .../components/wifi/wifi_component_esp_idf.cpp | 13 +++++++++++++ .../components/wifi/wifi_component_libretiny.cpp | 13 +++++++++++++ esphome/components/wifi/wifi_component_pico_w.cpp | 12 ++++++++++++ esphome/core/defines.h | 1 + tests/components/wifi/test.esp32-idf.yaml | 15 +++++++++++++++ 9 files changed, 93 insertions(+), 1 deletion(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index c42af23252..28db698a43 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -425,6 +425,8 @@ async def to_code(config): # Track if any network uses Enterprise authentication has_eap = False + # Track if any network uses manual IP + has_manual_ip = False # Initialize FixedVector with the count of networks networks = config.get(CONF_NETWORKS, []) @@ -438,11 +440,15 @@ async def to_code(config): for network in networks: if CONF_EAP in network: has_eap = True + if network.get(CONF_MANUAL_IP) or config.get(CONF_MANUAL_IP): + has_manual_ip = True cg.with_local_variable(network[CONF_ID], WiFiAP(), add_sta, network) if CONF_AP in config: conf = config[CONF_AP] ip_config = conf.get(CONF_MANUAL_IP) + if ip_config: + has_manual_ip = True cg.with_local_variable( conf[CONF_ID], WiFiAP(), @@ -458,6 +464,10 @@ async def to_code(config): if CORE.is_esp32: add_idf_sdkconfig_option("CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT", has_eap) + # Only define USE_WIFI_MANUAL_IP if any AP uses manual IP + if has_manual_ip: + cg.add_define("USE_WIFI_MANUAL_IP") + cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE])) if CONF_MIN_AUTH_MODE in config: diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index e79d821ba7..817419107f 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -569,6 +569,7 @@ void WiFiComponent::setup_ap_config_() { " IP Address: %s", this->ap_.get_ssid().c_str(), this->ap_.get_password().c_str(), ip_address.c_str()); +#ifdef USE_WIFI_MANUAL_IP auto manual_ip = this->ap_.get_manual_ip(); if (manual_ip.has_value()) { ESP_LOGCONFIG(TAG, @@ -578,6 +579,7 @@ void WiFiComponent::setup_ap_config_() { manual_ip->static_ip.str().c_str(), manual_ip->gateway.str().c_str(), manual_ip->subnet.str().c_str()); } +#endif if (!this->has_sta()) { this->state_ = WIFI_COMPONENT_STATE_AP; @@ -716,11 +718,14 @@ void WiFiComponent::start_connecting(const WiFiAP &ap) { } else { ESP_LOGV(TAG, " Channel not set"); } +#ifdef USE_WIFI_MANUAL_IP if (ap.get_manual_ip().has_value()) { ManualIP m = *ap.get_manual_ip(); ESP_LOGV(TAG, " Manual IP: Static IP=%s Gateway=%s Subnet=%s DNS1=%s DNS2=%s", m.static_ip.str().c_str(), m.gateway.str().c_str(), m.subnet.str().c_str(), m.dns1.str().c_str(), m.dns2.str().c_str()); - } else { + } else +#endif + { ESP_LOGV(TAG, " Using DHCP IP"); } ESP_LOGV(TAG, " Hidden: %s", YESNO(ap.get_hidden())); @@ -1577,7 +1582,9 @@ void WiFiAP::set_password(const std::string &password) { this->password_ = passw void WiFiAP::set_eap(optional eap_auth) { this->eap_ = std::move(eap_auth); } #endif void WiFiAP::set_channel(optional channel) { this->channel_ = channel; } +#ifdef USE_WIFI_MANUAL_IP void WiFiAP::set_manual_ip(optional manual_ip) { this->manual_ip_ = manual_ip; } +#endif void WiFiAP::set_hidden(bool hidden) { this->hidden_ = hidden; } const std::string &WiFiAP::get_ssid() const { return this->ssid_; } const optional &WiFiAP::get_bssid() const { return this->bssid_; } @@ -1586,7 +1593,9 @@ const std::string &WiFiAP::get_password() const { return this->password_; } const optional &WiFiAP::get_eap() const { return this->eap_; } #endif const optional &WiFiAP::get_channel() const { return this->channel_; } +#ifdef USE_WIFI_MANUAL_IP const optional &WiFiAP::get_manual_ip() const { return this->manual_ip_; } +#endif bool WiFiAP::get_hidden() const { return this->hidden_; } WiFiScanResult::WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth, diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index ef0372535a..713e6f223f 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -152,7 +152,9 @@ class WiFiAP { #endif // USE_WIFI_WPA2_EAP void set_channel(optional channel); void set_priority(int8_t priority) { priority_ = priority; } +#ifdef USE_WIFI_MANUAL_IP void set_manual_ip(optional manual_ip); +#endif void set_hidden(bool hidden); const std::string &get_ssid() const; const optional &get_bssid() const; @@ -162,7 +164,9 @@ class WiFiAP { #endif // USE_WIFI_WPA2_EAP const optional &get_channel() const; int8_t get_priority() const { return priority_; } +#ifdef USE_WIFI_MANUAL_IP const optional &get_manual_ip() const; +#endif bool get_hidden() const; protected: @@ -172,7 +176,9 @@ class WiFiAP { #ifdef USE_WIFI_WPA2_EAP optional eap_; #endif // USE_WIFI_WPA2_EAP +#ifdef USE_WIFI_MANUAL_IP optional manual_ip_; +#endif optional channel_; int8_t priority_{0}; bool hidden_{false}; diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 56e071404b..bcb5dc4cf7 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -282,9 +282,15 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { return false; } +#ifdef USE_WIFI_MANUAL_IP if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) { return false; } +#else + if (!this->wifi_sta_ip_config_({})) { + return false; + } +#endif // setup enterprise authentication if required #ifdef USE_WIFI_WPA2_EAP @@ -832,10 +838,17 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return false; } +#ifdef USE_WIFI_MANUAL_IP if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { ESP_LOGV(TAG, "wifi_ap_ip_config_ failed"); return false; } +#else + if (!this->wifi_ap_ip_config_({})) { + ESP_LOGV(TAG, "wifi_ap_ip_config_ failed"); + return false; + } +#endif return true; } diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index d3088c9a10..fd7e85fb6b 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -380,9 +380,15 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { return false; } +#ifdef USE_WIFI_MANUAL_IP if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) { return false; } +#else + if (!this->wifi_sta_ip_config_({})) { + return false; + } +#endif // setup enterprise authentication if required #ifdef USE_WIFI_WPA2_EAP @@ -994,10 +1000,17 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return false; } +#ifdef USE_WIFI_MANUAL_IP if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { ESP_LOGE(TAG, "wifi_ap_ip_config_ failed:"); return false; } +#else + if (!this->wifi_ap_ip_config_({})) { + ESP_LOGE(TAG, "wifi_ap_ip_config_ failed:"); + return false; + } +#endif return true; } diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index 45e2fba82a..2946b9e831 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -112,9 +112,15 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { WiFi.disconnect(); } +#ifdef USE_WIFI_MANUAL_IP if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) { return false; } +#else + if (!this->wifi_sta_ip_config_({})) { + return false; + } +#endif this->wifi_apply_hostname_(); @@ -445,10 +451,17 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { if (!this->wifi_mode_({}, true)) return false; +#ifdef USE_WIFI_MANUAL_IP if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { ESP_LOGV(TAG, "wifi_ap_ip_config_ failed"); return false; } +#else + if (!this->wifi_ap_ip_config_({})) { + ESP_LOGV(TAG, "wifi_ap_ip_config_ failed"); + return false; + } +#endif yield(); diff --git a/esphome/components/wifi/wifi_component_pico_w.cpp b/esphome/components/wifi/wifi_component_pico_w.cpp index bf15892cd5..7025ba16bd 100644 --- a/esphome/components/wifi/wifi_component_pico_w.cpp +++ b/esphome/components/wifi/wifi_component_pico_w.cpp @@ -55,8 +55,13 @@ bool WiFiComponent::wifi_apply_power_save_() { bool WiFiComponent::wifi_apply_output_power_(float output_power) { return true; } bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { +#ifdef USE_WIFI_MANUAL_IP if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) return false; +#else + if (!this->wifi_sta_ip_config_({})) + return false; +#endif auto ret = WiFi.begin(ap.get_ssid().c_str(), ap.get_password().c_str()); if (ret != WL_CONNECTED) @@ -161,10 +166,17 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { if (!this->wifi_mode_({}, true)) return false; +#ifdef USE_WIFI_MANUAL_IP if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { ESP_LOGV(TAG, "wifi_ap_ip_config_ failed"); return false; } +#else + if (!this->wifi_ap_ip_config_({})) { + ESP_LOGV(TAG, "wifi_ap_ip_config_ failed"); + return false; + } +#endif WiFi.beginAP(ap.get_ssid().c_str(), ap.get_password().c_str(), ap.get_channel().value_or(1)); diff --git a/esphome/core/defines.h b/esphome/core/defines.h index c522a8ec62..41f4b28cd5 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -144,6 +144,7 @@ #define USE_TIME_TIMEZONE #define USE_WIFI #define USE_WIFI_AP +#define USE_WIFI_MANUAL_IP #define USE_WIREGUARD #endif diff --git a/tests/components/wifi/test.esp32-idf.yaml b/tests/components/wifi/test.esp32-idf.yaml index 827e4b17f7..6b3ef20963 100644 --- a/tests/components/wifi/test.esp32-idf.yaml +++ b/tests/components/wifi/test.esp32-idf.yaml @@ -3,6 +3,21 @@ psram: wifi: use_psram: true min_auth_mode: WPA + manual_ip: + static_ip: 192.168.1.100 + gateway: 192.168.1.1 + subnet: 255.255.255.0 + dns1: 1.1.1.1 + dns2: 8.8.8.8 + ap: + ssid: Fallback AP + password: fallback_password + manual_ip: + static_ip: 192.168.4.1 + gateway: 192.168.4.1 + subnet: 255.255.255.0 + +captive_portal: packages: - !include common.yaml From d7fa131a8a31b3dab42acfe9685532b4b72ec141 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 11 Nov 2025 19:43:06 -0500 Subject: [PATCH 482/526] [network, psram, speaker wifi] Use CORE.data to enable high performance networking (#11812) --- esphome/components/network/__init__.py | 123 ++++++++++++++++++ esphome/components/psram/__init__.py | 37 +++++- .../speaker/media_player/__init__.py | 36 +++-- esphome/components/wifi/__init__.py | 58 ++++++++- tests/components/network/test.esp32-idf.yaml | 3 + 5 files changed, 235 insertions(+), 22 deletions(-) diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 1d62b661ca..d7a51fb0c6 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -1,7 +1,9 @@ import ipaddress +import logging import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option +from esphome.components.psram import is_guaranteed as psram_is_guaranteed import esphome.config_validation as cv from esphome.const import CONF_ENABLE_IPV6, CONF_MIN_IPV6_ADDR_COUNT from esphome.core import CORE, CoroPriority, coroutine_with_priority @@ -9,6 +11,13 @@ from esphome.core import CORE, CoroPriority, coroutine_with_priority CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["mdns"] +_LOGGER = logging.getLogger(__name__) + +# High performance networking tracking infrastructure +# Components can request high performance networking and this configures lwip and WiFi settings +KEY_HIGH_PERFORMANCE_NETWORKING = "high_performance_networking" +CONF_ENABLE_HIGH_PERFORMANCE = "enable_high_performance" + network_ns = cg.esphome_ns.namespace("network") IPAddress = network_ns.class_("IPAddress") @@ -47,6 +56,55 @@ def ip_address_literal(ip: str | int | None) -> cg.MockObj: return IPAddress(str(ip)) +def require_high_performance_networking() -> None: + """Request high performance networking for network and WiFi. + + Call this from components that need optimized network performance for streaming + or high-throughput data transfer. This enables high performance mode which + configures both lwip TCP settings and WiFi driver settings for improved + network performance. + + Settings applied (ESP-IDF only): + - lwip: Larger TCP buffers, windows, and mailbox sizes + - WiFi: Increased RX/TX buffers, AMPDU aggregation, PSRAM allocation (set by wifi component) + + Configuration is PSRAM-aware: + - With PSRAM guaranteed: Aggressive settings (512 RX buffers, 512KB TCP windows) + - Without PSRAM: Conservative optimized settings (64 buffers, 65KB TCP windows) + + Example: + from esphome.components import network + + def _request_high_performance_networking(config): + network.require_high_performance_networking() + return config + + CONFIG_SCHEMA = cv.All( + ..., + _request_high_performance_networking, + ) + """ + # Only set up once (idempotent - multiple components can call this) + if not CORE.data.get(KEY_HIGH_PERFORMANCE_NETWORKING, False): + CORE.data[KEY_HIGH_PERFORMANCE_NETWORKING] = True + + +def has_high_performance_networking() -> bool: + """Check if high performance networking mode is enabled. + + Returns True when high performance networking has been requested by a + component or explicitly enabled in the network configuration. This indicates + that lwip and WiFi will use optimized buffer sizes and settings. + + This function should be called during code generation (to_code phase) by + components that need to apply performance-related settings. + + Returns: + bool: True if high performance networking is enabled, False otherwise + """ + return CORE.data.get(KEY_HIGH_PERFORMANCE_NETWORKING, False) + + CONFIG_SCHEMA = cv.Schema( { cv.SplitDefault( @@ -71,6 +129,7 @@ CONFIG_SCHEMA = cv.Schema( ), ), cv.Optional(CONF_MIN_IPV6_ADDR_COUNT, default=0): cv.positive_int, + cv.Optional(CONF_ENABLE_HIGH_PERFORMANCE): cv.All(cv.boolean, cv.only_on_esp32), } ) @@ -80,6 +139,70 @@ async def to_code(config): cg.add_define("USE_NETWORK") if CORE.using_arduino and CORE.is_esp32: cg.add_library("Networking", None) + + # Apply high performance networking settings + # Config can explicitly enable/disable, or default to component-driven behavior + enable_high_perf = config.get(CONF_ENABLE_HIGH_PERFORMANCE) + component_requested = CORE.data.get(KEY_HIGH_PERFORMANCE_NETWORKING, False) + + # Explicit config overrides component request + should_enable = ( + enable_high_perf if enable_high_perf is not None else component_requested + ) + + # Log when user explicitly disables but a component requested it + if enable_high_perf is False and component_requested: + _LOGGER.info( + "High performance networking disabled by user configuration (overriding component request)" + ) + + if CORE.is_esp32 and CORE.using_esp_idf and should_enable: + # Check if PSRAM is guaranteed (set by psram component during final validation) + psram_guaranteed = psram_is_guaranteed() + + if psram_guaranteed: + _LOGGER.info( + "Applying high-performance lwip settings (PSRAM guaranteed): 512KB TCP windows, 512 mailbox sizes" + ) + # PSRAM is guaranteed - use aggressive settings + # Higher maximum values are allowed because CONFIG_LWIP_WND_SCALE is set to true + # CONFIG_LWIP_WND_SCALE can only be enabled if CONFIG_SPIRAM_IGNORE_NOTFOUND isn't set + # Based on https://github.com/espressif/esp-adf/issues/297#issuecomment-783811702 + + # Enable window scaling for much larger TCP windows + add_idf_sdkconfig_option("CONFIG_LWIP_WND_SCALE", True) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_RCV_SCALE", 3) + + # Large TCP buffers and windows (requires PSRAM) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_SND_BUF_DEFAULT", 65534) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_WND_DEFAULT", 512000) + + # Large mailboxes for high throughput + add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_RECVMBOX_SIZE", 512) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_RECVMBOX_SIZE", 512) + + # TCP connection limits + add_idf_sdkconfig_option("CONFIG_LWIP_MAX_ACTIVE_TCP", 16) + add_idf_sdkconfig_option("CONFIG_LWIP_MAX_LISTENING_TCP", 16) + + # TCP optimizations + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_MAXRTX", 12) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_SYNMAXRTX", 6) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_MSS", 1436) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_MSL", 60000) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_OVERSIZE_MSS", True) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_QUEUE_OOSEQ", True) + else: + _LOGGER.info( + "Applying optimized lwip settings: 65KB TCP windows, 64 mailbox sizes" + ) + # PSRAM not guaranteed - use more conservative, but still optimized settings + # Based on https://github.com/espressif/esp-idf/blob/release/v5.4/examples/wifi/iperf/sdkconfig.defaults.esp32 + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_SND_BUF_DEFAULT", 65534) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_WND_DEFAULT", 65534) + add_idf_sdkconfig_option("CONFIG_LWIP_TCP_RECVMBOX_SIZE", 64) + add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_RECVMBOX_SIZE", 64) + if (enable_ipv6 := config.get(CONF_ENABLE_IPV6, None)) is not None: cg.add_define("USE_NETWORK_IPV6", enable_ipv6) if enable_ipv6: diff --git a/esphome/components/psram/__init__.py b/esphome/components/psram/__init__.py index 11c238c1bf..c50c599855 100644 --- a/esphome/components/psram/__init__.py +++ b/esphome/components/psram/__init__.py @@ -35,6 +35,9 @@ DOMAIN = "psram" DEPENDENCIES = [PLATFORM_ESP32] +# PSRAM availability tracking for cross-component coordination +KEY_PSRAM_GUARANTEED = "psram_guaranteed" + _LOGGER = logging.getLogger(__name__) psram_ns = cg.esphome_ns.namespace(DOMAIN) @@ -71,6 +74,23 @@ def supported() -> bool: return variant in SPIRAM_MODES +def is_guaranteed() -> bool: + """Check if PSRAM is guaranteed to be available. + + Returns True when PSRAM is configured with both 'disabled: false' and + 'ignore_not_found: false', meaning the device will fail to boot if PSRAM + is not found. This ensures safe use of high buffer configurations that + depend on PSRAM. + + This function should be called during code generation (to_code phase) by + components that need to know PSRAM availability for configuration decisions. + + Returns: + bool: True if PSRAM is guaranteed, False otherwise + """ + return CORE.data.get(KEY_PSRAM_GUARANTEED, False) + + def validate_psram_mode(config): esp32_config = fv.full_config.get()[PLATFORM_ESP32] if config[CONF_SPEED] == "120MHZ": @@ -131,7 +151,22 @@ def get_config_schema(config): CONFIG_SCHEMA = get_config_schema -FINAL_VALIDATE_SCHEMA = validate_psram_mode + +def _store_psram_guaranteed(config): + """Store PSRAM guaranteed status in CORE.data for other components. + + PSRAM is "guaranteed" when it will fail if not found, ensuring safe use + of high buffer configurations in network/wifi components. + + Called during final validation to ensure the flag is available + before any to_code() functions run. + """ + psram_guaranteed = not config[CONF_DISABLED] and not config[CONF_IGNORE_NOT_FOUND] + CORE.data[KEY_PSRAM_GUARANTEED] = psram_guaranteed + return config + + +FINAL_VALIDATE_SCHEMA = cv.All(validate_psram_mode, _store_psram_guaranteed) async def to_code(config): diff --git a/esphome/components/speaker/media_player/__init__.py b/esphome/components/speaker/media_player/__init__.py index e50656e723..062bff92f8 100644 --- a/esphome/components/speaker/media_player/__init__.py +++ b/esphome/components/speaker/media_player/__init__.py @@ -6,7 +6,7 @@ from pathlib import Path from esphome import automation, external_files import esphome.codegen as cg -from esphome.components import audio, esp32, media_player, psram, speaker +from esphome.components import audio, esp32, media_player, network, psram, speaker import esphome.config_validation as cv from esphome.const import ( CONF_BUFFER_SIZE, @@ -32,6 +32,7 @@ _LOGGER = logging.getLogger(__name__) AUTO_LOAD = ["audio"] +DEPENDENCIES = ["network"] CODEOWNERS = ["@kahrendt", "@synesthesiam"] DOMAIN = "media_player" @@ -280,6 +281,18 @@ PIPELINE_SCHEMA = cv.Schema( } ) + +def _request_high_performance_networking(config): + """Request high performance networking for streaming media. + + Speaker media player streams audio data, so it always benefits from + optimized WiFi and lwip settings regardless of codec support. + Called during config validation to ensure flags are set before to_code(). + """ + network.require_high_performance_networking() + return config + + CONFIG_SCHEMA = cv.All( media_player.media_player_schema(SpeakerMediaPlayer).extend( { @@ -304,6 +317,7 @@ CONFIG_SCHEMA = cv.All( ), cv.only_with_esp_idf, _validate_repeated_speaker, + _request_high_performance_networking, ) @@ -321,28 +335,10 @@ FINAL_VALIDATE_SCHEMA = cv.All( async def to_code(config): if CORE.data[DOMAIN][config[CONF_ID].id][CONF_CODEC_SUPPORT_ENABLED]: - # Compile all supported audio codecs and optimize the wifi settings - + # Compile all supported audio codecs cg.add_define("USE_AUDIO_FLAC_SUPPORT", True) cg.add_define("USE_AUDIO_MP3_SUPPORT", True) - # Based on https://github.com/espressif/esp-idf/blob/release/v5.4/examples/wifi/iperf/sdkconfig.defaults.esp32 - esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM", 16) - esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM", 64) - esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM", 64) - esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_TX_ENABLED", True) - esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_TX_BA_WIN", 32) - esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_RX_ENABLED", True) - esp32.add_idf_sdkconfig_option("CONFIG_ESP_WIFI_RX_BA_WIN", 32) - - esp32.add_idf_sdkconfig_option("CONFIG_LWIP_TCP_SND_BUF_DEFAULT", 65534) - esp32.add_idf_sdkconfig_option("CONFIG_LWIP_TCP_WND_DEFAULT", 65534) - esp32.add_idf_sdkconfig_option("CONFIG_LWIP_TCP_RECVMBOX_SIZE", 64) - esp32.add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_RECVMBOX_SIZE", 64) - - # Allocate wifi buffers in PSRAM - esp32.add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True) - var = await media_player.new_media_player(config) await cg.register_component(var, config) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 28db698a43..4dbb425e4b 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -5,7 +5,11 @@ from esphome.automation import Condition import esphome.codegen as cg from esphome.components.const import CONF_USE_PSRAM from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant -from esphome.components.network import ip_address_literal +from esphome.components.network import ( + has_high_performance_networking, + ip_address_literal, +) +from esphome.components.psram import is_guaranteed as psram_is_guaranteed from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.config_validation import only_with_esp_idf @@ -56,6 +60,8 @@ _LOGGER = logging.getLogger(__name__) AUTO_LOAD = ["network"] +_LOGGER = logging.getLogger(__name__) + NO_WIFI_VARIANTS = [const.VARIANT_ESP32H2, const.VARIANT_ESP32P4] CONF_SAVE = "save" CONF_MIN_AUTH_MODE = "min_auth_mode" @@ -496,6 +502,56 @@ async def to_code(config): if config.get(CONF_USE_PSRAM): add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True) + + # Apply high performance WiFi settings if high performance networking is enabled + if CORE.is_esp32 and CORE.using_esp_idf and has_high_performance_networking(): + # Check if PSRAM is guaranteed (set by psram component during final validation) + psram_guaranteed = psram_is_guaranteed() + + # Always allocate WiFi buffers in PSRAM if available + add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True) + + if psram_guaranteed: + _LOGGER.info( + "Applying high-performance WiFi settings (PSRAM guaranteed): 512 RX buffers, 32 TX buffers" + ) + # PSRAM is guaranteed - use aggressive settings + # Higher maximum values are allowed because CONFIG_LWIP_WND_SCALE is set to true in networking component + # Based on https://github.com/espressif/esp-adf/issues/297#issuecomment-783811702 + + # Large dynamic RX buffers (requires PSRAM) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM", 16) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM", 512) + + # Static TX buffers for better performance + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_STATIC_TX_BUFFER", True) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_TX_BUFFER_TYPE", 0) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM", 32) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM", 8) + + # AMPDU settings optimized for PSRAM + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_TX_ENABLED", True) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_TX_BA_WIN", 16) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_RX_ENABLED", True) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_RX_BA_WIN", 32) + else: + _LOGGER.info( + "Applying optimized WiFi settings: 64 RX buffers, 64 TX buffers" + ) + # PSRAM not guaranteed - use more conservative, but still optimized settings + # Based on https://github.com/espressif/esp-idf/blob/release/v5.4/examples/wifi/iperf/sdkconfig.defaults.esp32 + + # Standard buffer counts + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM", 16) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM", 64) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM", 64) + + # Standard AMPDU settings + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_TX_ENABLED", True) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_TX_BA_WIN", 32) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_AMPDU_RX_ENABLED", True) + add_idf_sdkconfig_option("CONFIG_ESP_WIFI_RX_BA_WIN", 32) + cg.add_define("USE_WIFI") # must register before OTA safe mode check diff --git a/tests/components/network/test.esp32-idf.yaml b/tests/components/network/test.esp32-idf.yaml index dade44d145..7c01bafa0d 100644 --- a/tests/components/network/test.esp32-idf.yaml +++ b/tests/components/network/test.esp32-idf.yaml @@ -1 +1,4 @@ <<: !include common.yaml + +network: + enable_high_performance: true From 7806eb980f3caff0c1a7d73d8ec7bce4b0e2286d Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:50:47 -0500 Subject: [PATCH 483/526] Bump version to 2025.12.0-dev --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index 034fa3fa37..a19120b9da 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.11.0-dev +PROJECT_NUMBER = 2025.12.0-dev # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index d0d94ed283..a25114d80e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.11.0-dev" +__version__ = "2025.12.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 298813d4fab29c05767ec15914bbafb6f53b32ee Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:14:22 -0500 Subject: [PATCH 484/526] Bump version to 2025.11.0b1 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index a19120b9da..8025c71c19 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.12.0-dev +PROJECT_NUMBER = 2025.11.0b1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index a25114d80e..00975753c2 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.12.0-dev" +__version__ = "2025.11.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From a1ab19d127f3aeb28a22fbc401beff00c2799529 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 11 Nov 2025 21:44:19 -0600 Subject: [PATCH 485/526] [ci] Reduce release time by removing 21 redundant ESP32-S3 IDF tests (#11850) --- .../binary_sensor/test.esp32-s3-idf.yaml | 2 - .../bme68x_bsec2_i2c/test.esp32-s3-idf.yaml | 4 -- tests/components/debug/test.esp32-s3-idf.yaml | 1 - .../matrix_keypad/test.esp32-s3-idf.yaml | 15 ------ .../components/mcp3221/test.esp32-s3-idf.yaml | 4 -- .../mlx90393/test.esp32-s3-idf.yaml | 4 -- tests/components/npi19/test.esp32-s3-idf.yaml | 4 -- tests/components/ntc/test.esp32-s3-idf.yaml | 4 -- .../resistance/test.esp32-s3-idf.yaml | 4 -- .../components/switch/test.esp32-s3-idf.yaml | 2 - .../components/tem3200/test.esp32-s3-idf.yaml | 8 ---- .../template/test.esp32-s3-idf.yaml | 2 - ...max_with_usb_serial_jtag.esp32-s3-idf.yaml | 48 ------------------- .../wk2132_i2c/test.esp32-s3-idf.yaml | 9 ---- .../wk2132_spi/test.esp32-s3-idf.yaml | 11 ----- .../wk2168_i2c/test.esp32-s3-idf.yaml | 9 ---- .../wk2168_spi/test.esp32-s3-idf.yaml | 11 ----- .../wk2204_i2c/test.esp32-s3-idf.yaml | 9 ---- .../wk2204_spi/test.esp32-s3-idf.yaml | 11 ----- .../wk2212_i2c/test.esp32-s3-idf.yaml | 9 ---- .../wk2212_spi/test.esp32-s3-idf.yaml | 11 ----- 21 files changed, 182 deletions(-) delete mode 100644 tests/components/binary_sensor/test.esp32-s3-idf.yaml delete mode 100644 tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml delete mode 100644 tests/components/debug/test.esp32-s3-idf.yaml delete mode 100644 tests/components/matrix_keypad/test.esp32-s3-idf.yaml delete mode 100644 tests/components/mcp3221/test.esp32-s3-idf.yaml delete mode 100644 tests/components/mlx90393/test.esp32-s3-idf.yaml delete mode 100644 tests/components/npi19/test.esp32-s3-idf.yaml delete mode 100644 tests/components/ntc/test.esp32-s3-idf.yaml delete mode 100644 tests/components/resistance/test.esp32-s3-idf.yaml delete mode 100644 tests/components/switch/test.esp32-s3-idf.yaml delete mode 100644 tests/components/tem3200/test.esp32-s3-idf.yaml delete mode 100644 tests/components/template/test.esp32-s3-idf.yaml delete mode 100644 tests/components/uart/test-uart_max_with_usb_serial_jtag.esp32-s3-idf.yaml delete mode 100644 tests/components/wk2132_i2c/test.esp32-s3-idf.yaml delete mode 100644 tests/components/wk2132_spi/test.esp32-s3-idf.yaml delete mode 100644 tests/components/wk2168_i2c/test.esp32-s3-idf.yaml delete mode 100644 tests/components/wk2168_spi/test.esp32-s3-idf.yaml delete mode 100644 tests/components/wk2204_i2c/test.esp32-s3-idf.yaml delete mode 100644 tests/components/wk2204_spi/test.esp32-s3-idf.yaml delete mode 100644 tests/components/wk2212_i2c/test.esp32-s3-idf.yaml delete mode 100644 tests/components/wk2212_spi/test.esp32-s3-idf.yaml diff --git a/tests/components/binary_sensor/test.esp32-s3-idf.yaml b/tests/components/binary_sensor/test.esp32-s3-idf.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/binary_sensor/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml deleted file mode 100644 index 0fd8684a2c..0000000000 --- a/tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/debug/test.esp32-s3-idf.yaml b/tests/components/debug/test.esp32-s3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/debug/test.esp32-s3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/matrix_keypad/test.esp32-s3-idf.yaml b/tests/components/matrix_keypad/test.esp32-s3-idf.yaml deleted file mode 100644 index a491f2ed59..0000000000 --- a/tests/components/matrix_keypad/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,15 +0,0 @@ -packages: - common: !include common.yaml - -matrix_keypad: - id: keypad - rows: - - pin: 10 - - pin: 11 - columns: - - pin: 12 - - pin: 13 - keys: "1234" - has_pulldowns: true - on_key: - - lambda: ESP_LOGI("KEY", "key %d pressed", x); diff --git a/tests/components/mcp3221/test.esp32-s3-idf.yaml b/tests/components/mcp3221/test.esp32-s3-idf.yaml deleted file mode 100644 index 0fd8684a2c..0000000000 --- a/tests/components/mcp3221/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp32-s3-idf.yaml b/tests/components/mlx90393/test.esp32-s3-idf.yaml deleted file mode 100644 index 0fd8684a2c..0000000000 --- a/tests/components/mlx90393/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/npi19/test.esp32-s3-idf.yaml b/tests/components/npi19/test.esp32-s3-idf.yaml deleted file mode 100644 index 0fd8684a2c..0000000000 --- a/tests/components/npi19/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-s3-idf.yaml b/tests/components/ntc/test.esp32-s3-idf.yaml deleted file mode 100644 index 37fb325f4a..0000000000 --- a/tests/components/ntc/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO4 - -<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-s3-idf.yaml b/tests/components/resistance/test.esp32-s3-idf.yaml deleted file mode 100644 index 1910f325ae..0000000000 --- a/tests/components/resistance/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,4 +0,0 @@ -substitutions: - pin: GPIO1 - -<<: !include common.yaml diff --git a/tests/components/switch/test.esp32-s3-idf.yaml b/tests/components/switch/test.esp32-s3-idf.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/switch/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/tem3200/test.esp32-s3-idf.yaml b/tests/components/tem3200/test.esp32-s3-idf.yaml deleted file mode 100644 index e9d826aa7c..0000000000 --- a/tests/components/tem3200/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,8 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/template/test.esp32-s3-idf.yaml b/tests/components/template/test.esp32-s3-idf.yaml deleted file mode 100644 index 25cb37a0b4..0000000000 --- a/tests/components/template/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,2 +0,0 @@ -packages: - common: !include common.yaml diff --git a/tests/components/uart/test-uart_max_with_usb_serial_jtag.esp32-s3-idf.yaml b/tests/components/uart/test-uart_max_with_usb_serial_jtag.esp32-s3-idf.yaml deleted file mode 100644 index 88a806eb92..0000000000 --- a/tests/components/uart/test-uart_max_with_usb_serial_jtag.esp32-s3-idf.yaml +++ /dev/null @@ -1,48 +0,0 @@ -<<: !include ../logger/common-usb_serial_jtag.yaml - -esphome: - on_boot: - then: - - uart.write: - id: uart_1 - data: 'Hello World' - - uart.write: - id: uart_1 - data: [0x00, 0x20, 0x42] - -uart: - - id: uart_1 - tx_pin: 4 - rx_pin: 5 - flow_control_pin: 6 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 - - - id: uart_2 - tx_pin: 7 - rx_pin: 8 - flow_control_pin: 9 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 - - - id: uart_3 - tx_pin: 10 - rx_pin: 11 - flow_control_pin: 12 - baud_rate: 9600 - data_bits: 8 - rx_buffer_size: 512 - rx_full_threshold: 10 - rx_timeout: 1 - parity: EVEN - stop_bits: 2 diff --git a/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml deleted file mode 100644 index d7b149a6fd..0000000000 --- a/tests/components/wk2132_i2c/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml - uart_bridge_2: !include ../../test_build_components/common/uart_bridge_2/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wk2132_spi/test.esp32-s3-idf.yaml b/tests/components/wk2132_spi/test.esp32-s3-idf.yaml deleted file mode 100644 index 9c7d36996e..0000000000 --- a/tests/components/wk2132_spi/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - clk_pin: GPIO40 - miso_pin: GPIO41 - mosi_pin: GPIO6 - cs_pin: GPIO19 - -packages: - spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml - uart_bridge_2: !include ../../test_build_components/common/uart_bridge_2/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml deleted file mode 100644 index 115812be97..0000000000 --- a/tests/components/wk2168_i2c/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml - uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wk2168_spi/test.esp32-s3-idf.yaml b/tests/components/wk2168_spi/test.esp32-s3-idf.yaml deleted file mode 100644 index 374fe64d16..0000000000 --- a/tests/components/wk2168_spi/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - clk_pin: GPIO40 - miso_pin: GPIO41 - mosi_pin: GPIO6 - cs_pin: GPIO19 - -packages: - spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml - uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml deleted file mode 100644 index 115812be97..0000000000 --- a/tests/components/wk2204_i2c/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml - uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wk2204_spi/test.esp32-s3-idf.yaml b/tests/components/wk2204_spi/test.esp32-s3-idf.yaml deleted file mode 100644 index 374fe64d16..0000000000 --- a/tests/components/wk2204_spi/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - clk_pin: GPIO40 - miso_pin: GPIO41 - mosi_pin: GPIO6 - cs_pin: GPIO19 - -packages: - spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml - uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml b/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml deleted file mode 100644 index 115812be97..0000000000 --- a/tests/components/wk2212_i2c/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -substitutions: - scl_pin: GPIO40 - sda_pin: GPIO41 - -packages: - i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml - uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml - -<<: !include common.yaml diff --git a/tests/components/wk2212_spi/test.esp32-s3-idf.yaml b/tests/components/wk2212_spi/test.esp32-s3-idf.yaml deleted file mode 100644 index 374fe64d16..0000000000 --- a/tests/components/wk2212_spi/test.esp32-s3-idf.yaml +++ /dev/null @@ -1,11 +0,0 @@ -substitutions: - clk_pin: GPIO40 - miso_pin: GPIO41 - mosi_pin: GPIO6 - cs_pin: GPIO19 - -packages: - spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml - uart_bridge_4: !include ../../test_build_components/common/uart_bridge_4/esp32-s3-idf.yaml - -<<: !include common.yaml From 4f088c93c9f9135cb948dd6317b8024eeeeb888a Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 12 Nov 2025 10:41:22 -0500 Subject: [PATCH 486/526] [esp32] Update the recommended platform to 55.03.31-2 (#11865) --- esphome/components/esp32/__init__.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 6981662d77..61511cba0c 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -334,12 +334,14 @@ def _is_framework_url(source: str) -> str: # - https://github.com/espressif/arduino-esp32/releases ARDUINO_FRAMEWORK_VERSION_LOOKUP = { "recommended": cv.Version(3, 3, 2), - "latest": cv.Version(3, 3, 2), - "dev": cv.Version(3, 3, 2), + "latest": cv.Version(3, 3, 4), + "dev": cv.Version(3, 3, 4), } ARDUINO_PLATFORM_VERSION_LOOKUP = { - cv.Version(3, 3, 2): cv.Version(55, 3, 31, "1"), - cv.Version(3, 3, 1): cv.Version(55, 3, 31, "1"), + cv.Version(3, 3, 4): cv.Version(55, 3, 31, "2"), + cv.Version(3, 3, 3): cv.Version(55, 3, 31, "2"), + cv.Version(3, 3, 2): cv.Version(55, 3, 31, "2"), + cv.Version(3, 3, 1): cv.Version(55, 3, 31, "2"), cv.Version(3, 3, 0): cv.Version(55, 3, 30, "2"), cv.Version(3, 2, 1): cv.Version(54, 3, 21, "2"), cv.Version(3, 2, 0): cv.Version(54, 3, 20), @@ -357,8 +359,8 @@ ESP_IDF_FRAMEWORK_VERSION_LOOKUP = { "dev": cv.Version(5, 5, 1), } ESP_IDF_PLATFORM_VERSION_LOOKUP = { - cv.Version(5, 5, 1): cv.Version(55, 3, 31, "1"), - cv.Version(5, 5, 0): cv.Version(55, 3, 31, "1"), + cv.Version(5, 5, 1): cv.Version(55, 3, 31, "2"), + cv.Version(5, 5, 0): cv.Version(55, 3, 31, "2"), cv.Version(5, 4, 3): cv.Version(55, 3, 32), cv.Version(5, 4, 2): cv.Version(54, 3, 21, "2"), cv.Version(5, 4, 1): cv.Version(54, 3, 21, "2"), @@ -373,9 +375,9 @@ ESP_IDF_PLATFORM_VERSION_LOOKUP = { # The platform-espressif32 version # - https://github.com/pioarduino/platform-espressif32/releases PLATFORM_VERSION_LOOKUP = { - "recommended": cv.Version(55, 3, 31, "1"), - "latest": cv.Version(55, 3, 31, "1"), - "dev": cv.Version(55, 3, 31, "1"), + "recommended": cv.Version(55, 3, 31, "2"), + "latest": cv.Version(55, 3, 31, "2"), + "dev": cv.Version(55, 3, 31, "2"), } From a859ecaad1cb64d7648c5293771c93109f58c2c5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Nov 2025 11:56:19 -0600 Subject: [PATCH 487/526] [core] Fix wait_until hanging when used in on_boot automations (#11869) --- esphome/core/base_automation.h | 7 +- .../fixtures/wait_until_on_boot.yaml | 47 ++++++++++ tests/integration/test_wait_until_on_boot.py | 91 +++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 tests/integration/fixtures/wait_until_on_boot.yaml create mode 100644 tests/integration/test_wait_until_on_boot.py diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index 6f392c8959..a5e6139182 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -412,7 +412,12 @@ template class WaitUntilAction : public Action, public Co void setup() override { // Start with loop disabled - only enable when there's work to do - this->disable_loop(); + // IMPORTANT: Only disable if num_running_ is 0, otherwise play_complex() was already + // called before our setup() (e.g., from on_boot trigger at same priority level) + // and we must not undo its enable_loop() call + if (this->num_running_ == 0) { + this->disable_loop(); + } } void play_complex(const Ts &...x) override { diff --git a/tests/integration/fixtures/wait_until_on_boot.yaml b/tests/integration/fixtures/wait_until_on_boot.yaml new file mode 100644 index 0000000000..358bef971b --- /dev/null +++ b/tests/integration/fixtures/wait_until_on_boot.yaml @@ -0,0 +1,47 @@ +# Test for wait_until in on_boot automation +# Reproduces bug where wait_until in on_boot would hang forever +# because WaitUntilAction::setup() would disable_loop() after +# play_complex() had already enabled it. + +esphome: + name: wait-until-on-boot + on_boot: + then: + - logger.log: "on_boot: Starting wait_until test" + - globals.set: + id: on_boot_started + value: 'true' + - wait_until: + condition: + lambda: return id(test_flag); + timeout: 5s + - logger.log: "on_boot: wait_until completed successfully" + +host: + +logger: + level: DEBUG + +globals: + - id: on_boot_started + type: bool + initial_value: 'false' + - id: test_flag + type: bool + initial_value: 'false' + +api: + actions: + - action: set_test_flag + then: + - globals.set: + id: test_flag + value: 'true' + - action: check_on_boot_started + then: + - lambda: |- + if (id(on_boot_started)) { + ESP_LOGI("test", "on_boot has started"); + } else { + ESP_LOGI("test", "on_boot has NOT started"); + } diff --git a/tests/integration/test_wait_until_on_boot.py b/tests/integration/test_wait_until_on_boot.py new file mode 100644 index 0000000000..b42c530c54 --- /dev/null +++ b/tests/integration/test_wait_until_on_boot.py @@ -0,0 +1,91 @@ +"""Integration test for wait_until in on_boot automation. + +This test validates that wait_until works correctly when triggered from on_boot, +which runs at the same setup priority as WaitUntilAction itself. This was broken +before the fix because WaitUntilAction::setup() would unconditionally disable_loop(), +even if play_complex() had already been called and enabled the loop. + +The bug: on_boot fires during StartupTrigger::setup(), which calls WaitUntilAction::play_complex() +before WaitUntilAction::setup() has run. Then when WaitUntilAction::setup() runs, it calls +disable_loop(), undoing the enable_loop() from play_complex(), causing wait_until to hang forever. +""" + +from __future__ import annotations + +import asyncio +import re + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_wait_until_on_boot( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that wait_until works in on_boot automation with a condition that becomes true later.""" + loop = asyncio.get_running_loop() + + on_boot_started = False + on_boot_completed = False + + on_boot_started_pattern = re.compile(r"on_boot: Starting wait_until test") + on_boot_complete_pattern = re.compile(r"on_boot: wait_until completed successfully") + + on_boot_started_future = loop.create_future() + on_boot_complete_future = loop.create_future() + + def check_output(line: str) -> None: + """Check log output for test progress.""" + nonlocal on_boot_started, on_boot_completed + + if on_boot_started_pattern.search(line): + on_boot_started = True + if not on_boot_started_future.done(): + on_boot_started_future.set_result(True) + + if on_boot_complete_pattern.search(line): + on_boot_completed = True + if not on_boot_complete_future.done(): + on_boot_complete_future.set_result(True) + + async with ( + run_compiled(yaml_config, line_callback=check_output), + api_client_connected() as client, + ): + # Wait for on_boot to start + await asyncio.wait_for(on_boot_started_future, timeout=10.0) + assert on_boot_started, "on_boot did not start" + + # At this point, on_boot is blocked in wait_until waiting for test_flag to become true + # If the bug exists, wait_until's loop is disabled and it will never complete + # even after we set the flag + + # Give a moment for setup to complete + await asyncio.sleep(0.5) + + # Now set the flag that wait_until is waiting for + _, services = await client.list_entities_services() + set_flag_service = next( + (s for s in services if s.name == "set_test_flag"), None + ) + assert set_flag_service is not None, "set_test_flag service not found" + + client.execute_service(set_flag_service, {}) + + # If the fix works, wait_until's loop() will check the condition and proceed + # If the bug exists, wait_until is stuck with disabled loop and will timeout + try: + await asyncio.wait_for(on_boot_complete_future, timeout=2.0) + assert on_boot_completed, ( + "on_boot wait_until did not complete after flag was set" + ) + except TimeoutError: + pytest.fail( + "wait_until in on_boot did not complete within 2s after condition became true. " + "This indicates the bug where WaitUntilAction::setup() disables the loop " + "after play_complex() has already enabled it." + ) From 6df0264d51d7b0be1cecb2870cf076b55e0b9092 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Nov 2025 14:26:36 -0600 Subject: [PATCH 488/526] [api] Eliminate heap allocations when transmitting Event types (#11773) --- esphome/components/api/api_connection.cpp | 14 +++---- esphome/components/api/api_connection.h | 48 ++++------------------- 2 files changed, 14 insertions(+), 48 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 7eb61f08b6..ca9ddaedf4 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1294,11 +1294,11 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe #endif #ifdef USE_EVENT -void APIConnection::send_event(event::Event *event, const std::string &event_type) { +void APIConnection::send_event(event::Event *event, const char *event_type) { this->schedule_message_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE, EventResponse::ESTIMATED_SIZE); } -uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn, +uint16_t APIConnection::try_send_event_response(event::Event *event, const char *event_type, APIConnection *conn, uint32_t remaining_size, bool is_single) { EventResponse resp; resp.set_event_type(StringRef(event_type)); @@ -1650,9 +1650,7 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator c // O(n) but optimized for RAM and not performance. for (auto &item : items) { if (item.entity == entity && item.message_type == message_type) { - // Clean up old creator before replacing - item.creator.cleanup(message_type); - // Move assign the new creator + // Replace with new creator item.creator = std::move(creator); return; } @@ -1822,7 +1820,7 @@ void APIConnection::process_batch_() { // Handle remaining items more efficiently if (items_processed < this->deferred_batch_.size()) { - // Remove processed items from the beginning with proper cleanup + // Remove processed items from the beginning this->deferred_batch_.remove_front(items_processed); // Reschedule for remaining items this->schedule_batch_(); @@ -1835,10 +1833,10 @@ void APIConnection::process_batch_() { uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single, uint8_t message_type) const { #ifdef USE_EVENT - // Special case: EventResponse uses string pointer + // Special case: EventResponse uses const char * pointer if (message_type == EventResponse::MESSAGE_TYPE) { auto *e = static_cast(entity); - return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single); + return APIConnection::try_send_event_response(e, data_.const_char_ptr, conn, remaining_size, is_single); } #endif diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 284fa11a95..a77c93a2d5 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -177,7 +177,7 @@ class APIConnection final : public APIServerConnection { #endif #ifdef USE_EVENT - void send_event(event::Event *event, const std::string &event_type); + void send_event(event::Event *event, const char *event_type); #endif #ifdef USE_UPDATE @@ -450,7 +450,7 @@ class APIConnection final : public APIServerConnection { bool is_single); #endif #ifdef USE_EVENT - static uint16_t try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn, + static uint16_t try_send_event_response(event::Event *event, const char *event_type, APIConnection *conn, uint32_t remaining_size, bool is_single); static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); #endif @@ -508,10 +508,8 @@ class APIConnection final : public APIServerConnection { // Constructor for function pointer MessageCreator(MessageCreatorPtr ptr) { data_.function_ptr = ptr; } - // Constructor for string state capture - explicit MessageCreator(const std::string &str_value) { data_.string_ptr = new std::string(str_value); } - - // No destructor - cleanup must be called explicitly with message_type + // Constructor for const char * (Event types - no allocation needed) + explicit MessageCreator(const char *str_value) { data_.const_char_ptr = str_value; } // Delete copy operations - MessageCreator should only be moved MessageCreator(const MessageCreator &other) = delete; @@ -523,8 +521,6 @@ class APIConnection final : public APIServerConnection { // Move assignment MessageCreator &operator=(MessageCreator &&other) noexcept { if (this != &other) { - // IMPORTANT: Caller must ensure cleanup() was called if this contains a string! - // In our usage, this happens in add_item() deduplication and vector::erase() data_ = other.data_; other.data_.function_ptr = nullptr; } @@ -535,20 +531,10 @@ class APIConnection final : public APIServerConnection { uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single, uint8_t message_type) const; - // Manual cleanup method - must be called before destruction for string types - void cleanup(uint8_t message_type) { -#ifdef USE_EVENT - if (message_type == EventResponse::MESSAGE_TYPE && data_.string_ptr != nullptr) { - delete data_.string_ptr; - data_.string_ptr = nullptr; - } -#endif - } - private: union Data { MessageCreatorPtr function_ptr; - std::string *string_ptr; + const char *const_char_ptr; } data_; // 4 bytes on 32-bit, 8 bytes on 64-bit - same as before }; @@ -568,42 +554,24 @@ class APIConnection final : public APIServerConnection { std::vector items; uint32_t batch_start_time{0}; - private: - // Helper to cleanup items from the beginning - void cleanup_items_(size_t count) { - for (size_t i = 0; i < count; i++) { - items[i].creator.cleanup(items[i].message_type); - } - } - - public: DeferredBatch() { // Pre-allocate capacity for typical batch sizes to avoid reallocation items.reserve(8); } - ~DeferredBatch() { - // Ensure cleanup of any remaining items - clear(); - } - // Add item to the batch void add_item(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size); // Add item to the front of the batch (for high priority messages like ping) void add_item_front(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size); - // Clear all items with proper cleanup + // Clear all items void clear() { - cleanup_items_(items.size()); items.clear(); batch_start_time = 0; } - // Remove processed items from the front with proper cleanup - void remove_front(size_t count) { - cleanup_items_(count); - items.erase(items.begin(), items.begin() + count); - } + // Remove processed items from the front + void remove_front(size_t count) { items.erase(items.begin(), items.begin() + count); } bool empty() const { return items.empty(); } size_t size() const { return items.size(); } From 799cfe1de4353022304af4e9c403c69119735c0c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Nov 2025 14:26:46 -0600 Subject: [PATCH 489/526] [esp32_ble_tracker] Use initializer_list to eliminate compiler warning and reduce flash usage (#11861) --- esphome/components/esp32_ble_tracker/automation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32_ble_tracker/automation.h b/esphome/components/esp32_ble_tracker/automation.h index 054cbaa7df..bbf7992fa4 100644 --- a/esphome/components/esp32_ble_tracker/automation.h +++ b/esphome/components/esp32_ble_tracker/automation.h @@ -10,7 +10,7 @@ namespace esphome::esp32_ble_tracker { class ESPBTAdvertiseTrigger : public Trigger, public ESPBTDeviceListener { public: explicit ESPBTAdvertiseTrigger(ESP32BLETracker *parent) { parent->register_listener(this); } - void set_addresses(const std::vector &addresses) { this->address_vec_ = addresses; } + void set_addresses(std::initializer_list addresses) { this->address_vec_ = addresses; } bool parse_device(const ESPBTDevice &device) override { uint64_t u64_addr = device.address_uint64(); From 5a2e6697e0ed9a11494eb52a776238b878b69e4b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Nov 2025 17:42:50 -0600 Subject: [PATCH 490/526] [api][event] Send events immediately to prevent loss during rapid triggers (#11777) --- esphome/components/api/api_connection.cpp | 4 +- esphome/components/api/api_connection.h | 54 ++++++++++++++++++----- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index ca9ddaedf4..b4230576de 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1295,8 +1295,8 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe #ifdef USE_EVENT void APIConnection::send_event(event::Event *event, const char *event_type) { - this->schedule_message_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE, - EventResponse::ESTIMATED_SIZE); + this->send_message_smart_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE, + EventResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_event_response(event::Event *event, const char *event_type, APIConnection *conn, uint32_t remaining_size, bool is_single) { diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index a77c93a2d5..6cfd108927 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -650,21 +650,30 @@ class APIConnection final : public APIServerConnection { } #endif + // Helper to check if a message type should bypass batching + // Returns true if: + // 1. It's an UpdateStateResponse (always send immediately to handle cases where + // the main loop is blocked, e.g., during OTA updates) + // 2. It's an EventResponse (events are edge-triggered - every occurrence matters) + // 3. OR: User has opted into immediate sending (should_try_send_immediately = true + // AND batch_delay = 0) + inline bool should_send_immediately_(uint8_t message_type) const { + return ( +#ifdef USE_UPDATE + message_type == UpdateStateResponse::MESSAGE_TYPE || +#endif +#ifdef USE_EVENT + message_type == EventResponse::MESSAGE_TYPE || +#endif + (this->flags_.should_try_send_immediately && this->get_batch_delay_ms_() == 0)); + } + // Helper method to send a message either immediately or via batching + // Tries immediate send if should_send_immediately_() returns true and buffer has space + // Falls back to batching if immediate send fails or isn't applicable bool send_message_smart_(EntityBase *entity, MessageCreatorPtr creator, uint8_t message_type, uint8_t estimated_size) { - // Try to send immediately if: - // 1. It's an UpdateStateResponse (always send immediately to handle cases where - // the main loop is blocked, e.g., during OTA updates) - // 2. OR: We should try to send immediately (should_try_send_immediately = true) - // AND Batch delay is 0 (user has opted in to immediate sending) - // 3. AND: Buffer has space available - if (( -#ifdef USE_UPDATE - message_type == UpdateStateResponse::MESSAGE_TYPE || -#endif - (this->flags_.should_try_send_immediately && this->get_batch_delay_ms_() == 0)) && - this->helper_->can_write_without_blocking()) { + if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) { // Now actually encode and send if (creator(entity, this, MAX_BATCH_PACKET_SIZE, true) && this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) { @@ -682,6 +691,27 @@ class APIConnection final : public APIServerConnection { return this->schedule_message_(entity, creator, message_type, estimated_size); } + // Overload for MessageCreator (used by events which need to capture event_type) + bool send_message_smart_(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size) { + // Try to send immediately if message type should bypass batching and buffer has space + if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) { + // Now actually encode and send + if (creator(entity, this, MAX_BATCH_PACKET_SIZE, true, message_type) && + this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) { +#ifdef HAS_PROTO_MESSAGE_DUMP + // Log the message in verbose mode + this->log_proto_message_(entity, creator, message_type); +#endif + return true; + } + + // If immediate send failed, fall through to batching + } + + // Fall back to scheduled batching + return this->schedule_message_(entity, std::move(creator), message_type, estimated_size); + } + // Helper function to schedule a deferred message with known message type bool schedule_message_(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size) { this->deferred_batch_.add_item(entity, std::move(creator), message_type, estimated_size); From 72da3d0f1e3e202f7546299316bf242fbc81b439 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Nov 2025 17:55:06 -0600 Subject: [PATCH 491/526] [thermostat] Replace std::map with FixedVector, reduce flash usage (#11875) --- esphome/components/thermostat/climate.py | 38 ++++++++- .../thermostat/thermostat_climate.cpp | 82 +++++++++++------- .../thermostat/thermostat_climate.h | 33 +++++-- .../climate_custom_fan_modes_and_presets.yaml | 1 + .../integration/test_climate_custom_modes.py | 85 ++++++++++++++++++- 5 files changed, 195 insertions(+), 44 deletions(-) diff --git a/esphome/components/thermostat/climate.py b/esphome/components/thermostat/climate.py index a928d208f3..a3c155aac0 100644 --- a/esphome/components/thermostat/climate.py +++ b/esphome/components/thermostat/climate.py @@ -945,6 +945,10 @@ async def to_code(config): cg.add(var.set_humidity_hysteresis(config[CONF_HUMIDITY_HYSTERESIS])) if CONF_PRESET in config: + # Separate standard and custom presets, and build preset config variables + standard_presets: list[tuple[cg.MockObj, cg.MockObj]] = [] + custom_presets: list[tuple[str, cg.MockObj]] = [] + for preset_config in config[CONF_PRESET]: name = preset_config[CONF_NAME] standard_preset = None @@ -987,9 +991,39 @@ async def to_code(config): ) if standard_preset is not None: - cg.add(var.set_preset_config(standard_preset, preset_target_variable)) + standard_presets.append((standard_preset, preset_target_variable)) else: - cg.add(var.set_custom_preset_config(name, preset_target_variable)) + custom_presets.append((name, preset_target_variable)) + + # Build initializer list for standard presets + if standard_presets: + cg.add( + var.set_preset_config( + [ + cg.StructInitializer( + thermostat_ns.struct("ThermostatPresetEntry"), + ("preset", preset), + ("config", preset_var), + ) + for preset, preset_var in standard_presets + ] + ) + ) + + # Build initializer list for custom presets + if custom_presets: + cg.add( + var.set_custom_preset_config( + [ + cg.StructInitializer( + thermostat_ns.struct("ThermostatCustomPresetEntry"), + ("name", cg.RawExpression(f'"{name}"')), + ("config", preset_var), + ) + for name, preset_var in custom_presets + ] + ) + ) if CONF_DEFAULT_PRESET in config: default_preset_name = config[CONF_DEFAULT_PRESET] diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index d533ef93ec..2b51f58f4f 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -53,8 +53,8 @@ void ThermostatClimate::setup() { if (use_default_preset) { if (this->default_preset_ != climate::ClimatePreset::CLIMATE_PRESET_NONE) { this->change_preset_(this->default_preset_); - } else if (!this->default_custom_preset_.empty()) { - this->change_custom_preset_(this->default_custom_preset_.c_str()); + } else if (this->default_custom_preset_ != nullptr) { + this->change_custom_preset_(this->default_custom_preset_); } } @@ -319,16 +319,16 @@ climate::ClimateTraits ThermostatClimate::traits() { if (this->supports_swing_mode_vertical_) traits.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL); - for (auto &it : this->preset_config_) { - traits.add_supported_preset(it.first); + for (const auto &entry : this->preset_config_) { + traits.add_supported_preset(entry.preset); } - // Extract custom preset names from the custom_preset_config_ map + // Extract custom preset names from the custom_preset_config_ vector if (!this->custom_preset_config_.empty()) { std::vector custom_preset_names; custom_preset_names.reserve(this->custom_preset_config_.size()); - for (const auto &it : this->custom_preset_config_) { - custom_preset_names.push_back(it.first.c_str()); + for (const auto &entry : this->custom_preset_config_) { + custom_preset_names.push_back(entry.name); } traits.set_supported_custom_presets(custom_preset_names); } @@ -1154,12 +1154,18 @@ void ThermostatClimate::dump_preset_config_(const char *preset_name, const Therm } void ThermostatClimate::change_preset_(climate::ClimatePreset preset) { - auto config = this->preset_config_.find(preset); + // Linear search through preset configurations + const ThermostatClimateTargetTempConfig *config = nullptr; + for (const auto &entry : this->preset_config_) { + if (entry.preset == preset) { + config = &entry.config; + break; + } + } - if (config != this->preset_config_.end()) { + if (config != nullptr) { ESP_LOGV(TAG, "Preset %s requested", LOG_STR_ARG(climate::climate_preset_to_string(preset))); - if (this->change_preset_internal_(config->second) || (!this->preset.has_value()) || - this->preset.value() != preset) { + if (this->change_preset_internal_(*config) || (!this->preset.has_value()) || this->preset.value() != preset) { // Fire any preset changed trigger if defined Trigger<> *trig = this->preset_change_trigger_; this->set_preset_(preset); @@ -1178,11 +1184,18 @@ void ThermostatClimate::change_preset_(climate::ClimatePreset preset) { } void ThermostatClimate::change_custom_preset_(const char *custom_preset) { - auto config = this->custom_preset_config_.find(custom_preset); + // Linear search through custom preset configurations + const ThermostatClimateTargetTempConfig *config = nullptr; + for (const auto &entry : this->custom_preset_config_) { + if (strcmp(entry.name, custom_preset) == 0) { + config = &entry.config; + break; + } + } - if (config != this->custom_preset_config_.end()) { + if (config != nullptr) { ESP_LOGV(TAG, "Custom preset %s requested", custom_preset); - if (this->change_preset_internal_(config->second) || !this->has_custom_preset() || + if (this->change_preset_internal_(*config) || !this->has_custom_preset() || strcmp(this->get_custom_preset(), custom_preset) != 0) { // Fire any preset changed trigger if defined Trigger<> *trig = this->preset_change_trigger_; @@ -1247,14 +1260,12 @@ bool ThermostatClimate::change_preset_internal_(const ThermostatClimateTargetTem return something_changed; } -void ThermostatClimate::set_preset_config(climate::ClimatePreset preset, - const ThermostatClimateTargetTempConfig &config) { - this->preset_config_[preset] = config; +void ThermostatClimate::set_preset_config(std::initializer_list presets) { + this->preset_config_ = presets; } -void ThermostatClimate::set_custom_preset_config(const std::string &name, - const ThermostatClimateTargetTempConfig &config) { - this->custom_preset_config_[name] = config; +void ThermostatClimate::set_custom_preset_config(std::initializer_list presets) { + this->custom_preset_config_ = presets; } ThermostatClimate::ThermostatClimate() @@ -1293,8 +1304,16 @@ ThermostatClimate::ThermostatClimate() humidity_control_humidify_action_trigger_(new Trigger<>()), humidity_control_off_action_trigger_(new Trigger<>()) {} -void ThermostatClimate::set_default_preset(const std::string &custom_preset) { - this->default_custom_preset_ = custom_preset; +void ThermostatClimate::set_default_preset(const char *custom_preset) { + // Find the preset in custom_preset_config_ and store pointer from there + for (const auto &entry : this->custom_preset_config_) { + if (strcmp(entry.name, custom_preset) == 0) { + this->default_custom_preset_ = entry.name; + return; + } + } + // If not found, it will be caught during validation + this->default_custom_preset_ = nullptr; } void ThermostatClimate::set_default_preset(climate::ClimatePreset preset) { this->default_preset_ = preset; } @@ -1605,19 +1624,22 @@ void ThermostatClimate::dump_config() { if (!this->preset_config_.empty()) { ESP_LOGCONFIG(TAG, " Supported PRESETS:"); - for (auto &it : this->preset_config_) { - const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(it.first)); - ESP_LOGCONFIG(TAG, " %s:%s", preset_name, it.first == this->default_preset_ ? " (default)" : ""); - this->dump_preset_config_(preset_name, it.second); + for (const auto &entry : this->preset_config_) { + const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(entry.preset)); + ESP_LOGCONFIG(TAG, " %s:%s", preset_name, entry.preset == this->default_preset_ ? " (default)" : ""); + this->dump_preset_config_(preset_name, entry.config); } } if (!this->custom_preset_config_.empty()) { ESP_LOGCONFIG(TAG, " Supported CUSTOM PRESETS:"); - for (auto &it : this->custom_preset_config_) { - const auto *preset_name = it.first.c_str(); - ESP_LOGCONFIG(TAG, " %s:%s", preset_name, it.first == this->default_custom_preset_ ? " (default)" : ""); - this->dump_preset_config_(preset_name, it.second); + for (const auto &entry : this->custom_preset_config_) { + const auto *preset_name = entry.name; + ESP_LOGCONFIG(TAG, " %s:%s", preset_name, + (this->default_custom_preset_ != nullptr && strcmp(entry.name, this->default_custom_preset_) == 0) + ? " (default)" + : ""); + this->dump_preset_config_(preset_name, entry.config); } } } diff --git a/esphome/components/thermostat/thermostat_climate.h b/esphome/components/thermostat/thermostat_climate.h index c9795d9666..76391f800c 100644 --- a/esphome/components/thermostat/thermostat_climate.h +++ b/esphome/components/thermostat/thermostat_climate.h @@ -3,12 +3,12 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "esphome/components/climate/climate.h" #include "esphome/components/sensor/sensor.h" #include #include -#include namespace esphome { namespace thermostat { @@ -72,14 +72,29 @@ struct ThermostatClimateTargetTempConfig { optional mode_{}; }; +/// Entry for standard preset lookup +struct ThermostatPresetEntry { + climate::ClimatePreset preset; + ThermostatClimateTargetTempConfig config; +}; + +/// Entry for custom preset lookup +struct ThermostatCustomPresetEntry { + const char *name; + ThermostatClimateTargetTempConfig config; +}; + class ThermostatClimate : public climate::Climate, public Component { public: + using PresetEntry = ThermostatPresetEntry; + using CustomPresetEntry = ThermostatCustomPresetEntry; + ThermostatClimate(); void setup() override; void dump_config() override; void loop() override; - void set_default_preset(const std::string &custom_preset); + void set_default_preset(const char *custom_preset); void set_default_preset(climate::ClimatePreset preset); void set_on_boot_restore_from(OnBootRestoreFrom on_boot_restore_from); void set_set_point_minimum_differential(float differential); @@ -131,8 +146,8 @@ class ThermostatClimate : public climate::Climate, public Component { void set_supports_humidification(bool supports_humidification); void set_supports_two_points(bool supports_two_points); - void set_preset_config(climate::ClimatePreset preset, const ThermostatClimateTargetTempConfig &config); - void set_custom_preset_config(const std::string &name, const ThermostatClimateTargetTempConfig &config); + void set_preset_config(std::initializer_list presets); + void set_custom_preset_config(std::initializer_list presets); Trigger<> *get_cool_action_trigger() const; Trigger<> *get_supplemental_cool_action_trigger() const; @@ -516,9 +531,6 @@ class ThermostatClimate : public climate::Climate, public Component { Trigger<> *prev_swing_mode_trigger_{nullptr}; Trigger<> *prev_humidity_control_trigger_{nullptr}; - /// Default custom preset to use on start up - std::string default_custom_preset_{}; - /// Climate action timers std::array timer_{ ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)), @@ -534,9 +546,12 @@ class ThermostatClimate : public climate::Climate, public Component { }; /// The set of standard preset configurations this thermostat supports (Eg. AWAY, ECO, etc) - std::map preset_config_{}; + FixedVector preset_config_{}; /// The set of custom preset configurations this thermostat supports (eg. "My Custom Preset") - std::map custom_preset_config_{}; + FixedVector custom_preset_config_{}; + /// Default custom preset to use on start up (pointer to entry in custom_preset_config_) + private: + const char *default_custom_preset_{nullptr}; }; } // namespace thermostat diff --git a/tests/integration/fixtures/climate_custom_fan_modes_and_presets.yaml b/tests/integration/fixtures/climate_custom_fan_modes_and_presets.yaml index bf4ef9eafd..3996d0f169 100644 --- a/tests/integration/fixtures/climate_custom_fan_modes_and_presets.yaml +++ b/tests/integration/fixtures/climate_custom_fan_modes_and_presets.yaml @@ -14,6 +14,7 @@ climate: id: test_thermostat name: Test Thermostat Custom Modes sensor: thermostat_sensor + default_preset: "Eco Plus" preset: - name: Away default_target_temperature_low: 16°C diff --git a/tests/integration/test_climate_custom_modes.py b/tests/integration/test_climate_custom_modes.py index ce34959d88..67a7b0581a 100644 --- a/tests/integration/test_climate_custom_modes.py +++ b/tests/integration/test_climate_custom_modes.py @@ -2,9 +2,13 @@ from __future__ import annotations -from aioesphomeapi import ClimateInfo, ClimatePreset +import asyncio + +import aioesphomeapi +from aioesphomeapi import ClimateInfo, ClimatePreset, EntityState import pytest +from .state_utils import InitialStateHelper from .types import APIClientConnectedFactory, RunCompiledFunction @@ -14,15 +18,50 @@ async def test_climate_custom_fan_modes_and_presets( run_compiled: RunCompiledFunction, api_client_connected: APIClientConnectedFactory, ) -> None: - """Test that custom presets are properly exposed via API.""" + """Test that custom presets are properly exposed and can be changed.""" + loop = asyncio.get_running_loop() async with run_compiled(yaml_config), api_client_connected() as client: - # Get entities and services + states: dict[int, EntityState] = {} + super_saver_future: asyncio.Future[EntityState] = loop.create_future() + vacation_future: asyncio.Future[EntityState] = loop.create_future() + + def on_state(state: EntityState) -> None: + states[state.key] = state + if isinstance(state, aioesphomeapi.ClimateState): + # Wait for Super Saver preset + if ( + state.custom_preset == "Super Saver" + and state.target_temperature_low == 20.0 + and state.target_temperature_high == 24.0 + and not super_saver_future.done() + ): + super_saver_future.set_result(state) + # Wait for Vacation Mode preset + elif ( + state.custom_preset == "Vacation Mode" + and state.target_temperature_low == 15.0 + and state.target_temperature_high == 18.0 + and not vacation_future.done() + ): + vacation_future.set_result(state) + + # Get entities and set up state synchronization entities, services = await client.list_entities_services() + initial_state_helper = InitialStateHelper(entities) climate_infos = [e for e in entities if isinstance(e, ClimateInfo)] assert len(climate_infos) == 1, "Expected exactly 1 climate entity" test_climate = climate_infos[0] + # Subscribe with the wrapper that filters initial states + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + + # Wait for all initial states to be broadcast + try: + await initial_state_helper.wait_for_initial_states() + except TimeoutError: + pytest.fail("Timeout waiting for initial states") + # Verify enum presets are exposed (from preset: config map) assert ClimatePreset.AWAY in test_climate.supported_presets, ( "Expected AWAY in enum presets" @@ -40,3 +79,43 @@ async def test_climate_custom_fan_modes_and_presets( assert "Vacation Mode" in custom_presets, ( "Expected 'Vacation Mode' in custom presets" ) + + # Get initial state and verify default preset + initial_state = initial_state_helper.initial_states.get(test_climate.key) + assert initial_state is not None, "Climate initial state not found" + assert isinstance(initial_state, aioesphomeapi.ClimateState) + assert initial_state.custom_preset == "Eco Plus", ( + f"Expected default preset 'Eco Plus', got '{initial_state.custom_preset}'" + ) + assert initial_state.target_temperature_low == 18.0, ( + f"Expected low temp 18.0, got {initial_state.target_temperature_low}" + ) + assert initial_state.target_temperature_high == 22.0, ( + f"Expected high temp 22.0, got {initial_state.target_temperature_high}" + ) + + # Test changing to "Super Saver" custom preset + client.climate_command(test_climate.key, custom_preset="Super Saver") + + try: + super_saver_state = await asyncio.wait_for(super_saver_future, timeout=5.0) + except TimeoutError: + pytest.fail("Super Saver preset change not received within 5 seconds") + + assert isinstance(super_saver_state, aioesphomeapi.ClimateState) + assert super_saver_state.custom_preset == "Super Saver" + assert super_saver_state.target_temperature_low == 20.0 + assert super_saver_state.target_temperature_high == 24.0 + + # Test changing to "Vacation Mode" custom preset + client.climate_command(test_climate.key, custom_preset="Vacation Mode") + + try: + vacation_state = await asyncio.wait_for(vacation_future, timeout=5.0) + except TimeoutError: + pytest.fail("Vacation Mode preset change not received within 5 seconds") + + assert isinstance(vacation_state, aioesphomeapi.ClimateState) + assert vacation_state.custom_preset == "Vacation Mode" + assert vacation_state.target_temperature_low == 15.0 + assert vacation_state.target_temperature_high == 18.0 From ff107a0674091d24e51a9f2c3fd04857d857b90a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Nov 2025 18:40:26 -0600 Subject: [PATCH 492/526] [mqtt] Fix crash with empty broker during upload/logs (#11866) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/mqtt.py | 13 ++++- tests/unit_tests/test_main.py | 50 +++++++++++++++++++ tests/unit_tests/test_mqtt.py | 91 +++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 tests/unit_tests/test_mqtt.py diff --git a/esphome/mqtt.py b/esphome/mqtt.py index 093ee64df4..0d50edbc2c 100644 --- a/esphome/mqtt.py +++ b/esphome/mqtt.py @@ -30,6 +30,7 @@ from esphome.const import ( from esphome.core import CORE, EsphomeError from esphome.helpers import get_int_env, get_str_env from esphome.log import AnsiFore, color +from esphome.types import ConfigType from esphome.util import safe_print _LOGGER = logging.getLogger(__name__) @@ -154,8 +155,12 @@ def show_discover(config, username=None, password=None, client_id=None): def get_esphome_device_ip( - config, username=None, password=None, client_id=None, timeout=25 -): + config: ConfigType, + username: str | None = None, + password: str | None = None, + client_id: str | None = None, + timeout: int | float = 25, +) -> list[str]: if CONF_MQTT not in config: raise EsphomeError( "Cannot discover IP via MQTT as the config does not include the mqtt: " @@ -166,6 +171,10 @@ def get_esphome_device_ip( "Cannot discover IP via MQTT as the config does not include the device name: " "component" ) + if not config[CONF_MQTT].get(CONF_BROKER): + raise EsphomeError( + "Cannot discover IP via MQTT as the broker is not configured" + ) dev_name = config[CONF_ESPHOME][CONF_NAME] dev_ip = None diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index 9e5f399381..ccbc5a1306 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -1166,6 +1166,56 @@ def test_upload_program_ota_with_mqtt_resolution( ) +def test_upload_program_ota_with_mqtt_empty_broker( + mock_mqtt_get_ip: Mock, + mock_is_ip_address: Mock, + mock_run_ota: Mock, + tmp_path: Path, + caplog: CaptureFixture, +) -> None: + """Test upload_program with OTA when MQTT broker is empty (issue #11653).""" + setup_core(address="192.168.1.50", platform=PLATFORM_ESP32, tmp_path=tmp_path) + + mock_is_ip_address.return_value = True + mock_mqtt_get_ip.side_effect = EsphomeError( + "Cannot discover IP via MQTT as the broker is not configured" + ) + mock_run_ota.return_value = (0, "192.168.1.50") + + config = { + CONF_OTA: [ + { + CONF_PLATFORM: CONF_ESPHOME, + CONF_PORT: 3232, + } + ], + CONF_MQTT: { + CONF_BROKER: "", + }, + CONF_MDNS: { + CONF_DISABLED: True, + }, + } + args = MockArgs(username="user", password="pass", client_id="client") + devices = ["MQTTIP", "192.168.1.50"] + + exit_code, host = upload_program(config, args, devices) + + assert exit_code == 0 + assert host == "192.168.1.50" + # Verify MQTT was attempted but failed gracefully + mock_mqtt_get_ip.assert_called_once_with(config, "user", "pass", "client") + # Verify we fell back to the IP address + expected_firmware = ( + tmp_path / ".esphome" / "build" / "test" / ".pioenvs" / "test" / "firmware.bin" + ) + mock_run_ota.assert_called_once_with( + ["192.168.1.50"], 3232, None, expected_firmware + ) + # Verify warning was logged + assert "MQTT IP discovery failed" in caplog.text + + @patch("esphome.__main__.importlib.import_module") def test_upload_program_platform_specific_handler( mock_import: Mock, diff --git a/tests/unit_tests/test_mqtt.py b/tests/unit_tests/test_mqtt.py new file mode 100644 index 0000000000..4c2c34dff1 --- /dev/null +++ b/tests/unit_tests/test_mqtt.py @@ -0,0 +1,91 @@ +"""Unit tests for esphome.mqtt module.""" + +from __future__ import annotations + +import pytest + +from esphome.const import CONF_BROKER, CONF_ESPHOME, CONF_MQTT, CONF_NAME +from esphome.core import EsphomeError +from esphome.mqtt import get_esphome_device_ip + + +def test_get_esphome_device_ip_empty_broker() -> None: + """Test that get_esphome_device_ip raises EsphomeError when broker is empty.""" + config = { + CONF_MQTT: { + CONF_BROKER: "", + }, + CONF_ESPHOME: { + CONF_NAME: "test-device", + }, + } + + with pytest.raises( + EsphomeError, + match="Cannot discover IP via MQTT as the broker is not configured", + ): + get_esphome_device_ip(config) + + +def test_get_esphome_device_ip_none_broker() -> None: + """Test that get_esphome_device_ip raises EsphomeError when broker is None.""" + config = { + CONF_MQTT: { + CONF_BROKER: None, + }, + CONF_ESPHOME: { + CONF_NAME: "test-device", + }, + } + + with pytest.raises( + EsphomeError, + match="Cannot discover IP via MQTT as the broker is not configured", + ): + get_esphome_device_ip(config) + + +def test_get_esphome_device_ip_missing_mqtt() -> None: + """Test that get_esphome_device_ip raises EsphomeError when mqtt config is missing.""" + config = { + CONF_ESPHOME: { + CONF_NAME: "test-device", + }, + } + + with pytest.raises( + EsphomeError, + match="Cannot discover IP via MQTT as the config does not include the mqtt:", + ): + get_esphome_device_ip(config) + + +def test_get_esphome_device_ip_missing_esphome() -> None: + """Test that get_esphome_device_ip raises EsphomeError when esphome config is missing.""" + config = { + CONF_MQTT: { + CONF_BROKER: "mqtt.local", + }, + } + + with pytest.raises( + EsphomeError, + match="Cannot discover IP via MQTT as the config does not include the device name:", + ): + get_esphome_device_ip(config) + + +def test_get_esphome_device_ip_missing_name() -> None: + """Test that get_esphome_device_ip raises EsphomeError when device name is missing.""" + config = { + CONF_MQTT: { + CONF_BROKER: "mqtt.local", + }, + CONF_ESPHOME: {}, + } + + with pytest.raises( + EsphomeError, + match="Cannot discover IP via MQTT as the config does not include the device name:", + ): + get_esphome_device_ip(config) From afed58107967d519c8a62779fbce809cdbe79af0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Nov 2025 18:41:28 -0600 Subject: [PATCH 493/526] [light] Fix dangling reference in compute_color_mode causing memory corruption (#11868) --- esphome/components/api/api_connection.cpp | 3 ++- esphome/components/light/light_call.cpp | 2 +- esphome/components/light/light_traits.h | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index b4230576de..4acd2fc15c 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -476,8 +476,9 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c auto *light = static_cast(entity); ListEntitiesLightResponse msg; auto traits = light->get_traits(); + auto supported_modes = traits.get_supported_color_modes(); // Pass pointer to ColorModeMask so the iterator can encode actual ColorMode enum values - msg.supported_color_modes = &traits.get_supported_color_modes(); + msg.supported_color_modes = &supported_modes; if (traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) || traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE)) { msg.min_mireds = traits.get_min_mireds(); diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index df17f53adc..8365ac77cd 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -406,7 +406,7 @@ void LightCall::transform_parameters_() { } } ColorMode LightCall::compute_color_mode_() { - const auto &supported_modes = this->parent_->get_traits().get_supported_color_modes(); + auto supported_modes = this->parent_->get_traits().get_supported_color_modes(); int supported_count = supported_modes.size(); // Some lights don't support any color modes (e.g. monochromatic light), leave it at unknown. diff --git a/esphome/components/light/light_traits.h b/esphome/components/light/light_traits.h index 294b0cad1d..c3bb27a964 100644 --- a/esphome/components/light/light_traits.h +++ b/esphome/components/light/light_traits.h @@ -18,7 +18,8 @@ class LightTraits { public: LightTraits() = default; - const ColorModeMask &get_supported_color_modes() const { return this->supported_color_modes_; } + // Return by value to avoid dangling reference when get_traits() returns a temporary + ColorModeMask get_supported_color_modes() const { return this->supported_color_modes_; } void set_supported_color_modes(ColorModeMask supported_color_modes) { this->supported_color_modes_ = supported_color_modes; } From 1d8b08dcce066b876d055a44b7f7ad192571985f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Nov 2025 18:43:51 -0600 Subject: [PATCH 494/526] [wifi][ethernet] Fix spurious warnings and unclear status after PR #9823 (#11871) --- esphome/components/ethernet/ethernet_component.cpp | 5 ++++- esphome/components/wifi/wifi_component.cpp | 13 ++++++++++++- esphome/components/wifi/wifi_component.h | 3 +++ esphome/components/wifi/wifi_component_esp8266.cpp | 2 +- esphome/components/wifi/wifi_component_esp_idf.cpp | 11 +++++++---- .../components/wifi/wifi_component_libretiny.cpp | 2 +- esphome/components/wifi/wifi_component_pico_w.cpp | 2 +- 7 files changed, 29 insertions(+), 9 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 5888ddce60..cad963b299 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -381,7 +381,10 @@ void EthernetComponent::dump_config() { break; } - ESP_LOGCONFIG(TAG, "Ethernet:"); + ESP_LOGCONFIG(TAG, + "Ethernet:\n" + " Connected: %s", + YESNO(this->is_connected())); this->dump_connect_params_(); #ifdef USE_ETHERNET_SPI ESP_LOGCONFIG(TAG, diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 817419107f..33aa6c8139 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -743,6 +743,14 @@ void WiFiComponent::start_connecting(const WiFiAP &ap) { } const LogString *get_signal_bars(int8_t rssi) { + // Check for disconnected sentinel value first + if (rssi == WIFI_RSSI_DISCONNECTED) { + // MULTIPLICATION SIGN + // Unicode: U+00D7, UTF-8: C3 97 + return LOG_STR("\033[0;31m" // red + "\xc3\x97\xc3\x97\xc3\x97\xc3\x97" + "\033[0m"); + } // LOWER ONE QUARTER BLOCK // Unicode: U+2582, UTF-8: E2 96 82 // LOWER HALF BLOCK @@ -1022,7 +1030,10 @@ void WiFiComponent::check_scanning_finished() { } void WiFiComponent::dump_config() { - ESP_LOGCONFIG(TAG, "WiFi:"); + ESP_LOGCONFIG(TAG, + "WiFi:\n" + " Connected: %s", + YESNO(this->is_connected())); this->print_connect_params_(); } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 713e6f223f..5023cf3428 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -52,6 +52,9 @@ extern "C" { namespace esphome { namespace wifi { +/// Sentinel value for RSSI when WiFi is not connected +static constexpr int8_t WIFI_RSSI_DISCONNECTED = -127; + struct SavedWifiSettings { char ssid[33]; char password[65]; diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index bcb5dc4cf7..bdaae5382a 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -870,7 +870,7 @@ bssid_t WiFiComponent::wifi_bssid() { return bssid; } std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } -int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } +int8_t WiFiComponent::wifi_rssi() { return WiFi.status() == WL_CONNECTED ? WiFi.RSSI() : WIFI_RSSI_DISCONNECTED; } int32_t WiFiComponent::get_wifi_channel() { return WiFi.channel(); } network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {(const ip_addr_t *) WiFi.subnetMask()}; } network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {(const ip_addr_t *) WiFi.gatewayIP()}; } diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index fd7e85fb6b..8c27fe92db 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -1029,7 +1029,8 @@ bssid_t WiFiComponent::wifi_bssid() { wifi_ap_record_t info; esp_err_t err = esp_wifi_sta_get_ap_info(&info); if (err != ESP_OK) { - ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); + // Very verbose only: this is expected during dump_config() before connection is established (PR #9823) + ESP_LOGVV(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); return bssid; } std::copy(info.bssid, info.bssid + 6, bssid.begin()); @@ -1039,7 +1040,8 @@ std::string WiFiComponent::wifi_ssid() { wifi_ap_record_t info{}; esp_err_t err = esp_wifi_sta_get_ap_info(&info); if (err != ESP_OK) { - ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); + // Very verbose only: this is expected during dump_config() before connection is established (PR #9823) + ESP_LOGVV(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); return ""; } auto *ssid_s = reinterpret_cast(info.ssid); @@ -1050,8 +1052,9 @@ int8_t WiFiComponent::wifi_rssi() { wifi_ap_record_t info; esp_err_t err = esp_wifi_sta_get_ap_info(&info); if (err != ESP_OK) { - ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); - return 0; + // Very verbose only: this is expected during dump_config() before connection is established (PR #9823) + ESP_LOGVV(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); + return WIFI_RSSI_DISCONNECTED; } return info.rssi; } diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index 2946b9e831..8c6c28ac75 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -484,7 +484,7 @@ bssid_t WiFiComponent::wifi_bssid() { return bssid; } std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } -int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } +int8_t WiFiComponent::wifi_rssi() { return WiFi.status() == WL_CONNECTED ? WiFi.RSSI() : WIFI_RSSI_DISCONNECTED; } int32_t WiFiComponent::get_wifi_channel() { return WiFi.channel(); } network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; } network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } diff --git a/esphome/components/wifi/wifi_component_pico_w.cpp b/esphome/components/wifi/wifi_component_pico_w.cpp index 7025ba16bd..073b752886 100644 --- a/esphome/components/wifi/wifi_component_pico_w.cpp +++ b/esphome/components/wifi/wifi_component_pico_w.cpp @@ -200,7 +200,7 @@ bssid_t WiFiComponent::wifi_bssid() { return bssid; } std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } -int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } +int8_t WiFiComponent::wifi_rssi() { return WiFi.status() == WL_CONNECTED ? WiFi.RSSI() : WIFI_RSSI_DISCONNECTED; } int32_t WiFiComponent::get_wifi_channel() { return WiFi.channel(); } network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { From 1675408161b8e1631b3b1b53909580cfd7a1fb99 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Nov 2025 18:44:22 -0600 Subject: [PATCH 495/526] [wifi] Fix slow reconnection after connection loss for all network types (#11873) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/wifi/wifi_component.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 33aa6c8139..880928c3e3 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -465,6 +465,8 @@ void WiFiComponent::loop() { if (!this->is_connected()) { ESP_LOGW(TAG, "Connection lost; reconnecting"); this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING; + // Clear error flag before reconnecting so first attempt is not seen as immediate failure + this->error_from_callback_ = false; this->retry_connect(); } else { this->status_clear_warning(); @@ -1058,6 +1060,10 @@ void WiFiComponent::check_connecting_finished() { // Reset to initial phase on successful connection (don't log transition, just reset state) this->retry_phase_ = WiFiRetryPhase::INITIAL_CONNECT; this->num_retried_ = 0; + // Ensure next connection attempt does not inherit error state + // so when WiFi disconnects later we start fresh and don't see + // the first connection as a failure. + this->error_from_callback_ = false; this->print_connect_params_(); @@ -1144,6 +1150,11 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() { return WiFiRetryPhase::FAST_CONNECT_CYCLING_APS; // Move to next AP } #endif + // Check if we should try explicit hidden networks before scanning + // This handles reconnection after connection loss where first network is hidden + if (!this->sta_.empty() && this->sta_[0].get_hidden()) { + return WiFiRetryPhase::EXPLICIT_HIDDEN; + } // No more APs to try, fall back to scan return WiFiRetryPhase::SCAN_CONNECTING; From 382483b0635353b70cd1975af6eaaa4118f2091e Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:56:11 -0500 Subject: [PATCH 496/526] Bump version to 2025.11.0b2 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index 8025c71c19..8766b8f00c 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.11.0b1 +PROJECT_NUMBER = 2025.11.0b2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index 00975753c2..8c6020a868 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.11.0b1" +__version__ = "2025.11.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From aed80732f965ec9dc93fb1bde0dc5d9e603f866e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 14 Nov 2025 00:55:48 +1000 Subject: [PATCH 497/526] [esp32] Make esp-idf default framework for P4 (#11884) --- esphome/components/esp32/__init__.py | 163 ++++++++++++--------------- 1 file changed, 74 insertions(+), 89 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 61511cba0c..9741dc76a1 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -381,8 +381,9 @@ PLATFORM_VERSION_LOOKUP = { } -def _check_versions(value): - value = value.copy() +def _check_versions(config): + config = config.copy() + value = config[CONF_FRAMEWORK] if value[CONF_VERSION] in PLATFORM_VERSION_LOOKUP: if CONF_SOURCE in value or CONF_PLATFORM_VERSION in value: @@ -447,7 +448,7 @@ def _check_versions(value): "If there are connectivity or build issues please remove the manual version." ) - return value + return config def _parse_platform_version(value): @@ -598,89 +599,72 @@ def _validate_idf_component(config: ConfigType) -> ConfigType: FRAMEWORK_ESP_IDF = "esp-idf" FRAMEWORK_ARDUINO = "arduino" -FRAMEWORK_SCHEMA = cv.All( - cv.Schema( - { - cv.Optional(CONF_TYPE, default=FRAMEWORK_ARDUINO): cv.one_of( - FRAMEWORK_ESP_IDF, FRAMEWORK_ARDUINO - ), - cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, - cv.Optional(CONF_RELEASE): cv.string_strict, - cv.Optional(CONF_SOURCE): cv.string_strict, - cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version, - cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): { - cv.string_strict: cv.string_strict - }, - cv.Optional(CONF_LOG_LEVEL, default="ERROR"): cv.one_of( - *LOG_LEVELS_IDF, upper=True - ), - cv.Optional(CONF_ADVANCED, default={}): cv.Schema( - { - cv.Optional(CONF_ASSERTION_LEVEL): cv.one_of( - *ASSERTION_LEVELS, upper=True - ), - cv.Optional(CONF_COMPILER_OPTIMIZATION, default="SIZE"): cv.one_of( - *COMPILER_OPTIMIZATIONS, upper=True - ), - cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean, - cv.Optional(CONF_ENABLE_LWIP_ASSERT, default=True): cv.boolean, - cv.Optional( - CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False - ): cv.boolean, - cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean, - # DHCP server is needed for WiFi AP mode. When WiFi component is used, - # it will handle disabling DHCP server when AP is not configured. - # Default to false (disabled) when WiFi is not used. - cv.OnlyWithout( - CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False - ): cv.boolean, - cv.Optional( - CONF_ENABLE_LWIP_MDNS_QUERIES, default=True - ): cv.boolean, - cv.Optional( - CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False - ): cv.boolean, - cv.Optional( - CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING, default=True - ): cv.boolean, - cv.Optional( - CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY, default=True - ): cv.boolean, - cv.Optional( - CONF_DISABLE_LIBC_LOCKS_IN_IRAM, default=True - ): cv.boolean, - cv.Optional( - CONF_DISABLE_VFS_SUPPORT_TERMIOS, default=True - ): cv.boolean, - cv.Optional( - CONF_DISABLE_VFS_SUPPORT_SELECT, default=True - ): cv.boolean, - cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean, - cv.Optional(CONF_EXECUTE_FROM_PSRAM): cv.boolean, - cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range( - min=8192, max=32768 - ), - } - ), - cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list( - cv.All( - cv.Schema( - { - cv.Required(CONF_NAME): cv.string_strict, - cv.Optional(CONF_SOURCE): cv.git_ref, - cv.Optional(CONF_REF): cv.string, - cv.Optional(CONF_PATH): cv.string, - cv.Optional(CONF_REFRESH): cv.All( - cv.string, cv.source_refresh - ), - } - ), - _validate_idf_component, - ) - ), - } - ), - _check_versions, +FRAMEWORK_SCHEMA = cv.Schema( + { + cv.Optional(CONF_TYPE): cv.one_of(FRAMEWORK_ESP_IDF, FRAMEWORK_ARDUINO), + cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, + cv.Optional(CONF_RELEASE): cv.string_strict, + cv.Optional(CONF_SOURCE): cv.string_strict, + cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version, + cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): { + cv.string_strict: cv.string_strict + }, + cv.Optional(CONF_LOG_LEVEL, default="ERROR"): cv.one_of( + *LOG_LEVELS_IDF, upper=True + ), + cv.Optional(CONF_ADVANCED, default={}): cv.Schema( + { + cv.Optional(CONF_ASSERTION_LEVEL): cv.one_of( + *ASSERTION_LEVELS, upper=True + ), + cv.Optional(CONF_COMPILER_OPTIMIZATION, default="SIZE"): cv.one_of( + *COMPILER_OPTIMIZATIONS, upper=True + ), + cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean, + cv.Optional(CONF_ENABLE_LWIP_ASSERT, default=True): cv.boolean, + cv.Optional(CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False): cv.boolean, + cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean, + # DHCP server is needed for WiFi AP mode. When WiFi component is used, + # it will handle disabling DHCP server when AP is not configured. + # Default to false (disabled) when WiFi is not used. + cv.OnlyWithout( + CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False + ): cv.boolean, + cv.Optional(CONF_ENABLE_LWIP_MDNS_QUERIES, default=True): cv.boolean, + cv.Optional( + CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False + ): cv.boolean, + cv.Optional( + CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING, default=True + ): cv.boolean, + cv.Optional( + CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY, default=True + ): cv.boolean, + cv.Optional(CONF_DISABLE_LIBC_LOCKS_IN_IRAM, default=True): cv.boolean, + cv.Optional(CONF_DISABLE_VFS_SUPPORT_TERMIOS, default=True): cv.boolean, + cv.Optional(CONF_DISABLE_VFS_SUPPORT_SELECT, default=True): cv.boolean, + cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean, + cv.Optional(CONF_EXECUTE_FROM_PSRAM): cv.boolean, + cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range( + min=8192, max=32768 + ), + } + ), + cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list( + cv.All( + cv.Schema( + { + cv.Required(CONF_NAME): cv.string_strict, + cv.Optional(CONF_SOURCE): cv.git_ref, + cv.Optional(CONF_REF): cv.string, + cv.Optional(CONF_PATH): cv.string, + cv.Optional(CONF_REFRESH): cv.All(cv.string, cv.source_refresh), + } + ), + _validate_idf_component, + ) + ), + } ) @@ -743,11 +727,11 @@ def _show_framework_migration_message(name: str, variant: str) -> None: def _set_default_framework(config): + config = config.copy() if CONF_FRAMEWORK not in config: - config = config.copy() - - variant = config[CONF_VARIANT] config[CONF_FRAMEWORK] = FRAMEWORK_SCHEMA({}) + if CONF_TYPE not in config[CONF_FRAMEWORK]: + variant = config[CONF_VARIANT] if variant in ARDUINO_ALLOWED_VARIANTS: config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO _show_framework_migration_message( @@ -787,6 +771,7 @@ CONFIG_SCHEMA = cv.All( ), _detect_variant, _set_default_framework, + _check_versions, set_core_data, cv.has_at_least_one_key(CONF_BOARD, CONF_VARIANT), ) From fe00e209fff4f9c2e3e3b4c4664a300503488e94 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 14 Nov 2025 01:52:08 +1000 Subject: [PATCH 498/526] [esp32] Add sdkconfig flag to make OTA work for 32MB flash (#11883) Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/esp32/__init__.py | 81 +++++++++---------- tests/components/esp32/test.esp32-p4-idf.yaml | 27 +++++++ 2 files changed, 67 insertions(+), 41 deletions(-) create mode 100644 tests/components/esp32/test.esp32-p4-idf.yaml diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 9741dc76a1..0f85e585f7 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -498,6 +498,8 @@ def final_validate(config): from esphome.components.psram import DOMAIN as PSRAM_DOMAIN errs = [] + conf_fw = config[CONF_FRAMEWORK] + advanced = conf_fw[CONF_ADVANCED] full_config = fv.full_config.get() if pio_options := full_config[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS): pio_flash_size_key = "board_upload.flash_size" @@ -514,22 +516,14 @@ def final_validate(config): f"Please specify {CONF_FLASH_SIZE} within esp32 configuration only" ) ) - if ( - config[CONF_VARIANT] != VARIANT_ESP32 - and CONF_ADVANCED in (conf_fw := config[CONF_FRAMEWORK]) - and CONF_IGNORE_EFUSE_MAC_CRC in conf_fw[CONF_ADVANCED] - ): + if config[CONF_VARIANT] != VARIANT_ESP32 and advanced[CONF_IGNORE_EFUSE_MAC_CRC]: errs.append( cv.Invalid( f"'{CONF_IGNORE_EFUSE_MAC_CRC}' is not supported on {config[CONF_VARIANT]}", path=[CONF_FRAMEWORK, CONF_ADVANCED, CONF_IGNORE_EFUSE_MAC_CRC], ) ) - if ( - config.get(CONF_FRAMEWORK, {}) - .get(CONF_ADVANCED, {}) - .get(CONF_EXECUTE_FROM_PSRAM) - ): + if advanced[CONF_EXECUTE_FROM_PSRAM]: if config[CONF_VARIANT] != VARIANT_ESP32S3: errs.append( cv.Invalid( @@ -545,6 +539,17 @@ def final_validate(config): ) ) + if ( + config[CONF_FLASH_SIZE] == "32MB" + and "ota" in full_config + and not advanced[CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES] + ): + errs.append( + cv.Invalid( + f"OTA with 32MB flash requires '{CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES}' to be set in the '{CONF_ADVANCED}' section of the esp32 configuration", + path=[CONF_FLASH_SIZE], + ) + ) if errs: raise cv.MultipleInvalid(errs) @@ -620,10 +625,12 @@ FRAMEWORK_SCHEMA = cv.Schema( cv.Optional(CONF_COMPILER_OPTIMIZATION, default="SIZE"): cv.one_of( *COMPILER_OPTIMIZATIONS, upper=True ), - cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean, + cv.Optional( + CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES, default=False + ): cv.boolean, cv.Optional(CONF_ENABLE_LWIP_ASSERT, default=True): cv.boolean, cv.Optional(CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False): cv.boolean, - cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean, + cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC, default=False): cv.boolean, # DHCP server is needed for WiFi AP mode. When WiFi component is used, # it will handle disabling DHCP server when AP is not configured. # Default to false (disabled) when WiFi is not used. @@ -644,7 +651,7 @@ FRAMEWORK_SCHEMA = cv.Schema( cv.Optional(CONF_DISABLE_VFS_SUPPORT_TERMIOS, default=True): cv.boolean, cv.Optional(CONF_DISABLE_VFS_SUPPORT_SELECT, default=True): cv.boolean, cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean, - cv.Optional(CONF_EXECUTE_FROM_PSRAM): cv.boolean, + cv.Optional(CONF_EXECUTE_FROM_PSRAM, default=False): cv.boolean, cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range( min=8192, max=32768 ), @@ -790,9 +797,7 @@ def _configure_lwip_max_sockets(conf: dict) -> None: from esphome.components.socket import KEY_SOCKET_CONSUMERS # Check if user manually specified CONFIG_LWIP_MAX_SOCKETS - user_max_sockets = conf.get(CONF_SDKCONFIG_OPTIONS, {}).get( - "CONFIG_LWIP_MAX_SOCKETS" - ) + user_max_sockets = conf[CONF_SDKCONFIG_OPTIONS].get("CONFIG_LWIP_MAX_SOCKETS") socket_consumers: dict[str, int] = CORE.data.get(KEY_SOCKET_CONSUMERS, {}) total_sockets = sum(socket_consumers.values()) @@ -962,23 +967,18 @@ async def to_code(config): # WiFi component handles its own optimization when AP mode is not used # When using Arduino with Ethernet, DHCP server functions must be available # for the Network library to compile, even if not actively used - if ( - CONF_ENABLE_LWIP_DHCP_SERVER in advanced - and not advanced[CONF_ENABLE_LWIP_DHCP_SERVER] - and not ( - conf[CONF_TYPE] == FRAMEWORK_ARDUINO - and "ethernet" in CORE.loaded_integrations - ) + if advanced.get(CONF_ENABLE_LWIP_DHCP_SERVER) is False and not ( + conf[CONF_TYPE] == FRAMEWORK_ARDUINO and "ethernet" in CORE.loaded_integrations ): add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False) - if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, True): + if not advanced[CONF_ENABLE_LWIP_MDNS_QUERIES]: add_idf_sdkconfig_option("CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES", False) - if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False): + if not advanced[CONF_ENABLE_LWIP_BRIDGE_INTERFACE]: add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0) _configure_lwip_max_sockets(conf) - if advanced.get(CONF_EXECUTE_FROM_PSRAM, False): + if advanced[CONF_EXECUTE_FROM_PSRAM]: add_idf_sdkconfig_option("CONFIG_SPIRAM_FETCH_INSTRUCTIONS", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_RODATA", True) @@ -989,23 +989,22 @@ async def to_code(config): # - select() on 4 sockets: ~190μs (Arduino/core locking) vs ~235μs (ESP-IDF default) # - Up to 200% slower under load when all operations queue through tcpip_thread # Enabling this makes ESP-IDF socket performance match Arduino framework. - if advanced.get(CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING, True): + if advanced[CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING]: add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_CORE_LOCKING", True) - if advanced.get(CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY, True): + if advanced[CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY]: add_idf_sdkconfig_option("CONFIG_LWIP_CHECK_THREAD_SAFETY", True) # Disable placing libc locks in IRAM to save RAM # This is safe for ESPHome since no IRAM ISRs (interrupts that run while cache is disabled) # use libc lock APIs. Saves approximately 1.3KB (1,356 bytes) of IRAM. - if advanced.get(CONF_DISABLE_LIBC_LOCKS_IN_IRAM, True): + if advanced[CONF_DISABLE_LIBC_LOCKS_IN_IRAM]: add_idf_sdkconfig_option("CONFIG_LIBC_LOCKS_PLACE_IN_IRAM", False) # Disable VFS support for termios (terminal I/O functions) # ESPHome doesn't use termios functions on ESP32 (only used in host UART driver). # Saves approximately 1.8KB of flash when disabled (default). add_idf_sdkconfig_option( - "CONFIG_VFS_SUPPORT_TERMIOS", - not advanced.get(CONF_DISABLE_VFS_SUPPORT_TERMIOS, True), + "CONFIG_VFS_SUPPORT_TERMIOS", not advanced[CONF_DISABLE_VFS_SUPPORT_TERMIOS] ) # Disable VFS support for select() with file descriptors @@ -1019,8 +1018,7 @@ async def to_code(config): else: # No component needs it - allow user to control (default: disabled) add_idf_sdkconfig_option( - "CONFIG_VFS_SUPPORT_SELECT", - not advanced.get(CONF_DISABLE_VFS_SUPPORT_SELECT, True), + "CONFIG_VFS_SUPPORT_SELECT", not advanced[CONF_DISABLE_VFS_SUPPORT_SELECT] ) # Disable VFS support for directory functions (opendir, readdir, mkdir, etc.) @@ -1033,8 +1031,7 @@ async def to_code(config): else: # No component needs it - allow user to control (default: disabled) add_idf_sdkconfig_option( - "CONFIG_VFS_SUPPORT_DIR", - not advanced.get(CONF_DISABLE_VFS_SUPPORT_DIR, True), + "CONFIG_VFS_SUPPORT_DIR", not advanced[CONF_DISABLE_VFS_SUPPORT_DIR] ) cg.add_platformio_option("board_build.partitions", "partitions.csv") @@ -1048,7 +1045,7 @@ async def to_code(config): add_idf_sdkconfig_option(flag, assertion_level == key) add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False) - compiler_optimization = advanced.get(CONF_COMPILER_OPTIMIZATION) + compiler_optimization = advanced[CONF_COMPILER_OPTIMIZATION] for key, flag in COMPILER_OPTIMIZATIONS.items(): add_idf_sdkconfig_option(flag, compiler_optimization == key) @@ -1057,18 +1054,20 @@ async def to_code(config): conf[CONF_ADVANCED][CONF_ENABLE_LWIP_ASSERT], ) - if advanced.get(CONF_IGNORE_EFUSE_MAC_CRC): + if advanced[CONF_IGNORE_EFUSE_MAC_CRC]: add_idf_sdkconfig_option("CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR", True) add_idf_sdkconfig_option("CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE", False) - if advanced.get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): + if advanced[CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES]: _LOGGER.warning( "Using experimental features in ESP-IDF may result in unexpected failures." ) add_idf_sdkconfig_option("CONFIG_IDF_EXPERIMENTAL_FEATURES", True) + if config[CONF_FLASH_SIZE] == "32MB": + add_idf_sdkconfig_option( + "CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH", True + ) - cg.add_define( - "ESPHOME_LOOP_TASK_STACK_SIZE", advanced.get(CONF_LOOP_TASK_STACK_SIZE) - ) + cg.add_define("ESPHOME_LOOP_TASK_STACK_SIZE", advanced[CONF_LOOP_TASK_STACK_SIZE]) cg.add_define( "USE_ESP_IDF_VERSION_CODE", diff --git a/tests/components/esp32/test.esp32-p4-idf.yaml b/tests/components/esp32/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..a4c930f236 --- /dev/null +++ b/tests/components/esp32/test.esp32-p4-idf.yaml @@ -0,0 +1,27 @@ +esp32: + variant: esp32p4 + flash_size: 32MB + cpu_frequency: 400MHz + framework: + type: esp-idf + advanced: + enable_idf_experimental_features: yes + +ota: + platform: esphome + +wifi: + ssid: MySSID + password: password1 + +esp32_hosted: + variant: ESP32C6 + slot: 1 + active_high: true + reset_pin: GPIO15 + cmd_pin: GPIO13 + clk_pin: GPIO12 + d0_pin: GPIO11 + d1_pin: GPIO10 + d2_pin: GPIO9 + d3_pin: GPIO8 From eb54c0026dfcab03fbb841ce5fd374554fa61960 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 13 Nov 2025 19:08:06 +0100 Subject: [PATCH 499/526] [light] Fix missing `ColorMode::BRIGHTNESS` case in logging (#11836) --- esphome/components/light/light_call.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index 8365ac77cd..b15ff84b97 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -52,8 +52,10 @@ static void log_invalid_parameter(const char *name, const LogString *message) { } static const LogString *color_mode_to_human(ColorMode color_mode) { - if (color_mode == ColorMode::UNKNOWN) - return LOG_STR("Unknown"); + if (color_mode == ColorMode::ON_OFF) + return LOG_STR("On/Off"); + if (color_mode == ColorMode::BRIGHTNESS) + return LOG_STR("Brightness"); if (color_mode == ColorMode::WHITE) return LOG_STR("White"); if (color_mode == ColorMode::COLOR_TEMPERATURE) @@ -68,7 +70,7 @@ static const LogString *color_mode_to_human(ColorMode color_mode) { return LOG_STR("RGB + cold/warm white"); if (color_mode == ColorMode::RGB_COLOR_TEMPERATURE) return LOG_STR("RGB + color temperature"); - return LOG_STR(""); + return LOG_STR("Unknown"); } // Helper to log percentage values From 7ce94c27feeade6a50082ed9f14165c056fb2a71 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:13:48 +0100 Subject: [PATCH 500/526] [wifi] Allow `use_psram` with Arduino (#11902) --- esphome/components/wifi/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 4dbb425e4b..11bd7798e2 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -12,7 +12,6 @@ from esphome.components.network import ( from esphome.components.psram import is_guaranteed as psram_is_guaranteed from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv -from esphome.config_validation import only_with_esp_idf from esphome.const import ( CONF_AP, CONF_BSSID, @@ -352,7 +351,7 @@ CONFIG_SCHEMA = cv.All( single=True ), cv.Optional(CONF_USE_PSRAM): cv.All( - only_with_esp_idf, cv.requires_component("psram"), cv.boolean + cv.only_on_esp32, cv.requires_component("psram"), cv.boolean ), } ), From 97c4914573b5b7444f44c526e1fb11d13cdcfe38 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:06:08 -0500 Subject: [PATCH 501/526] [uart] Improve error handling and validate buffer size (#11895) Co-authored-by: J. Nick Koston --- esphome/components/uart/__init__.py | 19 ++++++++++ .../uart/uart_component_esp_idf.cpp | 35 +++++++++++++++---- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index cbc11d0db0..7b0d9726b8 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -1,3 +1,4 @@ +from logging import getLogger import math import re @@ -35,6 +36,8 @@ from esphome.core import CORE, ID import esphome.final_validate as fv from esphome.yaml_util import make_data_base +_LOGGER = getLogger(__name__) + CODEOWNERS = ["@esphome/core"] uart_ns = cg.esphome_ns.namespace("uart") UARTComponent = uart_ns.class_("UARTComponent") @@ -130,6 +133,21 @@ def validate_host_config(config): return config +def validate_rx_buffer_size(config): + if CORE.is_esp32: + # ESP32 UART hardware FIFO is 128 bytes (LP UART is 16 bytes, but we use 128 as safe minimum) + # rx_buffer_size must be greater than the hardware FIFO length + min_buffer_size = 128 + if config[CONF_RX_BUFFER_SIZE] <= min_buffer_size: + _LOGGER.warning( + "UART rx_buffer_size (%d bytes) is too small and must be greater than the hardware " + "FIFO size (%d bytes). The buffer size will be automatically adjusted at runtime.", + config[CONF_RX_BUFFER_SIZE], + min_buffer_size, + ) + return config + + def _uart_declare_type(value): if CORE.is_esp8266: return cv.declare_id(ESP8266UartComponent)(value) @@ -247,6 +265,7 @@ CONFIG_SCHEMA = cv.All( ).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN, CONF_PORT), validate_host_config, + validate_rx_buffer_size, ) diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 73813d2d5b..70a13c9e37 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -91,6 +91,16 @@ void IDFUARTComponent::setup() { this->uart_num_ = static_cast(next_uart_num++); this->lock_ = xSemaphoreCreateMutex(); +#if (SOC_UART_LP_NUM >= 1) + size_t fifo_len = ((this->uart_num_ < SOC_UART_HP_NUM) ? SOC_UART_FIFO_LEN : SOC_LP_UART_FIFO_LEN); +#else + size_t fifo_len = SOC_UART_FIFO_LEN; +#endif + if (this->rx_buffer_size_ <= fifo_len) { + ESP_LOGW(TAG, "rx_buffer_size is too small, must be greater than %zu", fifo_len); + this->rx_buffer_size_ = fifo_len * 2; + } + xSemaphoreTake(this->lock_, portMAX_DELAY); this->load_settings(false); @@ -237,8 +247,12 @@ void IDFUARTComponent::set_rx_timeout(size_t rx_timeout) { void IDFUARTComponent::write_array(const uint8_t *data, size_t len) { xSemaphoreTake(this->lock_, portMAX_DELAY); - uart_write_bytes(this->uart_num_, data, len); + int32_t write_len = uart_write_bytes(this->uart_num_, data, len); xSemaphoreGive(this->lock_); + if (write_len != (int32_t) len) { + ESP_LOGW(TAG, "uart_write_bytes failed: %d != %zu", write_len, len); + this->mark_failed(); + } #ifdef USE_UART_DEBUGGER for (size_t i = 0; i < len; i++) { this->debug_callback_.call(UART_DIRECTION_TX, data[i]); @@ -267,6 +281,7 @@ bool IDFUARTComponent::peek_byte(uint8_t *data) { bool IDFUARTComponent::read_array(uint8_t *data, size_t len) { size_t length_to_read = len; + int32_t read_len = 0; if (!this->check_read_timeout_(len)) return false; xSemaphoreTake(this->lock_, portMAX_DELAY); @@ -277,25 +292,31 @@ bool IDFUARTComponent::read_array(uint8_t *data, size_t len) { this->has_peek_ = false; } if (length_to_read > 0) - uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_PERIOD_MS); + read_len = uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_PERIOD_MS); xSemaphoreGive(this->lock_); #ifdef USE_UART_DEBUGGER for (size_t i = 0; i < len; i++) { this->debug_callback_.call(UART_DIRECTION_RX, data[i]); } #endif - return true; + return read_len == (int32_t) length_to_read; } int IDFUARTComponent::available() { - size_t available; + size_t available = 0; + esp_err_t err; xSemaphoreTake(this->lock_, portMAX_DELAY); - uart_get_buffered_data_len(this->uart_num_, &available); - if (this->has_peek_) - available++; + err = uart_get_buffered_data_len(this->uart_num_, &available); xSemaphoreGive(this->lock_); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_get_buffered_data_len failed: %s", esp_err_to_name(err)); + this->mark_failed(); + } + if (this->has_peek_) { + available++; + } return available; } From 6440b5fbf5d05e7b572d9272c8ea8865b59ed513 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Nov 2025 22:03:43 -0600 Subject: [PATCH 502/526] [ld2412] Fix stuck targets by adding timeout filter (#11919) --- esphome/components/ld2412/sensor.py | 76 ++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/esphome/components/ld2412/sensor.py b/esphome/components/ld2412/sensor.py index abb823faad..0bfbd9bf1d 100644 --- a/esphome/components/ld2412/sensor.py +++ b/esphome/components/ld2412/sensor.py @@ -31,36 +31,84 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component), cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema( device_class=DEVICE_CLASS_DISTANCE, - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_SIGNAL, unit_of_measurement=UNIT_CENTIMETER, ), cv.Optional(CONF_LIGHT): sensor.sensor_schema( device_class=DEVICE_CLASS_ILLUMINANCE, entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_LIGHTBULB, unit_of_measurement=UNIT_EMPTY, # No standard unit for this light sensor ), cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema( device_class=DEVICE_CLASS_DISTANCE, - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_SIGNAL, unit_of_measurement=UNIT_CENTIMETER, ), cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema( - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_MOTION_SENSOR, unit_of_measurement=UNIT_PERCENT, ), cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema( device_class=DEVICE_CLASS_DISTANCE, - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_SIGNAL, unit_of_measurement=UNIT_CENTIMETER, ), cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_FLASH, unit_of_measurement=UNIT_PERCENT, ), @@ -74,7 +122,13 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend( cv.Optional(CONF_MOVE_ENERGY): sensor.sensor_schema( entity_category=ENTITY_CATEGORY_DIAGNOSTIC, filters=[ - {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)} + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, ], icon=ICON_MOTION_SENSOR, unit_of_measurement=UNIT_PERCENT, @@ -82,7 +136,13 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend( cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( entity_category=ENTITY_CATEGORY_DIAGNOSTIC, filters=[ - {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)} + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, ], icon=ICON_FLASH, unit_of_measurement=UNIT_PERCENT, From d559f9f52e26cc779528fdb5c015cb73560536f1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Nov 2025 22:04:25 -0600 Subject: [PATCH 503/526] [ld2410] Add timeout filter to prevent stuck targets (#11920) --- esphome/components/ld2410/sensor.py | 76 ++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/esphome/components/ld2410/sensor.py b/esphome/components/ld2410/sensor.py index fca2b2ceca..3bd34963bc 100644 --- a/esphome/components/ld2410/sensor.py +++ b/esphome/components/ld2410/sensor.py @@ -31,35 +31,83 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema( device_class=DEVICE_CLASS_DISTANCE, - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_SIGNAL, unit_of_measurement=UNIT_CENTIMETER, ), cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema( device_class=DEVICE_CLASS_DISTANCE, - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_SIGNAL, unit_of_measurement=UNIT_CENTIMETER, ), cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema( - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_MOTION_SENSOR, unit_of_measurement=UNIT_PERCENT, ), cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_FLASH, unit_of_measurement=UNIT_PERCENT, ), cv.Optional(CONF_LIGHT): sensor.sensor_schema( device_class=DEVICE_CLASS_ILLUMINANCE, entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_LIGHTBULB, ), cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema( device_class=DEVICE_CLASS_DISTANCE, - filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], + filters=[ + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, + ], icon=ICON_SIGNAL, unit_of_measurement=UNIT_CENTIMETER, ), @@ -73,7 +121,13 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend( cv.Optional(CONF_MOVE_ENERGY): sensor.sensor_schema( entity_category=ENTITY_CATEGORY_DIAGNOSTIC, filters=[ - {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)} + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, ], icon=ICON_MOTION_SENSOR, unit_of_measurement=UNIT_PERCENT, @@ -81,7 +135,13 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend( cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( entity_category=ENTITY_CATEGORY_DIAGNOSTIC, filters=[ - {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)} + { + "timeout": { + "timeout": cv.TimePeriod(milliseconds=1000), + "value": "last", + } + }, + {"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}, ], icon=ICON_FLASH, unit_of_measurement=UNIT_PERCENT, From 36868ee7b173628e838951d3fb3c899e66b74866 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Nov 2025 22:20:57 -0600 Subject: [PATCH 504/526] [scheduler] Fix timing breakage after 49 days of uptime on ESP8266/RP2040 (#11924) --- esphome/core/scheduler.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index d285af2d0e..d2e0f0dab4 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -609,13 +609,12 @@ uint64_t Scheduler::millis_64_(uint32_t now) { if (now < last && (last - now) > HALF_MAX_UINT32) { this->millis_major_++; major++; + this->last_millis_ = now; #ifdef ESPHOME_DEBUG_SCHEDULER ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last); #endif /* ESPHOME_DEBUG_SCHEDULER */ - } - - // Only update if time moved forward - if (now > last) { + } else if (now > last) { + // Only update if time moved forward this->last_millis_ = now; } From f19296ac7f59c4b2610b127cd1663e11648cf645 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 16 Nov 2025 07:35:31 -0600 Subject: [PATCH 505/526] [analyze-memory] Show all core symbols > 100 B instead of top 15 (#11909) --- esphome/analyze_memory/cli.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/esphome/analyze_memory/cli.py b/esphome/analyze_memory/cli.py index 718f42330d..44ade221f8 100644 --- a/esphome/analyze_memory/cli.py +++ b/esphome/analyze_memory/cli.py @@ -15,6 +15,11 @@ from . import ( class MemoryAnalyzerCLI(MemoryAnalyzer): """Memory analyzer with CLI-specific report generation.""" + # Symbol size threshold for detailed analysis + SYMBOL_SIZE_THRESHOLD: int = ( + 100 # Show symbols larger than this in detailed analysis + ) + # Column width constants COL_COMPONENT: int = 29 COL_FLASH_TEXT: int = 14 @@ -191,14 +196,21 @@ class MemoryAnalyzerCLI(MemoryAnalyzer): f"{len(symbols):>{self.COL_CORE_COUNT}} | {percentage:>{self.COL_CORE_PERCENT - 1}.1f}%" ) - # Top 15 largest core symbols + # All core symbols above threshold lines.append("") - lines.append(f"Top 15 Largest {_COMPONENT_CORE} Symbols:") sorted_core_symbols = sorted( self._esphome_core_symbols, key=lambda x: x[2], reverse=True ) + large_core_symbols = [ + (symbol, demangled, size) + for symbol, demangled, size in sorted_core_symbols + if size > self.SYMBOL_SIZE_THRESHOLD + ] - for i, (symbol, demangled, size) in enumerate(sorted_core_symbols[:15]): + lines.append( + f"{_COMPONENT_CORE} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B ({len(large_core_symbols)} symbols):" + ) + for i, (symbol, demangled, size) in enumerate(large_core_symbols): lines.append(f"{i + 1}. {demangled} ({size:,} B)") lines.append("=" * self.TABLE_WIDTH) @@ -268,13 +280,15 @@ class MemoryAnalyzerCLI(MemoryAnalyzer): lines.append(f"Total size: {comp_mem.flash_total:,} B") lines.append("") - # Show all symbols > 100 bytes for better visibility + # Show all symbols above threshold for better visibility large_symbols = [ - (sym, dem, size) for sym, dem, size in sorted_symbols if size > 100 + (sym, dem, size) + for sym, dem, size in sorted_symbols + if size > self.SYMBOL_SIZE_THRESHOLD ] lines.append( - f"{comp_name} Symbols > 100 B ({len(large_symbols)} symbols):" + f"{comp_name} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B ({len(large_symbols)} symbols):" ) for i, (symbol, demangled, size) in enumerate(large_symbols): lines.append(f"{i + 1}. {demangled} ({size:,} B)") From 91514894817fa318186b29dec48af397f9222ca9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 16 Nov 2025 18:38:38 -0600 Subject: [PATCH 506/526] [sntp] Merge multiple instances to fix crash and undefined behavior (#11904) --- esphome/components/sntp/time.py | 68 +++++ tests/component_tests/sntp/__init__.py | 1 + .../sntp/config/sntp_test.yaml | 22 ++ tests/component_tests/sntp/test_init.py | 238 ++++++++++++++++++ 4 files changed, 329 insertions(+) create mode 100644 tests/component_tests/sntp/__init__.py create mode 100644 tests/component_tests/sntp/config/sntp_test.yaml create mode 100644 tests/component_tests/sntp/test_init.py diff --git a/esphome/components/sntp/time.py b/esphome/components/sntp/time.py index d27fc9991d..69a2436d3d 100644 --- a/esphome/components/sntp/time.py +++ b/esphome/components/sntp/time.py @@ -1,9 +1,14 @@ +import logging + import esphome.codegen as cg from esphome.components import time as time_ +from esphome.config_helpers import merge_config import esphome.config_validation as cv from esphome.const import ( CONF_ID, + CONF_PLATFORM, CONF_SERVERS, + CONF_TIME, PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, @@ -12,13 +17,74 @@ from esphome.const import ( PLATFORM_RTL87XX, ) from esphome.core import CORE +import esphome.final_validate as fv +from esphome.types import ConfigType + +_LOGGER = logging.getLogger(__name__) DEPENDENCIES = ["network"] + +CONF_SNTP = "sntp" + sntp_ns = cg.esphome_ns.namespace("sntp") SNTPComponent = sntp_ns.class_("SNTPComponent", time_.RealTimeClock) DEFAULT_SERVERS = ["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org"] + +def _sntp_final_validate(config: ConfigType) -> None: + """Merge multiple SNTP instances into one, similar to OTA merging behavior.""" + full_conf = fv.full_config.get() + time_confs = full_conf.get(CONF_TIME, []) + + sntp_configs: list[ConfigType] = [] + other_time_configs: list[ConfigType] = [] + + for time_conf in time_confs: + if time_conf.get(CONF_PLATFORM) == CONF_SNTP: + sntp_configs.append(time_conf) + else: + other_time_configs.append(time_conf) + + if len(sntp_configs) <= 1: + return + + # Merge all SNTP configs into the first one + merged = sntp_configs[0] + for sntp_conf in sntp_configs[1:]: + # Validate that IDs are consistent if manually specified + if merged[CONF_ID].is_manual and sntp_conf[CONF_ID].is_manual: + raise cv.Invalid( + f"Found multiple SNTP configurations but {CONF_ID} is inconsistent" + ) + merged = merge_config(merged, sntp_conf) + + # Deduplicate servers while preserving order + servers = merged[CONF_SERVERS] + unique_servers = list(dict.fromkeys(servers)) + + # Warn if we're dropping servers due to 3-server limit + if len(unique_servers) > 3: + dropped = unique_servers[3:] + unique_servers = unique_servers[:3] + _LOGGER.warning( + "SNTP supports maximum 3 servers. Dropped excess server(s): %s", + dropped, + ) + + merged[CONF_SERVERS] = unique_servers + + _LOGGER.warning( + "Found and merged %d SNTP time configurations into one instance", + len(sntp_configs), + ) + + # Replace time configs with merged SNTP + other time platforms + other_time_configs.append(merged) + full_conf[CONF_TIME] = other_time_configs + fv.full_config.set(full_conf) + + CONFIG_SCHEMA = cv.All( time_.TIME_SCHEMA.extend( { @@ -40,6 +106,8 @@ CONFIG_SCHEMA = cv.All( ), ) +FINAL_VALIDATE_SCHEMA = _sntp_final_validate + async def to_code(config): servers = config[CONF_SERVERS] diff --git a/tests/component_tests/sntp/__init__.py b/tests/component_tests/sntp/__init__.py new file mode 100644 index 0000000000..7d323a4980 --- /dev/null +++ b/tests/component_tests/sntp/__init__.py @@ -0,0 +1 @@ +"""Tests for SNTP component.""" diff --git a/tests/component_tests/sntp/config/sntp_test.yaml b/tests/component_tests/sntp/config/sntp_test.yaml new file mode 100644 index 0000000000..3942c9606b --- /dev/null +++ b/tests/component_tests/sntp/config/sntp_test.yaml @@ -0,0 +1,22 @@ +esphome: + name: sntp-test + +esp32: + board: esp32dev + framework: + type: esp-idf + +wifi: + ssid: "testssid" + password: "testpassword" + +# Test multiple SNTP instances that should be merged +time: + - platform: sntp + servers: + - 192.168.1.1 + - pool.ntp.org + - platform: sntp + servers: + - pool.ntp.org + - 192.168.1.2 diff --git a/tests/component_tests/sntp/test_init.py b/tests/component_tests/sntp/test_init.py new file mode 100644 index 0000000000..9197ff55d0 --- /dev/null +++ b/tests/component_tests/sntp/test_init.py @@ -0,0 +1,238 @@ +"""Tests for SNTP time configuration validation.""" + +from __future__ import annotations + +import logging +from typing import Any + +import pytest + +from esphome import config_validation as cv +from esphome.components.sntp.time import CONF_SNTP, _sntp_final_validate +from esphome.const import CONF_ID, CONF_PLATFORM, CONF_SERVERS, CONF_TIME +from esphome.core import ID +import esphome.final_validate as fv + + +@pytest.mark.parametrize( + ("time_configs", "expected_count", "expected_servers", "warning_messages"), + [ + pytest.param( + [ + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time", is_manual=False), + CONF_SERVERS: ["192.168.1.1", "pool.ntp.org"], + } + ], + 1, + ["192.168.1.1", "pool.ntp.org"], + [], + id="single_instance_no_merge", + ), + pytest.param( + [ + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_1", is_manual=False), + CONF_SERVERS: ["192.168.1.1", "pool.ntp.org"], + }, + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_2", is_manual=False), + CONF_SERVERS: ["192.168.1.2"], + }, + ], + 1, + ["192.168.1.1", "pool.ntp.org", "192.168.1.2"], + ["Found and merged 2 SNTP time configurations into one instance"], + id="two_instances_merged", + ), + pytest.param( + [ + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_1", is_manual=False), + CONF_SERVERS: ["192.168.1.1", "pool.ntp.org"], + }, + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_2", is_manual=False), + CONF_SERVERS: ["pool.ntp.org", "192.168.1.2"], + }, + ], + 1, + ["192.168.1.1", "pool.ntp.org", "192.168.1.2"], + ["Found and merged 2 SNTP time configurations into one instance"], + id="deduplication_preserves_order", + ), + pytest.param( + [ + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_1", is_manual=False), + CONF_SERVERS: ["192.168.1.1", "pool.ntp.org"], + }, + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_2", is_manual=False), + CONF_SERVERS: ["192.168.1.2", "pool2.ntp.org"], + }, + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_3", is_manual=False), + CONF_SERVERS: ["pool3.ntp.org"], + }, + ], + 1, + ["192.168.1.1", "pool.ntp.org", "192.168.1.2"], + [ + "SNTP supports maximum 3 servers. Dropped excess server(s): ['pool2.ntp.org', 'pool3.ntp.org']", + "Found and merged 3 SNTP time configurations into one instance", + ], + id="three_instances_drops_excess_servers", + ), + pytest.param( + [ + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_1", is_manual=False), + CONF_SERVERS: [ + "192.168.1.1", + "pool.ntp.org", + "pool.ntp.org", + "192.168.1.1", + ], + }, + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_2", is_manual=False), + CONF_SERVERS: ["pool.ntp.org", "192.168.1.2"], + }, + ], + 1, + ["192.168.1.1", "pool.ntp.org", "192.168.1.2"], + ["Found and merged 2 SNTP time configurations into one instance"], + id="deduplication_multiple_duplicates", + ), + ], +) +def test_sntp_instance_merging( + time_configs: list[dict[str, Any]], + expected_count: int, + expected_servers: list[str], + warning_messages: list[str], + caplog: pytest.LogCaptureFixture, +) -> None: + """Test SNTP instance merging behavior.""" + # Create a mock full config with time configs + full_conf = {CONF_TIME: time_configs.copy()} + + # Set the context var + token = fv.full_config.set(full_conf) + try: + with caplog.at_level(logging.WARNING): + _sntp_final_validate({}) + + # Get the updated config + updated_conf = fv.full_config.get() + + # Check if merging occurred + if len(time_configs) > 1: + # Verify only one SNTP instance remains + sntp_instances = [ + tc + for tc in updated_conf[CONF_TIME] + if tc.get(CONF_PLATFORM) == CONF_SNTP + ] + assert len(sntp_instances) == expected_count + + # Verify server list + assert sntp_instances[0][CONF_SERVERS] == expected_servers + + # Verify warnings + for expected_msg in warning_messages: + assert any( + expected_msg in record.message for record in caplog.records + ), f"Expected warning message '{expected_msg}' not found in log" + else: + # Single instance should not trigger merging or warnings + assert len(caplog.records) == 0 + # Config should be unchanged + assert updated_conf[CONF_TIME] == time_configs + finally: + fv.full_config.reset(token) + + +def test_sntp_inconsistent_manual_ids() -> None: + """Test that inconsistent manual IDs raise an error.""" + # Create configs with manual IDs that are inconsistent + time_configs = [ + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_1", is_manual=True), + CONF_SERVERS: ["192.168.1.1"], + }, + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_2", is_manual=True), + CONF_SERVERS: ["192.168.1.2"], + }, + ] + + full_conf = {CONF_TIME: time_configs} + + token = fv.full_config.set(full_conf) + try: + with pytest.raises( + cv.Invalid, + match="Found multiple SNTP configurations but id is inconsistent", + ): + _sntp_final_validate({}) + finally: + fv.full_config.reset(token) + + +def test_sntp_with_other_time_platforms(caplog: pytest.LogCaptureFixture) -> None: + """Test that SNTP merging doesn't affect other time platforms.""" + time_configs = [ + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_1", is_manual=False), + CONF_SERVERS: ["192.168.1.1"], + }, + { + CONF_PLATFORM: "homeassistant", + CONF_ID: ID("homeassistant_time", is_manual=False), + }, + { + CONF_PLATFORM: CONF_SNTP, + CONF_ID: ID("sntp_time_2", is_manual=False), + CONF_SERVERS: ["192.168.1.2"], + }, + ] + + full_conf = {CONF_TIME: time_configs.copy()} + + token = fv.full_config.set(full_conf) + try: + with caplog.at_level(logging.WARNING): + _sntp_final_validate({}) + + updated_conf = fv.full_config.get() + + # Should have 2 time platforms: 1 merged SNTP + 1 homeassistant + assert len(updated_conf[CONF_TIME]) == 2 + + # Find the platforms + platforms = {tc[CONF_PLATFORM] for tc in updated_conf[CONF_TIME]} + assert platforms == {CONF_SNTP, "homeassistant"} + + # Verify SNTP was merged + sntp_instances = [ + tc for tc in updated_conf[CONF_TIME] if tc[CONF_PLATFORM] == CONF_SNTP + ] + assert len(sntp_instances) == 1 + assert sntp_instances[0][CONF_SERVERS] == ["192.168.1.1", "192.168.1.2"] + finally: + fv.full_config.reset(token) From 3fd58f1a917f3d69af101a544f2688d70ec69228 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 16 Nov 2025 18:38:52 -0600 Subject: [PATCH 507/526] [web_server.ota] Merge multiple instances to prevent undefined behavior (#11905) --- esphome/components/web_server/ota/__init__.py | 58 ++++++- .../ota/test_web_server_ota.py | 153 ++++++++++++++++++ 2 files changed, 210 insertions(+), 1 deletion(-) diff --git a/esphome/components/web_server/ota/__init__.py b/esphome/components/web_server/ota/__init__.py index 4a98db8877..260e6aea6d 100644 --- a/esphome/components/web_server/ota/__init__.py +++ b/esphome/components/web_server/ota/__init__.py @@ -1,10 +1,17 @@ +import logging + import esphome.codegen as cg from esphome.components.esp32 import add_idf_component from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code +from esphome.config_helpers import merge_config import esphome.config_validation as cv -from esphome.const import CONF_ID +from esphome.const import CONF_ID, CONF_OTA, CONF_PLATFORM, CONF_WEB_SERVER from esphome.core import CORE, coroutine_with_priority from esphome.coroutine import CoroPriority +import esphome.final_validate as fv +from esphome.types import ConfigType + +_LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network", "web_server_base"] @@ -12,6 +19,53 @@ DEPENDENCIES = ["network", "web_server_base"] web_server_ns = cg.esphome_ns.namespace("web_server") WebServerOTAComponent = web_server_ns.class_("WebServerOTAComponent", OTAComponent) + +def _web_server_ota_final_validate(config: ConfigType) -> None: + """Merge multiple web_server OTA instances into one. + + Multiple web_server OTA instances register duplicate HTTP handlers for /update, + causing undefined behavior. Merge them into a single instance. + """ + full_conf = fv.full_config.get() + ota_confs = full_conf.get(CONF_OTA, []) + + web_server_ota_configs: list[ConfigType] = [] + other_ota_configs: list[ConfigType] = [] + + for ota_conf in ota_confs: + if ota_conf.get(CONF_PLATFORM) == CONF_WEB_SERVER: + web_server_ota_configs.append(ota_conf) + else: + other_ota_configs.append(ota_conf) + + if len(web_server_ota_configs) <= 1: + return + + # Merge all web_server OTA configs into the first one + merged = web_server_ota_configs[0] + for ota_conf in web_server_ota_configs[1:]: + # Validate that IDs are consistent if manually specified + if ( + merged[CONF_ID].is_manual + and ota_conf[CONF_ID].is_manual + and merged[CONF_ID] != ota_conf[CONF_ID] + ): + raise cv.Invalid( + f"Found multiple web_server OTA configurations but {CONF_ID} is inconsistent" + ) + merged = merge_config(merged, ota_conf) + + _LOGGER.warning( + "Found and merged %d web_server OTA configurations into one instance", + len(web_server_ota_configs), + ) + + # Replace OTA configs with merged web_server + other OTA platforms + other_ota_configs.append(merged) + full_conf[CONF_OTA] = other_ota_configs + fv.full_config.set(full_conf) + + CONFIG_SCHEMA = ( cv.Schema( { @@ -22,6 +76,8 @@ CONFIG_SCHEMA = ( .extend(cv.COMPONENT_SCHEMA) ) +FINAL_VALIDATE_SCHEMA = _web_server_ota_final_validate + @coroutine_with_priority(CoroPriority.WEB_SERVER_OTA) async def to_code(config): diff --git a/tests/component_tests/ota/test_web_server_ota.py b/tests/component_tests/ota/test_web_server_ota.py index 0d8ff6f134..794eaac9be 100644 --- a/tests/component_tests/ota/test_web_server_ota.py +++ b/tests/component_tests/ota/test_web_server_ota.py @@ -1,6 +1,18 @@ """Tests for the web_server OTA platform.""" +from __future__ import annotations + from collections.abc import Callable +import logging +from typing import Any + +import pytest + +from esphome import config_validation as cv +from esphome.components.web_server.ota import _web_server_ota_final_validate +from esphome.const import CONF_ID, CONF_OTA, CONF_PLATFORM, CONF_WEB_SERVER +from esphome.core import ID +import esphome.final_validate as fv def test_web_server_ota_generated(generate_main: Callable[[str], str]) -> None: @@ -100,3 +112,144 @@ def test_web_server_ota_esp8266(generate_main: Callable[[str], str]) -> None: # Check web server OTA component is present assert "WebServerOTAComponent" in main_cpp assert "web_server::WebServerOTAComponent" in main_cpp + + +@pytest.mark.parametrize( + ("ota_configs", "expected_count", "warning_expected"), + [ + pytest.param( + [ + { + CONF_PLATFORM: CONF_WEB_SERVER, + CONF_ID: ID("ota_web", is_manual=False), + } + ], + 1, + False, + id="single_instance_no_merge", + ), + pytest.param( + [ + { + CONF_PLATFORM: CONF_WEB_SERVER, + CONF_ID: ID("ota_web_1", is_manual=False), + }, + { + CONF_PLATFORM: CONF_WEB_SERVER, + CONF_ID: ID("ota_web_2", is_manual=False), + }, + ], + 1, + True, + id="two_instances_merged", + ), + pytest.param( + [ + { + CONF_PLATFORM: CONF_WEB_SERVER, + CONF_ID: ID("ota_web_1", is_manual=False), + }, + { + CONF_PLATFORM: "esphome", + CONF_ID: ID("ota_esphome", is_manual=False), + }, + { + CONF_PLATFORM: CONF_WEB_SERVER, + CONF_ID: ID("ota_web_2", is_manual=False), + }, + ], + 2, + True, + id="mixed_platforms_web_server_merged", + ), + ], +) +def test_web_server_ota_instance_merging( + ota_configs: list[dict[str, Any]], + expected_count: int, + warning_expected: bool, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test web_server OTA instance merging behavior.""" + full_conf = {CONF_OTA: ota_configs.copy()} + + token = fv.full_config.set(full_conf) + try: + with caplog.at_level(logging.WARNING): + _web_server_ota_final_validate({}) + + updated_conf = fv.full_config.get() + + # Verify total number of OTA platforms + assert len(updated_conf[CONF_OTA]) == expected_count + + # Verify warning + if warning_expected: + assert any( + "Found and merged" in record.message + and "web_server OTA" in record.message + for record in caplog.records + ), "Expected merge warning not found in log" + else: + assert len(caplog.records) == 0, "Unexpected warnings logged" + finally: + fv.full_config.reset(token) + + +def test_web_server_ota_consistent_manual_ids( + caplog: pytest.LogCaptureFixture, +) -> None: + """Test that consistent manual IDs can be merged successfully.""" + ota_configs = [ + { + CONF_PLATFORM: CONF_WEB_SERVER, + CONF_ID: ID("ota_web", is_manual=True), + }, + { + CONF_PLATFORM: CONF_WEB_SERVER, + CONF_ID: ID("ota_web", is_manual=True), + }, + ] + + full_conf = {CONF_OTA: ota_configs} + + token = fv.full_config.set(full_conf) + try: + with caplog.at_level(logging.WARNING): + _web_server_ota_final_validate({}) + + updated_conf = fv.full_config.get() + assert len(updated_conf[CONF_OTA]) == 1 + assert updated_conf[CONF_OTA][0][CONF_ID].id == "ota_web" + assert any( + "Found and merged" in record.message and "web_server OTA" in record.message + for record in caplog.records + ) + finally: + fv.full_config.reset(token) + + +def test_web_server_ota_inconsistent_manual_ids() -> None: + """Test that inconsistent manual IDs raise an error.""" + ota_configs = [ + { + CONF_PLATFORM: CONF_WEB_SERVER, + CONF_ID: ID("ota_web_1", is_manual=True), + }, + { + CONF_PLATFORM: CONF_WEB_SERVER, + CONF_ID: ID("ota_web_2", is_manual=True), + }, + ] + + full_conf = {CONF_OTA: ota_configs} + + token = fv.full_config.set(full_conf) + try: + with pytest.raises( + cv.Invalid, + match="Found multiple web_server OTA configurations but id is inconsistent", + ): + _web_server_ota_final_validate({}) + finally: + fv.full_config.reset(token) From 9e02e3191798010befd85006f17ebebc4a03b360 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 16 Nov 2025 18:39:01 -0600 Subject: [PATCH 508/526] [web_server_idf] Fix lwIP assertion crash by shutting down sockets on connection close (#11937) --- .../components/web_server_idf/web_server_idf.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index 0dab5e7e8c..ce91569de2 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -489,10 +489,18 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest * void AsyncEventSourceResponse::destroy(void *ptr) { auto *rsp = static_cast(ptr); - ESP_LOGD(TAG, "Event source connection closed (fd: %d)", rsp->fd_.load()); - // Mark as dead by setting fd to 0 - will be cleaned up in the main loop - rsp->fd_.store(0); - // Note: We don't delete or remove from set here to avoid race conditions + int fd = rsp->fd_.exchange(0); // Atomically get and clear fd + + if (fd > 0) { + ESP_LOGD(TAG, "Event source connection closed (fd: %d)", fd); + // Immediately shut down the socket to prevent lwIP from delivering more data + // This prevents "recv_tcp: recv for wrong pcb!" assertions when the TCP stack + // tries to deliver queued data after the session is marked as dead + // See: https://github.com/esphome/esphome/issues/11936 + shutdown(fd, SHUT_RDWR); + // Note: We don't close() the socket - httpd owns it and will close it + } + // Session will be cleaned up in the main loop to avoid race conditions } // helper for allowing only unique entries in the queue From 6c6b03bda05a32519b0690e11b12706f1e2df24d Mon Sep 17 00:00:00 2001 From: Anton Sergunov Date: Mon, 17 Nov 2025 07:25:00 +0600 Subject: [PATCH 509/526] [uart] Setup uart pins only if flags are set (#11914) Co-authored-by: J. Nick Koston --- .../components/uart/uart_component_esp8266.cpp | 18 +++++++++++++----- .../components/uart/uart_component_esp_idf.cpp | 18 +++++++++++++----- .../uart/uart_component_libretiny.cpp | 2 +- .../components/uart/uart_component_rp2040.cpp | 18 +++++++++++++----- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/esphome/components/uart/uart_component_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp index 7a453dbb50..c84a877ef4 100644 --- a/esphome/components/uart/uart_component_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -56,11 +56,19 @@ uint32_t ESP8266UartComponent::get_config() { } void ESP8266UartComponent::setup() { - if (this->rx_pin_) { - this->rx_pin_->setup(); - } - if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) { - this->tx_pin_->setup(); + auto setup_pin_if_needed = [](InternalGPIOPin *pin) { + if (!pin) { + return; + } + const auto mask = gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN; + if ((pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) { + pin->setup(); + } + }; + + setup_pin_if_needed(this->rx_pin_); + if (this->rx_pin_ != this->tx_pin_) { + setup_pin_if_needed(this->tx_pin_); } // Use Arduino HardwareSerial UARTs if all used pins match the ones diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 70a13c9e37..61ca8c1c0c 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -133,11 +133,19 @@ void IDFUARTComponent::load_settings(bool dump_config) { return; } - if (this->rx_pin_) { - this->rx_pin_->setup(); - } - if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) { - this->tx_pin_->setup(); + auto setup_pin_if_needed = [](InternalGPIOPin *pin) { + if (!pin) { + return; + } + const auto mask = gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN; + if ((pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) { + pin->setup(); + } + }; + + setup_pin_if_needed(this->rx_pin_); + if (this->rx_pin_ != this->tx_pin_) { + setup_pin_if_needed(this->tx_pin_); } int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1; diff --git a/esphome/components/uart/uart_component_libretiny.cpp b/esphome/components/uart/uart_component_libretiny.cpp index 8d1d28fce4..1e408b169b 100644 --- a/esphome/components/uart/uart_component_libretiny.cpp +++ b/esphome/components/uart/uart_component_libretiny.cpp @@ -53,7 +53,7 @@ void LibreTinyUARTComponent::setup() { auto shouldFallbackToSoftwareSerial = [&]() -> bool { auto hasFlags = [](InternalGPIOPin *pin, const gpio::Flags mask) -> bool { - return pin && pin->get_flags() & mask != gpio::Flags::FLAG_NONE; + return pin && (pin->get_flags() & mask) != gpio::Flags::FLAG_NONE; }; if (hasFlags(this->tx_pin_, gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN) || hasFlags(this->rx_pin_, gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN)) { diff --git a/esphome/components/uart/uart_component_rp2040.cpp b/esphome/components/uart/uart_component_rp2040.cpp index c78691653d..cd3905b5c1 100644 --- a/esphome/components/uart/uart_component_rp2040.cpp +++ b/esphome/components/uart/uart_component_rp2040.cpp @@ -52,11 +52,19 @@ uint16_t RP2040UartComponent::get_config() { } void RP2040UartComponent::setup() { - if (this->rx_pin_) { - this->rx_pin_->setup(); - } - if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) { - this->tx_pin_->setup(); + auto setup_pin_if_needed = [](InternalGPIOPin *pin) { + if (!pin) { + return; + } + const auto mask = gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN; + if ((pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) { + pin->setup(); + } + }; + + setup_pin_if_needed(this->rx_pin_); + if (this->rx_pin_ != this->tx_pin_) { + setup_pin_if_needed(this->tx_pin_); } uint16_t config = get_config(); From a38c4e0c6e4c4e4bf960db6f1e75f74afdfcb574 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 17 Nov 2025 15:32:09 +1300 Subject: [PATCH 510/526] Bump version to 2025.11.0b3 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index 8766b8f00c..04046d9ce6 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.11.0b2 +PROJECT_NUMBER = 2025.11.0b3 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index 8c6020a868..2ca0018fdb 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.11.0b2" +__version__ = "2025.11.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 6c8577678c63c01c12734f9c0cbc0966268bbffa Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Nov 2025 14:01:07 -0600 Subject: [PATCH 511/526] [captive_portal] Warn when enabled without WiFi AP configured (#11856) --- esphome/components/captive_portal/__init__.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/esphome/components/captive_portal/__init__.py b/esphome/components/captive_portal/__init__.py index 99acb76bcf..9bd3ef8a05 100644 --- a/esphome/components/captive_portal/__init__.py +++ b/esphome/components/captive_portal/__init__.py @@ -1,9 +1,12 @@ +import logging + import esphome.codegen as cg from esphome.components import web_server_base from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( + CONF_AP, CONF_ID, PLATFORM_BK72XX, PLATFORM_ESP32, @@ -14,6 +17,10 @@ from esphome.const import ( ) from esphome.core import CORE, coroutine_with_priority from esphome.coroutine import CoroPriority +import esphome.final_validate as fv +from esphome.types import ConfigType + +_LOGGER = logging.getLogger(__name__) def AUTO_LOAD() -> list[str]: @@ -50,6 +57,27 @@ CONFIG_SCHEMA = cv.All( ) +def _final_validate(config: ConfigType) -> ConfigType: + full_config = fv.full_config.get() + wifi_conf = full_config.get("wifi") + + if wifi_conf is None: + # This shouldn't happen due to DEPENDENCIES = ["wifi"], but check anyway + raise cv.Invalid("Captive portal requires the wifi component to be configured") + + if CONF_AP not in wifi_conf: + _LOGGER.warning( + "Captive portal is enabled but no WiFi AP is configured. " + "The captive portal will not be accessible. " + "Add 'ap:' to your WiFi configuration to enable the captive portal." + ) + + return config + + +FINAL_VALIDATE_SCHEMA = _final_validate + + @coroutine_with_priority(CoroPriority.CAPTIVE_PORTAL) async def to_code(config): paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID]) From 3b25fdbc5f458067494a87ddb8f59e4669ae85ad Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 17 Nov 2025 12:32:08 -0500 Subject: [PATCH 512/526] [core] Add support for setting environment variables (#11953) --- esphome/const.py | 1 + esphome/core/config.py | 15 +++++++++++++++ tests/components/esphome/common.yaml | 3 +++ 3 files changed, 19 insertions(+) diff --git a/esphome/const.py b/esphome/const.py index 2ca0018fdb..8360531bff 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -336,6 +336,7 @@ CONF_ENERGY = "energy" CONF_ENTITY_CATEGORY = "entity_category" CONF_ENTITY_ID = "entity_id" CONF_ENUM_DATAPOINT = "enum_datapoint" +CONF_ENVIRONMENT_VARIABLES = "environment_variables" CONF_EQUATION = "equation" CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support" CONF_ESPHOME = "esphome" diff --git a/esphome/core/config.py b/esphome/core/config.py index 763f9ebd9f..0a239c5f5e 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -17,6 +17,7 @@ from esphome.const import ( CONF_COMPILE_PROCESS_LIMIT, CONF_DEBUG_SCHEDULER, CONF_DEVICES, + CONF_ENVIRONMENT_VARIABLES, CONF_ESPHOME, CONF_FRIENDLY_NAME, CONF_ID, @@ -215,6 +216,11 @@ CONFIG_SCHEMA = cv.All( cv.string_strict: cv.Any([cv.string], cv.string), } ), + cv.Optional(CONF_ENVIRONMENT_VARIABLES, default={}): cv.Schema( + { + cv.string_strict: cv.string, + } + ), cv.Optional(CONF_ON_BOOT): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger), @@ -426,6 +432,12 @@ async def _add_platformio_options(pio_options): cg.add_platformio_option(key, val) +@coroutine_with_priority(CoroPriority.FINAL) +async def _add_environment_variables(env_vars: dict[str, str]) -> None: + # Set environment variables for the build process + os.environ.update(env_vars) + + @coroutine_with_priority(CoroPriority.AUTOMATION) async def _add_automations(config): for conf in config.get(CONF_ON_BOOT, []): @@ -563,6 +575,9 @@ async def to_code(config: ConfigType) -> None: if config[CONF_PLATFORMIO_OPTIONS]: CORE.add_job(_add_platformio_options, config[CONF_PLATFORMIO_OPTIONS]) + if config[CONF_ENVIRONMENT_VARIABLES]: + CORE.add_job(_add_environment_variables, config[CONF_ENVIRONMENT_VARIABLES]) + # Process areas all_areas: list[dict[str, str | core.ID]] = [] if CONF_AREA in config: diff --git a/tests/components/esphome/common.yaml b/tests/components/esphome/common.yaml index b2d7bccaa5..db75b08b38 100644 --- a/tests/components/esphome/common.yaml +++ b/tests/components/esphome/common.yaml @@ -2,6 +2,9 @@ esphome: debug_scheduler: true platformio_options: board_build.flash_mode: dio + environment_variables: + TEST_ENV_VAR: "test_value" + BUILD_NUMBER: "12345" area: id: testing_area name: Testing Area From e8998a79c71309164c0d4f4ab8cb0cce6c1b25c0 Mon Sep 17 00:00:00 2001 From: strange_v Date: Tue, 18 Nov 2025 02:32:17 +0100 Subject: [PATCH 513/526] [mipi_rgb] Fix GUITION-4848S040 colors (#11709) --- esphome/components/mipi_rgb/mipi_rgb.cpp | 15 ++++++++------- esphome/components/mipi_rgb/models/guition.py | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/esphome/components/mipi_rgb/mipi_rgb.cpp b/esphome/components/mipi_rgb/mipi_rgb.cpp index 00c9c8cbff..080fb08c09 100644 --- a/esphome/components/mipi_rgb/mipi_rgb.cpp +++ b/esphome/components/mipi_rgb/mipi_rgb.cpp @@ -350,6 +350,7 @@ void MipiRgb::dump_config() { "\n Width: %u" "\n Height: %u" "\n Rotation: %d degrees" + "\n PCLK Inverted: %s" "\n HSync Pulse Width: %u" "\n HSync Back Porch: %u" "\n HSync Front Porch: %u" @@ -357,18 +358,18 @@ void MipiRgb::dump_config() { "\n VSync Back Porch: %u" "\n VSync Front Porch: %u" "\n Invert Colors: %s" - "\n Pixel Clock: %dMHz" + "\n Pixel Clock: %uMHz" "\n Reset Pin: %s" "\n DE Pin: %s" "\n PCLK Pin: %s" "\n HSYNC Pin: %s" "\n VSYNC Pin: %s", - this->model_, this->width_, this->height_, this->rotation_, this->hsync_pulse_width_, - this->hsync_back_porch_, this->hsync_front_porch_, this->vsync_pulse_width_, this->vsync_back_porch_, - this->vsync_front_porch_, YESNO(this->invert_colors_), this->pclk_frequency_ / 1000000, - get_pin_name(this->reset_pin_).c_str(), get_pin_name(this->de_pin_).c_str(), - get_pin_name(this->pclk_pin_).c_str(), get_pin_name(this->hsync_pin_).c_str(), - get_pin_name(this->vsync_pin_).c_str()); + this->model_, this->width_, this->height_, this->rotation_, YESNO(this->pclk_inverted_), + this->hsync_pulse_width_, this->hsync_back_porch_, this->hsync_front_porch_, this->vsync_pulse_width_, + this->vsync_back_porch_, this->vsync_front_porch_, YESNO(this->invert_colors_), + (unsigned) (this->pclk_frequency_ / 1000000), get_pin_name(this->reset_pin_).c_str(), + get_pin_name(this->de_pin_).c_str(), get_pin_name(this->pclk_pin_).c_str(), + get_pin_name(this->hsync_pin_).c_str(), get_pin_name(this->vsync_pin_).c_str()); if (this->madctl_ & MADCTL_BGR) { this->dump_pins_(8, 13, "Blue", 0); diff --git a/esphome/components/mipi_rgb/models/guition.py b/esphome/components/mipi_rgb/models/guition.py index da433e686e..915b8beda0 100644 --- a/esphome/components/mipi_rgb/models/guition.py +++ b/esphome/components/mipi_rgb/models/guition.py @@ -11,6 +11,7 @@ st7701s.extend( vsync_pin=17, pclk_pin=21, pclk_frequency="12MHz", + pclk_inverted=False, pixel_mode="18bit", mirror_x=True, mirror_y=True, From 70aa94b8a40845b23f573d425845e11376f24fe3 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:27:50 +1000 Subject: [PATCH 514/526] [lvgl] Apply scale to spinbox value (#11946) --- esphome/components/lvgl/widgets/spinbox.py | 5 ++++- tests/components/lvgl/lvgl-package.yaml | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/esphome/components/lvgl/widgets/spinbox.py b/esphome/components/lvgl/widgets/spinbox.py index ac23ded723..c6f25e9587 100644 --- a/esphome/components/lvgl/widgets/spinbox.py +++ b/esphome/components/lvgl/widgets/spinbox.py @@ -1,6 +1,7 @@ from esphome import automation import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_RANGE_FROM, CONF_RANGE_TO, CONF_STEP, CONF_VALUE +from esphome.cpp_generator import MockObj from ..automation import action_to_code from ..defines import ( @@ -114,7 +115,9 @@ class SpinboxType(WidgetType): w.obj, digits, digits - config[CONF_DECIMAL_PLACES] ) if (value := config.get(CONF_VALUE)) is not None: - lv.spinbox_set_value(w.obj, await lv_float.process(value)) + lv.spinbox_set_value( + w.obj, MockObj(await lv_float.process(value)) * w.get_scale() + ) def get_scale(self, config): return 10 ** config[CONF_DECIMAL_PLACES] diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index d7c342b16e..fba860a407 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -703,7 +703,9 @@ lvgl: on_value: - lvgl.spinbox.update: id: spinbox_id - value: !lambda return x; + value: !lambda |- + static float yyy = 83.0; + return yyy + .8; - button: styles: spin_button id: spin_up From 93215f17375679b1c847b2a359bfe19533a9dff0 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:40:31 -0500 Subject: [PATCH 515/526] [esp32] Fix Arduino build on some ESP32 S2 boards (#11972) --- esphome/components/esp32/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 0f85e585f7..6f577d2926 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -931,6 +931,12 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True) add_idf_sdkconfig_option("CONFIG_ESP_PHY_REDUCE_TX_POWER", True) + # ESP32-S2 Arduino: Disable USB Serial on boot to avoid TinyUSB dependency + if get_esp32_variant() == VARIANT_ESP32S2: + cg.add_build_unflag("-DARDUINO_USB_CDC_ON_BOOT=1") + cg.add_build_unflag("-DARDUINO_USB_CDC_ON_BOOT=0") + cg.add_build_flag("-DARDUINO_USB_CDC_ON_BOOT=0") + cg.add_build_flag("-Wno-nonnull-compare") add_idf_sdkconfig_option(f"CONFIG_IDF_TARGET_{variant}", True) From 6db73df649ce1d63c66fd1ead771a31bd5c5f912 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 18 Nov 2025 12:16:18 -0600 Subject: [PATCH 516/526] [scheduler] Add defensive nullptr checks and explicit locking requirements (#11974) --- esphome/core/scheduler.cpp | 14 ++++++++------ esphome/core/scheduler.h | 35 +++++++++++++++++++++++------------ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index d2e0f0dab4..09d50ee7c8 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -154,8 +154,8 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type // For retries, check if there's a cancelled timeout first if (is_retry && name_cstr != nullptr && type == SchedulerItem::TIMEOUT && - (has_cancelled_timeout_in_container_(this->items_, component, name_cstr, /* match_retry= */ true) || - has_cancelled_timeout_in_container_(this->to_add_, component, name_cstr, /* match_retry= */ true))) { + (has_cancelled_timeout_in_container_locked_(this->items_, component, name_cstr, /* match_retry= */ true) || + has_cancelled_timeout_in_container_locked_(this->to_add_, component, name_cstr, /* match_retry= */ true))) { // Skip scheduling - the retry was cancelled #ifdef ESPHOME_DEBUG_SCHEDULER ESP_LOGD(TAG, "Skipping retry '%s' - found cancelled item", name_cstr); @@ -556,7 +556,8 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c #ifndef ESPHOME_THREAD_SINGLE // Mark items in defer queue as cancelled (they'll be skipped when processed) if (type == SchedulerItem::TIMEOUT) { - total_cancelled += this->mark_matching_items_removed_(this->defer_queue_, component, name_cstr, type, match_retry); + total_cancelled += + this->mark_matching_items_removed_locked_(this->defer_queue_, component, name_cstr, type, match_retry); } #endif /* not ESPHOME_THREAD_SINGLE */ @@ -565,19 +566,20 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c // (removing the last element doesn't break heap structure) if (!this->items_.empty()) { auto &last_item = this->items_.back(); - if (this->matches_item_(last_item, component, name_cstr, type, match_retry)) { + if (this->matches_item_locked_(last_item, component, name_cstr, type, match_retry)) { this->recycle_item_(std::move(this->items_.back())); this->items_.pop_back(); total_cancelled++; } // For other items in heap, we can only mark for removal (can't remove from middle of heap) - size_t heap_cancelled = this->mark_matching_items_removed_(this->items_, component, name_cstr, type, match_retry); + size_t heap_cancelled = + this->mark_matching_items_removed_locked_(this->items_, component, name_cstr, type, match_retry); total_cancelled += heap_cancelled; this->to_remove_ += heap_cancelled; // Track removals for heap items } // Cancel items in to_add_ - total_cancelled += this->mark_matching_items_removed_(this->to_add_, component, name_cstr, type, match_retry); + total_cancelled += this->mark_matching_items_removed_locked_(this->to_add_, component, name_cstr, type, match_retry); return total_cancelled > 0; } diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index fd16840240..bea1503df0 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -243,8 +243,18 @@ class Scheduler { } // Helper function to check if item matches criteria for cancellation - inline bool HOT matches_item_(const std::unique_ptr &item, Component *component, const char *name_cstr, - SchedulerItem::Type type, bool match_retry, bool skip_removed = true) const { + // IMPORTANT: Must be called with scheduler lock held + inline bool HOT matches_item_locked_(const std::unique_ptr &item, Component *component, + const char *name_cstr, SchedulerItem::Type type, bool match_retry, + bool skip_removed = true) const { + // THREAD SAFETY: Check for nullptr first to prevent LoadProhibited crashes. On multi-threaded + // platforms, items can be moved out of defer_queue_ during processing, leaving nullptr entries. + // PR #11305 added nullptr checks in callers (mark_matching_items_removed_locked_() and + // has_cancelled_timeout_in_container_locked_()), but this check provides defense-in-depth: helper + // functions should be safe regardless of caller behavior. + // Fixes: https://github.com/esphome/esphome/issues/11940 + if (!item) + return false; if (item->component != component || item->type != type || (skip_removed && item->remove) || (match_retry && !item->is_retry)) { return false; @@ -304,8 +314,8 @@ class Scheduler { // SAFETY: Moving out the unique_ptr leaves a nullptr in the vector at defer_queue_front_. // This is intentional and safe because: // 1. The vector is only cleaned up by cleanup_defer_queue_locked_() at the end of this function - // 2. Any code iterating defer_queue_ MUST check for nullptr items (see mark_matching_items_removed_ - // and has_cancelled_timeout_in_container_ in scheduler.h) + // 2. Any code iterating defer_queue_ MUST check for nullptr items (see mark_matching_items_removed_locked_ + // and has_cancelled_timeout_in_container_locked_ in scheduler.h) // 3. The lock protects concurrent access, but the nullptr remains until cleanup item = std::move(this->defer_queue_[this->defer_queue_front_]); this->defer_queue_front_++; @@ -393,10 +403,10 @@ class Scheduler { // Helper to mark matching items in a container as removed // Returns the number of items marked for removal - // IMPORTANT: Caller must hold the scheduler lock before calling this function. + // IMPORTANT: Must be called with scheduler lock held template - size_t mark_matching_items_removed_(Container &container, Component *component, const char *name_cstr, - SchedulerItem::Type type, bool match_retry) { + size_t mark_matching_items_removed_locked_(Container &container, Component *component, const char *name_cstr, + SchedulerItem::Type type, bool match_retry) { size_t count = 0; for (auto &item : container) { // Skip nullptr items (can happen in defer_queue_ when items are being processed) @@ -405,7 +415,7 @@ class Scheduler { // the vector can still contain nullptr items from the processing loop. This check prevents crashes. if (!item) continue; - if (this->matches_item_(item, component, name_cstr, type, match_retry)) { + if (this->matches_item_locked_(item, component, name_cstr, type, match_retry)) { // Mark item for removal (platform-specific) this->set_item_removed_(item.get(), true); count++; @@ -415,9 +425,10 @@ class Scheduler { } // Template helper to check if any item in a container matches our criteria + // IMPORTANT: Must be called with scheduler lock held template - bool has_cancelled_timeout_in_container_(const Container &container, Component *component, const char *name_cstr, - bool match_retry) const { + bool has_cancelled_timeout_in_container_locked_(const Container &container, Component *component, + const char *name_cstr, bool match_retry) const { for (const auto &item : container) { // Skip nullptr items (can happen in defer_queue_ when items are being processed) // The defer_queue_ uses index-based processing: items are std::moved out but left in the @@ -426,8 +437,8 @@ class Scheduler { if (!item) continue; if (is_item_removed_(item.get()) && - this->matches_item_(item, component, name_cstr, SchedulerItem::TIMEOUT, match_retry, - /* skip_removed= */ false)) { + this->matches_item_locked_(item, component, name_cstr, SchedulerItem::TIMEOUT, match_retry, + /* skip_removed= */ false)) { return true; } } From f18bc626909d68efde5156f59f85a817bc866971 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:29:40 -0500 Subject: [PATCH 517/526] [sfa30] Fix negative temperature values (#11973) --- esphome/components/sfa30/sfa30.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/sfa30/sfa30.cpp b/esphome/components/sfa30/sfa30.cpp index 99709d5fbb..bbe3bcd7d2 100644 --- a/esphome/components/sfa30/sfa30.cpp +++ b/esphome/components/sfa30/sfa30.cpp @@ -73,17 +73,17 @@ void SFA30Component::update() { } if (this->formaldehyde_sensor_ != nullptr) { - const float formaldehyde = raw_data[0] / 5.0f; + const float formaldehyde = static_cast(raw_data[0]) / 5.0f; this->formaldehyde_sensor_->publish_state(formaldehyde); } if (this->humidity_sensor_ != nullptr) { - const float humidity = raw_data[1] / 100.0f; + const float humidity = static_cast(raw_data[1]) / 100.0f; this->humidity_sensor_->publish_state(humidity); } if (this->temperature_sensor_ != nullptr) { - const float temperature = raw_data[2] / 200.0f; + const float temperature = static_cast(raw_data[2]) / 200.0f; this->temperature_sensor_->publish_state(temperature); } From f436f6ee2e3de361cdb5441aa0accf60986abb69 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 18 Nov 2025 13:17:21 -0600 Subject: [PATCH 518/526] [wifi] Fix captive portal unusable when WiFi credentials are wrong (#11965) --- esphome/components/captive_portal/__init__.py | 10 +++ .../captive_portal/captive_portal.cpp | 10 ++- .../captive_portal/captive_portal.h | 4 ++ esphome/components/esp32_improv/__init__.py | 6 +- .../esp32_improv/esp32_improv_component.cpp | 1 + .../esp32_improv/esp32_improv_component.h | 1 + .../web_server_idf/web_server_idf.cpp | 15 +++++ .../web_server_idf/web_server_idf.h | 4 ++ esphome/components/wifi/__init__.py | 8 ++- esphome/components/wifi/wifi_component.cpp | 62 +++++++++++++++---- esphome/components/wifi/wifi_component.h | 5 ++ 11 files changed, 111 insertions(+), 15 deletions(-) diff --git a/esphome/components/captive_portal/__init__.py b/esphome/components/captive_portal/__init__.py index 9bd3ef8a05..25d0a22083 100644 --- a/esphome/components/captive_portal/__init__.py +++ b/esphome/components/captive_portal/__init__.py @@ -72,6 +72,16 @@ def _final_validate(config: ConfigType) -> ConfigType: "Add 'ap:' to your WiFi configuration to enable the captive portal." ) + # Register socket needs for DNS server and additional HTTP connections + # - 1 UDP socket for DNS server + # - 3 additional TCP sockets for captive portal detection probes + configuration requests + # OS captive portal detection makes multiple probe requests that stay in TIME_WAIT. + # Need headroom for actual user configuration requests. + # LRU purging will reclaim idle sockets to prevent exhaustion from repeated attempts. + from esphome.components import socket + + socket.consume_sockets(4, "captive_portal")(config) + return config diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 30438747f2..459ac557c8 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -50,8 +50,8 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) { ESP_LOGI(TAG, "Requested WiFi Settings Change:"); ESP_LOGI(TAG, " SSID='%s'", ssid.c_str()); ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str()); - wifi::global_wifi_component->save_wifi_sta(ssid, psk); - wifi::global_wifi_component->start_scanning(); + // Defer save to main loop thread to avoid NVS operations from HTTP thread + this->defer([ssid, psk]() { wifi::global_wifi_component->save_wifi_sta(ssid, psk); }); request->redirect(ESPHOME_F("/?save")); } @@ -63,6 +63,12 @@ void CaptivePortal::start() { this->base_->init(); if (!this->initialized_) { this->base_->add_handler(this); +#ifdef USE_ESP32 + // Enable LRU socket purging to handle captive portal detection probe bursts + // OS captive portal detection makes many simultaneous HTTP requests which can + // exhaust sockets. LRU purging automatically closes oldest idle connections. + this->base_->get_server()->set_lru_purge_enable(true); +#endif } network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); diff --git a/esphome/components/captive_portal/captive_portal.h b/esphome/components/captive_portal/captive_portal.h index f48c286f0c..ae9b9dfba0 100644 --- a/esphome/components/captive_portal/captive_portal.h +++ b/esphome/components/captive_portal/captive_portal.h @@ -40,6 +40,10 @@ class CaptivePortal : public AsyncWebHandler, public Component { void end() { this->active_ = false; this->disable_loop(); // Stop processing DNS requests +#ifdef USE_ESP32 + // Disable LRU socket purging now that captive portal is done + this->base_->get_server()->set_lru_purge_enable(false); +#endif this->base_->deinit(); if (this->dns_server_ != nullptr) { this->dns_server_->stop(); diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index 1a7194da81..2e69d400ca 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -20,6 +20,10 @@ CONF_ON_STOP = "on_stop" CONF_STATUS_INDICATOR = "status_indicator" CONF_WIFI_TIMEOUT = "wifi_timeout" +# Default WiFi timeout - aligned with WiFi component ap_timeout +# Allows sufficient time to try all BSSIDs before starting provisioning mode +DEFAULT_WIFI_TIMEOUT = "90s" + improv_ns = cg.esphome_ns.namespace("improv") Error = improv_ns.enum("Error") @@ -59,7 +63,7 @@ CONFIG_SCHEMA = ( CONF_AUTHORIZED_DURATION, default="1min" ): cv.positive_time_period_milliseconds, cv.Optional( - CONF_WIFI_TIMEOUT, default="1min" + CONF_WIFI_TIMEOUT, default=DEFAULT_WIFI_TIMEOUT ): cv.positive_time_period_milliseconds, cv.Optional(CONF_ON_PROVISIONED): automation.validate_automation( { diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 398b1d4251..0ad54bbb15 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -127,6 +127,7 @@ void ESP32ImprovComponent::loop() { // Set initial state based on whether we have an authorizer this->set_state_(this->get_initial_state_(), false); this->set_error_(improv::ERROR_NONE); + this->should_start_ = false; // Clear flag after starting ESP_LOGD(TAG, "Service started!"); } } diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index 989552ea56..8f4cfd7958 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -45,6 +45,7 @@ class ESP32ImprovComponent : public Component, public improv_base::ImprovBase { void start(); void stop(); bool is_active() const { return this->state_ != improv::STATE_STOPPED; } + bool should_start() const { return this->should_start_; } #ifdef USE_ESP32_IMPROV_STATE_CALLBACK void add_on_state_callback(std::function &&callback) { diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index ce91569de2..f5a66f6bd9 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -94,6 +94,18 @@ void AsyncWebServer::end() { } } +void AsyncWebServer::set_lru_purge_enable(bool enable) { + if (this->lru_purge_enable_ == enable) { + return; // No change needed + } + this->lru_purge_enable_ = enable; + // If server is already running, restart it with new config + if (this->server_) { + this->end(); + this->begin(); + } +} + void AsyncWebServer::begin() { if (this->server_) { this->end(); @@ -101,6 +113,8 @@ void AsyncWebServer::begin() { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.server_port = this->port_; config.uri_match_fn = [](const char * /*unused*/, const char * /*unused*/, size_t /*unused*/) { return true; }; + // Enable LRU purging if requested (e.g., by captive portal to handle probe bursts) + config.lru_purge_enable = this->lru_purge_enable_; if (httpd_start(&this->server_, &config) == ESP_OK) { const httpd_uri_t handler_get = { .uri = "", @@ -242,6 +256,7 @@ void AsyncWebServerRequest::send(int code, const char *content_type, const char void AsyncWebServerRequest::redirect(const std::string &url) { httpd_resp_set_status(*this, "302 Found"); httpd_resp_set_hdr(*this, "Location", url.c_str()); + httpd_resp_set_hdr(*this, "Connection", "close"); httpd_resp_send(*this, nullptr, 0); } diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index 5ec6fec009..b9f690b462 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -199,9 +199,13 @@ class AsyncWebServer { return *handler; } + void set_lru_purge_enable(bool enable); + httpd_handle_t get_server() { return this->server_; } + protected: uint16_t port_{}; httpd_handle_t server_{}; + bool lru_purge_enable_{false}; static esp_err_t request_handler(httpd_req_t *r); static esp_err_t request_post_handler(httpd_req_t *r); esp_err_t request_handler_(AsyncWebServerRequest *request) const; diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 11bd7798e2..5b3b30e0e9 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -69,6 +69,12 @@ CONF_MIN_AUTH_MODE = "min_auth_mode" # Limited to 127 because selected_sta_index_ is int8_t in C++ MAX_WIFI_NETWORKS = 127 +# Default AP timeout - allows sufficient time to try all BSSIDs during initial connection +# After AP starts, WiFi scanning is skipped to avoid disrupting the AP, so we only +# get best-effort connection attempts. Longer timeout ensures we exhaust all options +# before falling back to AP mode. Aligned with improv wifi_timeout default. +DEFAULT_AP_TIMEOUT = "90s" + wifi_ns = cg.esphome_ns.namespace("wifi") EAPAuth = wifi_ns.struct("EAPAuth") ManualIP = wifi_ns.struct("ManualIP") @@ -177,7 +183,7 @@ CONF_AP_TIMEOUT = "ap_timeout" WIFI_NETWORK_AP = WIFI_NETWORK_BASE.extend( { cv.Optional( - CONF_AP_TIMEOUT, default="1min" + CONF_AP_TIMEOUT, default=DEFAULT_AP_TIMEOUT ): cv.positive_time_period_milliseconds, } ) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 880928c3e3..e31d7bbf32 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -199,7 +199,12 @@ static constexpr uint8_t WIFI_RETRY_COUNT_PER_AP = 1; /// Cooldown duration in milliseconds after adapter restart or repeated failures /// Allows WiFi hardware to stabilize before next connection attempt -static constexpr uint32_t WIFI_COOLDOWN_DURATION_MS = 1000; +static constexpr uint32_t WIFI_COOLDOWN_DURATION_MS = 500; + +/// Cooldown duration when fallback AP is active and captive portal may be running +/// Longer interval gives users time to configure WiFi without constant connection attempts +/// While connecting, WiFi can't beacon the AP properly, so needs longer cooldown +static constexpr uint32_t WIFI_COOLDOWN_WITH_AP_ACTIVE_MS = 30000; static constexpr uint8_t get_max_retries_for_phase(WiFiRetryPhase phase) { switch (phase) { @@ -275,7 +280,9 @@ int8_t WiFiComponent::find_next_hidden_sta_(int8_t start_index) { } } - if (!this->ssid_was_seen_in_scan_(sta.get_ssid())) { + // If we didn't scan this cycle, treat all networks as potentially hidden + // Otherwise, only retry networks that weren't seen in the scan + if (!this->did_scan_this_cycle_ || !this->ssid_was_seen_in_scan_(sta.get_ssid())) { ESP_LOGD(TAG, "Hidden candidate " LOG_SECRET("'%s'") " at index %d", sta.get_ssid().c_str(), static_cast(i)); return static_cast(i); } @@ -417,10 +424,6 @@ void WiFiComponent::start() { void WiFiComponent::restart_adapter() { ESP_LOGW(TAG, "Restarting adapter"); this->wifi_mode_(false, {}); - // Enter cooldown state to allow WiFi hardware to stabilize after restart - // Don't set retry_phase_ or num_retried_ here - state machine handles transitions - this->state_ = WIFI_COMPONENT_STATE_COOLDOWN; - this->action_started_ = millis(); this->error_from_callback_ = false; } @@ -441,7 +444,16 @@ void WiFiComponent::loop() { switch (this->state_) { case WIFI_COMPONENT_STATE_COOLDOWN: { this->status_set_warning(LOG_STR("waiting to reconnect")); - if (now - this->action_started_ > WIFI_COOLDOWN_DURATION_MS) { + // Skip cooldown if new credentials were provided while connecting + if (this->skip_cooldown_next_cycle_) { + this->skip_cooldown_next_cycle_ = false; + this->check_connecting_finished(); + break; + } + // Use longer cooldown when captive portal/improv is active to avoid disrupting user config + bool portal_active = this->is_captive_portal_active_() || this->is_esp32_improv_active_(); + uint32_t cooldown_duration = portal_active ? WIFI_COOLDOWN_WITH_AP_ACTIVE_MS : WIFI_COOLDOWN_DURATION_MS; + if (now - this->action_started_ > cooldown_duration) { // After cooldown we either restarted the adapter because of // a failure, or something tried to connect over and over // so we entered cooldown. In both cases we call @@ -495,7 +507,8 @@ void WiFiComponent::loop() { #endif // USE_WIFI_AP #ifdef USE_IMPROV - if (esp32_improv::global_improv_component != nullptr && !esp32_improv::global_improv_component->is_active()) { + if (esp32_improv::global_improv_component != nullptr && !esp32_improv::global_improv_component->is_active() && + !esp32_improv::global_improv_component->should_start()) { if (now - this->last_connected_ > esp32_improv::global_improv_component->get_wifi_timeout()) { if (this->wifi_mode_(true, {})) esp32_improv::global_improv_component->start(); @@ -605,6 +618,8 @@ void WiFiComponent::set_sta(const WiFiAP &ap) { this->init_sta(1); this->add_sta(ap); this->selected_sta_index_ = 0; + // When new credentials are set (e.g., from improv), skip cooldown to retry immediately + this->skip_cooldown_next_cycle_ = true; } WiFiAP WiFiComponent::build_params_for_current_phase_() { @@ -666,6 +681,17 @@ void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &pa sta.set_ssid(ssid); sta.set_password(password); this->set_sta(sta); + + // Trigger connection attempt (exits cooldown if needed, no-op if already connecting/connected) + this->connect_soon_(); +} + +void WiFiComponent::connect_soon_() { + // Only trigger retry if we're in cooldown - if already connecting/connected, do nothing + if (this->state_ == WIFI_COMPONENT_STATE_COOLDOWN) { + ESP_LOGD(TAG, "Exiting cooldown early due to new WiFi credentials"); + this->retry_connect(); + } } void WiFiComponent::start_connecting(const WiFiAP &ap) { @@ -961,6 +987,7 @@ void WiFiComponent::check_scanning_finished() { return; } this->scan_done_ = false; + this->did_scan_this_cycle_ = true; if (this->scan_result_.empty()) { ESP_LOGW(TAG, "No networks found"); @@ -1227,9 +1254,16 @@ WiFiRetryPhase WiFiComponent::determine_next_phase_() { return WiFiRetryPhase::RESTARTING_ADAPTER; case WiFiRetryPhase::RESTARTING_ADAPTER: - // After restart, go back to explicit hidden if we went through it initially, otherwise scan - return this->went_through_explicit_hidden_phase_() ? WiFiRetryPhase::EXPLICIT_HIDDEN - : WiFiRetryPhase::SCAN_CONNECTING; + // After restart, go back to explicit hidden if we went through it initially + if (this->went_through_explicit_hidden_phase_()) { + return WiFiRetryPhase::EXPLICIT_HIDDEN; + } + // Skip scanning when captive portal/improv is active to avoid disrupting AP + // Even passive scans can cause brief AP disconnections on ESP32 + if (this->is_captive_portal_active_() || this->is_esp32_improv_active_()) { + return WiFiRetryPhase::RETRY_HIDDEN; + } + return WiFiRetryPhase::SCAN_CONNECTING; } // Should never reach here @@ -1317,6 +1351,12 @@ bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) { if (!this->is_captive_portal_active_() && !this->is_esp32_improv_active_()) { this->restart_adapter(); } + // Clear scan flag - we're starting a new retry cycle + this->did_scan_this_cycle_ = false; + // Always enter cooldown after restart (or skip-restart) to allow stabilization + // Use extended cooldown when AP is active to avoid constant scanning that blocks DNS + this->state_ = WIFI_COMPONENT_STATE_COOLDOWN; + this->action_started_ = millis(); // Return true to indicate we should wait (go to COOLDOWN) instead of immediately connecting return true; diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 5023cf3428..2e0a9816c6 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -291,6 +291,7 @@ class WiFiComponent : public Component { void set_passive_scan(bool passive); void save_wifi_sta(const std::string &ssid, const std::string &password); + // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) /// Setup WiFi interface. @@ -424,6 +425,8 @@ class WiFiComponent : public Component { return true; } + void connect_soon_(); + void wifi_loop_(); bool wifi_mode_(optional sta, optional ap); bool wifi_sta_pre_setup_(); @@ -529,6 +532,8 @@ class WiFiComponent : public Component { bool enable_on_boot_; bool got_ipv4_address_{false}; bool keep_scan_results_{false}; + bool did_scan_this_cycle_{false}; + bool skip_cooldown_next_cycle_{false}; // Pointers at the end (naturally aligned) Trigger<> *connect_trigger_{new Trigger<>()}; From 2681a14d05cbc1df1f434b2d5270b5b326e54140 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 19 Nov 2025 09:17:33 +1300 Subject: [PATCH 519/526] Bump version to 2025.11.0b4 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index 04046d9ce6..c7b2187964 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.11.0b3 +PROJECT_NUMBER = 2025.11.0b4 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index 8360531bff..7a3a79f270 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.11.0b3" +__version__ = "2025.11.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 13b875c763b990a6d56104bd619f391c09ccceec Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 18 Nov 2025 23:19:54 -0600 Subject: [PATCH 520/526] [tests] Fix SNTP time ID conflicts in component tests for grouped testing (#11990) --- tests/components/lvgl/common.yaml | 4 ++-- tests/components/lvgl/lvgl-package.yaml | 6 +++--- tests/components/mqtt/common.yaml | 1 + tests/components/uptime/common.yaml | 1 + tests/components/wireguard/common.yaml | 2 ++ 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index c70dd7568d..652ae7e7a1 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -115,8 +115,8 @@ wifi: password: PASSWORD123 time: - platform: sntp - id: time_id + - platform: sntp + id: sntp_time text: - id: lvgl_text diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index fba860a407..cb5b6f59b1 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -478,19 +478,19 @@ lvgl: id: hello_label text: time_format: "%c" - time: time_id + time: sntp_time - lvgl.label.update: id: hello_label text: time_format: "%c" - time: !lambda return id(time_id).now(); + time: !lambda return id(sntp_time).now(); - lvgl.label.update: id: hello_label text: time_format: "%c" time: !lambda |- ESP_LOGD("label", "multi-line lambda"); - return id(time_id).now(); + return id(sntp_time).now(); on_value: logger.log: format: "state now %d" diff --git a/tests/components/mqtt/common.yaml b/tests/components/mqtt/common.yaml index 3f1b83bb01..284ac30337 100644 --- a/tests/components/mqtt/common.yaml +++ b/tests/components/mqtt/common.yaml @@ -4,6 +4,7 @@ wifi: time: - platform: sntp + id: sntp_time mqtt: broker: "192.168.178.84" diff --git a/tests/components/uptime/common.yaml b/tests/components/uptime/common.yaml index 86b764e7ff..279258c670 100644 --- a/tests/components/uptime/common.yaml +++ b/tests/components/uptime/common.yaml @@ -3,6 +3,7 @@ wifi: time: - platform: sntp + id: sntp_time sensor: - platform: uptime diff --git a/tests/components/wireguard/common.yaml b/tests/components/wireguard/common.yaml index cd7ab1075e..342ffa32f6 100644 --- a/tests/components/wireguard/common.yaml +++ b/tests/components/wireguard/common.yaml @@ -4,8 +4,10 @@ wifi: time: - platform: sntp + id: sntp_time wireguard: + time_id: sntp_time address: 172.16.34.100 netmask: 255.255.255.0 # NEVER use the following keys for your VPN -- they are now public! From 7ef4b4f3d9d6cb8ce5414985ae59538647a3d438 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:12:34 -0500 Subject: [PATCH 521/526] [text_sensor] Fix infinite loop in substitute filter (#11989) Co-authored-by: J. Nick Koston --- esphome/components/text_sensor/filter.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/text_sensor/filter.cpp b/esphome/components/text_sensor/filter.cpp index a242b43b1c..40a37febee 100644 --- a/esphome/components/text_sensor/filter.cpp +++ b/esphome/components/text_sensor/filter.cpp @@ -66,10 +66,14 @@ SubstituteFilter::SubstituteFilter(const std::initializer_list &su : substitutions_(substitutions) {} optional SubstituteFilter::new_value(std::string value) { - std::size_t pos; for (const auto &sub : this->substitutions_) { - while ((pos = value.find(sub.from)) != std::string::npos) + std::size_t pos = 0; + while ((pos = value.find(sub.from, pos)) != std::string::npos) { value.replace(pos, sub.from.size(), sub.to); + // Advance past the replacement to avoid infinite loop when + // the replacement contains the search pattern (e.g., f -> foo) + pos += sub.to.size(); + } } return value; } From 0a224f919b3889651db230fbd047cf05f4996cdf Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:12:57 -0500 Subject: [PATCH 522/526] [wifi] Fix positive RSSI values on 8266 (#11994) --- esphome/components/wifi/wifi_component_esp8266.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index bdaae5382a..0134fcaed8 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -870,7 +870,13 @@ bssid_t WiFiComponent::wifi_bssid() { return bssid; } std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } -int8_t WiFiComponent::wifi_rssi() { return WiFi.status() == WL_CONNECTED ? WiFi.RSSI() : WIFI_RSSI_DISCONNECTED; } +int8_t WiFiComponent::wifi_rssi() { + if (WiFi.status() != WL_CONNECTED) + return WIFI_RSSI_DISCONNECTED; + int8_t rssi = WiFi.RSSI(); + // Values >= 31 are error codes per NONOS SDK API, not valid RSSI readings + return rssi >= 31 ? WIFI_RSSI_DISCONNECTED : rssi; +} int32_t WiFiComponent::get_wifi_channel() { return WiFi.channel(); } network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {(const ip_addr_t *) WiFi.subnetMask()}; } network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {(const ip_addr_t *) WiFi.gatewayIP()}; } From 71dc2d374d18f236c7a5fdae1eee61cbe78c0320 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 19 Nov 2025 12:58:33 -0600 Subject: [PATCH 523/526] [web_server_idf] Fix pbuf_free crash by moving shutdown before close (#11995) --- .../web_server_idf/web_server_idf.cpp | 41 ++++++++++++++----- .../web_server_idf/web_server_idf.h | 1 + 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index f5a66f6bd9..c910ed06c5 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -87,6 +87,29 @@ int nonblocking_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_ } } // namespace +void AsyncWebServer::safe_close_with_shutdown(httpd_handle_t hd, int sockfd) { + // CRITICAL: Shut down receive BEFORE closing to prevent lwIP race conditions + // + // The race condition occurs because close() initiates lwIP teardown while + // the TCP/IP thread can still receive packets, causing assertions when + // recv_tcp() sees partially-torn-down state. + // + // By shutting down receive first, we tell lwIP to stop accepting new data BEFORE + // the teardown begins, eliminating the race window. We only shutdown RD (not RDWR) + // to allow the FIN packet to be sent cleanly during close(). + // + // Note: This function may be called with an already-closed socket if the network + // stack closed it. In that case, shutdown() will fail but close() is safe to call. + // + // See: https://github.com/esphome/esphome-webserver/issues/163 + + // Attempt shutdown - ignore errors as socket may already be closed + shutdown(sockfd, SHUT_RD); + + // Always close - safe even if socket is already closed by network stack + close(sockfd); +} + void AsyncWebServer::end() { if (this->server_) { httpd_stop(this->server_); @@ -115,6 +138,8 @@ void AsyncWebServer::begin() { config.uri_match_fn = [](const char * /*unused*/, const char * /*unused*/, size_t /*unused*/) { return true; }; // Enable LRU purging if requested (e.g., by captive portal to handle probe bursts) config.lru_purge_enable = this->lru_purge_enable_; + // Use custom close function that shuts down before closing to prevent lwIP race conditions + config.close_fn = AsyncWebServer::safe_close_with_shutdown; if (httpd_start(&this->server_, &config) == ESP_OK) { const httpd_uri_t handler_get = { .uri = "", @@ -505,17 +530,11 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest * void AsyncEventSourceResponse::destroy(void *ptr) { auto *rsp = static_cast(ptr); int fd = rsp->fd_.exchange(0); // Atomically get and clear fd - - if (fd > 0) { - ESP_LOGD(TAG, "Event source connection closed (fd: %d)", fd); - // Immediately shut down the socket to prevent lwIP from delivering more data - // This prevents "recv_tcp: recv for wrong pcb!" assertions when the TCP stack - // tries to deliver queued data after the session is marked as dead - // See: https://github.com/esphome/esphome/issues/11936 - shutdown(fd, SHUT_RDWR); - // Note: We don't close() the socket - httpd owns it and will close it - } - // Session will be cleaned up in the main loop to avoid race conditions + ESP_LOGD(TAG, "Event source connection closed (fd: %d)", fd); + // Mark as dead - will be cleaned up in the main loop + // Note: We don't delete or remove from set here to avoid race conditions + // httpd will call our custom close_fn (safe_close_with_shutdown) which handles + // shutdown() before close() to prevent lwIP race conditions } // helper for allowing only unique entries in the queue diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index b9f690b462..a139e9e4df 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -209,6 +209,7 @@ class AsyncWebServer { static esp_err_t request_handler(httpd_req_t *r); static esp_err_t request_post_handler(httpd_req_t *r); esp_err_t request_handler_(AsyncWebServerRequest *request) const; + static void safe_close_with_shutdown(httpd_handle_t hd, int sockfd); #ifdef USE_WEBSERVER_OTA esp_err_t handle_multipart_upload_(httpd_req_t *r, const char *content_type); #endif From 1157b4aee8b9c6a991854928a05f3169715fd0a6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 20 Nov 2025 08:11:45 +1300 Subject: [PATCH 524/526] [epaper_spi] Add basic `7.3in-Spectra-E6` model (#12001) --- esphome/components/epaper_spi/display.py | 2 +- esphome/components/epaper_spi/models/spectra_e6.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/esphome/components/epaper_spi/display.py b/esphome/components/epaper_spi/display.py index 9ff393b397..182c37ba40 100644 --- a/esphome/components/epaper_spi/display.py +++ b/esphome/components/epaper_spi/display.py @@ -102,7 +102,7 @@ def customise_schema(config): """ config = cv.Schema( { - cv.Required(CONF_MODEL): cv.one_of(*MODELS, upper=True), + cv.Required(CONF_MODEL): cv.one_of(*MODELS, upper=True, space="-"), }, extra=cv.ALLOW_EXTRA, )(config) diff --git a/esphome/components/epaper_spi/models/spectra_e6.py b/esphome/components/epaper_spi/models/spectra_e6.py index 9f0b673d69..42a5a7da72 100644 --- a/esphome/components/epaper_spi/models/spectra_e6.py +++ b/esphome/components/epaper_spi/models/spectra_e6.py @@ -32,11 +32,15 @@ class SpectraE6(EpaperModel): spectra_e6 = SpectraE6("spectra-e6") -spectra_e6.extend( - "Seeed-reTerminal-E1002", +spectra_e6_7p3 = spectra_e6.extend( + "7.3in-Spectra-E6", width=800, height=480, data_rate="20MHz", +) + +spectra_e6_7p3.extend( + "Seeed-reTerminal-E1002", cs_pin=10, dc_pin=11, reset_pin=12, From c75abfb8949ede2182765abcdffab807565fbd05 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 19 Nov 2025 14:17:03 -0500 Subject: [PATCH 525/526] Bump version to 2025.11.0b5 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index c7b2187964..56373c5f69 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.11.0b4 +PROJECT_NUMBER = 2025.11.0b5 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index 7a3a79f270..ddd36f69d7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.11.0b4" +__version__ = "2025.11.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 1e9c7d3c6dbc8e9e4da1367aaacf70c73dc3712b Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 19 Nov 2025 16:02:52 -0500 Subject: [PATCH 526/526] Bump version to 2025.11.0 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index 56373c5f69..1448fd010d 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.11.0b5 +PROJECT_NUMBER = 2025.11.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index ddd36f69d7..3505ad169b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.11.0b5" +__version__ = "2025.11.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = (