[core] Native idf full support (#14678)

Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <swoboda1337@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: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Diorcet Yann
2026-05-11 02:12:07 +00:00
committed by GitHub
co-authored by Jonathan Swoboda Jonathan Swoboda pre-commit-ci-lite[bot] Jesse Hills
parent 66e2dcffc4
commit e9cc10fedc
27 changed files with 3537 additions and 364 deletions
+86
View File
@@ -783,6 +783,91 @@ jobs:
# Run compilation with grouping and isolation
python3 script/test_build_components.py -e compile -c "$components_csv" -f --isolate "$directly_changed_csv"
test-native-idf:
name: Test components with native ESP-IDF
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: github.event_name == 'pull_request'
env:
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
TEST_COMPONENTS: esp32,api,heatpumpir,bme280_i2c,bh1750,aht10,esp32_ble,esp32_ble_beacon,esp32_ble_client,esp32_ble_server,esp32_ble_tracker,ble_client,ble_presence,ble_rssi,ble_scanner
steps:
- name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESPHome
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-${{ needs.common.outputs.cache-key }}
- name: Run native ESP-IDF compile test
run: |
. venv/bin/activate
# 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/esphome-idf
sudo chown $USER:$USER /mnt/esphome-idf
mkdir -p ~/.esphome-idf
sudo mount --bind /mnt/esphome-idf ~/.esphome-idf
# 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
echo "Testing components: $TEST_COMPONENTS"
echo ""
# Show disk space before validation (after bind mounts setup)
echo "Disk space before config validation:"
df -h
echo ""
# Run config validation (auto-grouped by test_build_components.py)
python3 script/test_build_components.py -e config -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain esp-idf
echo ""
echo "Config validation passed! Starting compilation..."
echo ""
# Show disk space before compilation
echo "Disk space before compilation:"
df -h
echo ""
# Run compilation (auto-grouped by test_build_components.py)
python3 script/test_build_components.py -e compile -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain esp-idf
- name: Save ESPHome cache
if: github.ref == 'refs/heads/dev'
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-${{ needs.common.outputs.cache-key }}
pre-commit-ci-lite:
name: pre-commit.ci lite
runs-on: ubuntu-latest
@@ -1114,6 +1199,7 @@ jobs:
- determine-jobs
- device-builder
- test-build-components-split
- test-native-idf
- pre-commit-ci-lite
- memory-impact-target-branch
- memory-impact-pr-branch
+80 -44
View File
File diff suppressed because it is too large Load Diff
+23 -1
View File
@@ -6,6 +6,7 @@ from pathlib import Path
from esphome.components.esp32 import get_esp32_variant
from esphome.core import CORE
from esphome.helpers import mkdir_p, write_file_if_changed
from esphome.writer import update_storage_json
def get_available_components() -> list[str] | None:
@@ -54,7 +55,7 @@ def get_project_cmakelists() -> str:
idf_target = variant.lower().replace("-", "")
# Extract compile definitions from build flags (-DXXX -> XXX)
compile_defs = [flag for flag in CORE.build_flags if flag.startswith("-D")]
compile_defs = [flag for flag in sorted(CORE.build_flags) if flag.startswith("-D")]
extra_compile_options = "\n".join(
f'idf_build_set_property(COMPILE_OPTIONS "{compile_def}" APPEND)'
for compile_def in compile_defs
@@ -64,6 +65,22 @@ def get_project_cmakelists() -> str:
# Auto-generated by ESPHome
cmake_minimum_required(VERSION 3.16)
# On Windows, Ninja can fail with:
# "CreateProcess: The parameter is incorrect (is the command line too long?)"
# when compiler/linker command lines exceed the OS length limit.
#
# The following settings force CMake/Ninja to use *response files* (@file.rsp)
# to pass long lists of includes, objects, and other arguments indirectly,
# avoiding command-line length limits and fixing the build failure.
#
# This is especially useful for large ESP-IDF / ESPHome projects with many
# source files or include directories.
set(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1)
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1)
set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1)
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1)
set(CMAKE_NINJA_FORCE_RESPONSE_FILE 1)
set(IDF_TARGET {idf_target})
set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
@@ -124,6 +141,11 @@ target_link_options(${{COMPONENT_LIB}} PUBLIC
def write_project(minimal: bool = False) -> None:
"""Write ESP-IDF project files."""
# Refresh <data_dir>/storage/<name>.yaml.json so the dashboard's
# /info and /downloads endpoints can locate the build (they 404
# otherwise). This mirrors the PlatformIO build-gen path's call
# in build_gen/platformio.py:write_ini().
update_storage_json()
mkdir_p(CORE.build_path)
mkdir_p(CORE.relative_src_path())
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -36,7 +36,6 @@ from esphome.const import (
CONF_VALUE,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
KEY_NATIVE_IDF,
Platform,
PlatformFramework,
)
@@ -705,7 +704,7 @@ def _filter_source_files() -> list[str]:
# and pioarduino doesn't have it builtin (IDF 5.4.2 to 5.x)
if eth_type != "JL1101":
excluded.append("esp_eth_phy_jl1101.c")
elif CORE.is_esp32 and not CORE.data.get(KEY_NATIVE_IDF, False):
elif CORE.is_esp32 and not CORE.using_toolchain_esp_idf:
from esphome.components.esp32 import idf_version
# pioarduino has JL1101 builtin on IDF 5.4.2-5.x; exclude custom driver
+1
View File
@@ -244,6 +244,7 @@ async def to_code(config):
# disable built in rgb support as it uses the new RMT drivers and will
# conflict with NeoPixelBus which uses the legacy drivers
cg.add_build_flag("-DESP32_ARDUINO_NO_RGB_BUILTIN")
cg.add_library("SPI", None)
cg.add_library("makuna/NeoPixelBus", "2.8.0")
else:
cg.add_library("makuna/NeoPixelBus", "2.7.3")
+1 -1
View File
@@ -142,7 +142,7 @@ async def _smpmgr_upload_connected(
with open(firmware, "rb") as file:
image = file.read()
upload_size = len(image)
progress = ProgressBar()
progress = ProgressBar("Uploading")
progress.update(0)
try:
async for offset in smp_client.upload(image):
+8 -1
View File
@@ -15,6 +15,13 @@ VALID_SUBSTITUTIONS_CHARACTERS = (
ARGUMENT_HELP_DEVICE = "Manually specify the serial port/address to use, for example /dev/ttyUSB0. Can be specified multiple times for fallback addresses. Use 'OTA' for resolving from MQTT, DNS or mDNS and avoiding the interactive prompt."
class Toolchain(StrEnum):
"""Toolchain identifiers for ESPHome."""
PLATFORMIO = "platformio"
ESP_IDF = "esp-idf"
class Platform(StrEnum):
"""Platform identifiers for ESPHome."""
@@ -1036,6 +1043,7 @@ CONF_TO = "to"
CONF_TO_NTC_RESISTANCE = "to_ntc_resistance"
CONF_TO_NTC_TEMPERATURE = "to_ntc_temperature"
CONF_TOLERANCE = "tolerance"
CONF_TOOLCHAIN = "toolchain"
CONF_TOPIC = "topic"
CONF_TOPIC_PREFIX = "topic_prefix"
CONF_TOTAL = "total"
@@ -1393,7 +1401,6 @@ KEY_FRAMEWORK_VERSION = "framework_version"
KEY_NAME = "name"
KEY_VARIANT = "variant"
KEY_PAST_SAFE_MODE = "past_safe_mode"
KEY_NATIVE_IDF = "native_idf"
# Entity categories
ENTITY_CATEGORY_NONE = ""
+19 -6
View File
@@ -17,7 +17,6 @@ from esphome.const import (
CONF_WEB_SERVER,
CONF_WIFI,
KEY_CORE,
KEY_NATIVE_IDF,
KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM,
PLATFORM_BK72XX,
@@ -28,6 +27,7 @@ from esphome.const import (
PLATFORM_NRF52,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
Toolchain,
)
# pylint: disable=unused-import
@@ -618,6 +618,10 @@ class EsphomeCore:
# When True, skip network freshness checks for cached external files
# (e.g. for `esphome logs`, where remote downloads aren't needed)
self.skip_external_update: bool = False
# Toolchain used for building the configuration. None until resolved
# by CLI (--toolchain) or by `esphome.toolchain:` in YAML during
# preload_core_config; defaults to PLATFORMIO if neither sets it.
self.toolchain: Toolchain | None = None
def reset(self):
from esphome.pins import PIN_SCHEMA_REGISTRY
@@ -648,6 +652,7 @@ class EsphomeCore:
self.address_cache = None
self._config_hash = None
self.skip_external_update = False
self.toolchain = None
PIN_SCHEMA_REGISTRY.reset()
@contextmanager
@@ -772,8 +777,8 @@ class EsphomeCore:
@property
def firmware_bin(self) -> Path:
# Check if using native ESP-IDF build (--native-idf)
if self.data.get(KEY_NATIVE_IDF, False):
# Check if using ESP-IDF toolchain
if self.using_toolchain_esp_idf:
return self.relative_build_path("build", f"{self.name}.bin")
if self.is_libretiny:
return self.relative_pioenvs_path(self.name, "firmware.uf2")
@@ -781,10 +786,10 @@ class EsphomeCore:
@property
def partition_table_bin(self) -> Path:
# Native ESP-IDF (--native-idf): the partition table image is emitted under
# Native ESP-IDF (--toolchain esp-idf): the partition table image is emitted under
# build/partition_table/partition-table.bin alongside firmware.bin. PlatformIO writes the
# equivalent file as partitions.bin in the env-specific .pioenvs directory.
if self.data.get(KEY_NATIVE_IDF):
if self.using_toolchain_esp_idf:
return self.relative_build_path(
"build", "partition_table", "partition-table.bin"
)
@@ -792,7 +797,7 @@ class EsphomeCore:
@property
def bootloader_bin(self) -> Path:
if self.data.get(KEY_NATIVE_IDF):
if self.using_toolchain_esp_idf:
return self.relative_build_path("build", "bootloader", "bootloader.bin")
return self.relative_pioenvs_path(self.name, "bootloader.bin")
@@ -853,6 +858,14 @@ class EsphomeCore:
)
return self.target_framework == "esp-idf"
@property
def using_toolchain_esp_idf(self):
return self.toolchain == Toolchain.ESP_IDF
@property
def using_toolchain_platformio(self):
return self.toolchain == Toolchain.PLATFORMIO
@property
def using_zephyr(self):
return self.target_framework == "zephyr"
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
"""Print JSON ``{paths_to_export, export_vars}`` for ESP-IDF tools.
Run via ``python <this file> <idf_framework_root>``. PYTHONPATH must include
``<idf_framework_root>/tools`` so ``idf_tools`` is importable. Exits with
status 1 and prints ``Missing ESP-IDF tools: ...`` on stderr if any tool is
not installed.
"""
# pylint: disable=import-error # idf_tools is on PYTHONPATH at runtime only
import json
import os
import sys
from types import SimpleNamespace
from idf_tools import (
TOOLS_FILE,
IDFEnv,
IDFTool,
filter_tools_info,
g,
load_tools_info,
process_tool,
)
g.idf_path = sys.argv[1]
g.idf_tools_path = os.environ.get("IDF_TOOLS_PATH")
g.tools_json = os.path.join(g.idf_path, TOOLS_FILE)
tools_info = filter_tools_info(IDFEnv.get_idf_env(), load_tools_info())
args = SimpleNamespace(prefer_system=False)
paths_to_export: list[str] = []
export_vars: dict[str, str] = {}
missing_tools: list[str] = []
for name, tool in tools_info.items():
if tool.get_install_type() == IDFTool.INSTALL_NEVER:
continue
tool_paths, tool_vars, found = process_tool(
tool, name, args, "install_cmd", "prefer_system_hint"
)
if not found:
missing_tools.append(name)
paths_to_export += tool_paths
export_vars |= tool_vars
if missing_tools:
print("Missing ESP-IDF tools: " + ", ".join(missing_tools), file=sys.stderr)
raise SystemExit(1)
print(json.dumps({"paths_to_export": paths_to_export, "export_vars": export_vars}))
+14
View File
@@ -0,0 +1,14 @@
"""Print the ESP-IDF version of a given framework root.
Run via ``python <this file> <idf_framework_root>``. PYTHONPATH must include
``<idf_framework_root>/tools`` so ``idf_tools`` is importable.
"""
# pylint: disable=import-error # idf_tools is on PYTHONPATH at runtime only
import sys
from idf_tools import g, get_idf_version
g.idf_path = sys.argv[1]
print(get_idf_version())
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -441,7 +441,7 @@ def perform_ota(
start_time = time.perf_counter()
offset = 0
progress = ProgressBar()
progress = ProgressBar("Uploading")
while True:
chunk = upload_contents[offset : offset + UPLOAD_BLOCK_SIZE]
if not chunk:
+10 -3
View File
@@ -11,7 +11,7 @@ import shutil
import stat
import sys
import tempfile
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, TextIO
from urllib.parse import urlparse
from esphome.const import __version__ as ESPHOME_VERSION
@@ -617,10 +617,15 @@ def sanitize(value):
class ProgressBar:
"""A simple terminal progress bar for upload operations."""
def __init__(self) -> None:
def __init__(self, header: str, stream: TextIO | None = None) -> None:
self.header = header
self.stream = stream or sys.stderr
self.last_progress: int | None = None
self.enabled = hasattr(self.stream, "isatty") and self.stream.isatty()
def update(self, progress: float) -> None:
if not self.enabled:
return
bar_length = 60
status = ""
if progress >= 1:
@@ -631,11 +636,13 @@ class ProgressBar:
return
self.last_progress = new_progress
block = int(round(bar_length * progress))
text = f"\rUploading: [{'=' * block + ' ' * (bar_length - block)}] {new_progress}% {status}"
text = f"\r{self.header}: [{'=' * block + ' ' * (bar_length - block)}] {new_progress}% {status}"
sys.stderr.write(text)
sys.stderr.flush()
def done(self) -> None:
if not self.enabled:
return
sys.stderr.write("\n")
sys.stderr.flush()
+1 -1
View File
@@ -60,7 +60,7 @@ class _MultipartStreamer:
self._idx = 0
self._total = len(prefix) + file_size + len(suffix)
self._sent = 0
self.progress = ProgressBar()
self.progress = ProgressBar("Uploading")
def __len__(self) -> int:
return self._total
+1 -1
View File
@@ -562,7 +562,7 @@ def lint_constants_usage():
# Maximum allowed CONF_ constants in esphome/const.py.
# This file is frozen — new constants go in esphome/components/const/__init__.py.
# Decrease this number when constants are moved out of const.py.
CONST_PY_MAX_CONF = 1011
CONST_PY_MAX_CONF = 1012
@lint_content_check(include=["esphome/const.py"])
+41 -10
View File
@@ -175,7 +175,7 @@ def group_components_by_platform(
}
def format_github_summary(test_results: list[TestResult]) -> str:
def format_github_summary(test_results: list[TestResult], toolchain=None) -> str:
"""Format test results as GitHub Actions job summary markdown.
Args:
@@ -225,11 +225,12 @@ def format_github_summary(test_results: list[TestResult]) -> str:
lines.append("```bash\n")
# Generate one command per platform and test type
extra_arguments = f" --toolchain {toolchain}" if toolchain else ""
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"
f"script/test_build_components.py -c {components_csv} -t {platform} -e {test_type}{extra_arguments}\n"
)
lines.append("```\n")
@@ -274,13 +275,15 @@ def format_github_summary(test_results: list[TestResult]) -> str:
return "".join(lines)
def write_github_summary(test_results: list[TestResult]) -> None:
def write_github_summary(
test_results: list[TestResult], toolchain: str | None = None
) -> 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)
summary_content = format_github_summary(test_results, toolchain)
with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as f:
f.write(summary_content)
@@ -308,6 +311,7 @@ def run_esphome_test(
esphome_command: str,
continue_on_fail: bool,
use_testing_mode: bool = False,
toolchain: str | None = None,
) -> TestResult:
"""Run esphome test for a single component.
@@ -367,8 +371,14 @@ def run_esphome_test(
]
)
# Add command and config file
cmd.extend([esphome_command, str(output_file)])
if toolchain:
cmd.extend(["--toolchain", toolchain])
# Add command
cmd.append(esphome_command)
# Add config file
cmd.append(str(output_file))
# Build command string for display/logging
cmd_str = " ".join(cmd)
@@ -432,6 +442,7 @@ def run_grouped_test(
tests_dir: Path,
esphome_command: str,
continue_on_fail: bool,
toolchain: str | None = None,
) -> TestResult:
"""Run esphome test for a group of components with shared bus configs.
@@ -510,10 +521,16 @@ def run_grouped_test(
"-s",
"target_platform",
platform,
esphome_command,
str(output_file),
]
if toolchain:
cmd.extend(["--toolchain", toolchain])
# Add command
cmd.append(esphome_command)
cmd.append(str(output_file))
# Build command string for display/logging
cmd_str = " ".join(cmd)
@@ -576,6 +593,7 @@ def run_grouped_component_tests(
esphome_command: str,
continue_on_fail: bool,
additional_isolated: set[str] | None = None,
toolchain: str | None = None,
) -> tuple[set[tuple[str, str]], list[TestResult]]:
"""Run grouped component tests.
@@ -879,6 +897,7 @@ def run_grouped_component_tests(
tests_dir=tests_dir,
esphome_command=esphome_command,
continue_on_fail=continue_on_fail,
toolchain=toolchain,
)
# Mark all components as tested
@@ -902,6 +921,7 @@ def run_individual_component_test(
continue_on_fail: bool,
tested_components: set[tuple[str, str]],
test_results: list[TestResult],
toolchain: str | None = None,
) -> None:
"""Run an individual component test if not already tested in a group.
@@ -930,6 +950,7 @@ def run_individual_component_test(
build_dir=build_dir,
esphome_command=esphome_command,
continue_on_fail=continue_on_fail,
toolchain=toolchain,
)
test_results.append(test_result)
@@ -942,6 +963,7 @@ def test_components(
enable_grouping: bool = True,
isolated_components: set[str] | None = None,
base_only: bool = False,
toolchain: str | None = None,
) -> int:
"""Test components with optional intelligent grouping.
@@ -1018,6 +1040,7 @@ def test_components(
esphome_command=esphome_command,
continue_on_fail=continue_on_fail,
additional_isolated=isolated_components,
toolchain=toolchain,
)
test_results.extend(grouped_results)
@@ -1046,6 +1069,7 @@ def test_components(
continue_on_fail=continue_on_fail,
tested_components=tested_components,
test_results=test_results,
toolchain=toolchain,
)
else:
# Platform-specific test
@@ -1078,6 +1102,7 @@ def test_components(
continue_on_fail=continue_on_fail,
tested_components=tested_components,
test_results=test_results,
toolchain=toolchain,
)
# Separate results into passed and failed
@@ -1098,17 +1123,18 @@ def test_components(
print("\n" + "=" * 80)
print("Commands to reproduce failures (copy-paste to reproduce locally):")
print("=" * 80)
extra_arguments = f" --toolchain {toolchain}" if toolchain else ""
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}"
f"script/test_build_components.py -c {components_csv} -t {platform} -e {test_type}{extra_arguments}"
)
print()
# Write GitHub Actions job summary if in CI
if os.environ.get("GITHUB_STEP_SUMMARY"):
write_github_summary(test_results)
write_github_summary(test_results, toolchain=toolchain)
if failed_results:
return 1
@@ -1161,6 +1187,10 @@ def main() -> int:
action="store_true",
help="Only test base test files (test.*.yaml), not variant files (test-*.yaml)",
)
parser.add_argument(
"--toolchain",
help="Select toolchain for compiling.",
)
args = parser.parse_args()
@@ -1180,6 +1210,7 @@ def main() -> int:
enable_grouping=not args.no_grouping,
isolated_components=isolated_components,
base_only=args.base_only,
toolchain=args.toolchain,
)
+2 -2
View File
@@ -16,8 +16,8 @@ from esphome.const import (
CONF_ESPHOME,
CONF_IGNORE_PIN_VALIDATION_ERROR,
CONF_NUMBER,
KEY_NATIVE_IDF,
PlatformFramework,
Toolchain,
)
from esphome.core import CORE
from tests.component_tests.types import SetCoreConfigCallable
@@ -266,7 +266,7 @@ def test_native_idf_enables_reproducible_build(
CORE.config_path = component_config_path("reproducible_build.yaml")
CORE.config = read_config({})
CORE.data[KEY_NATIVE_IDF] = True
CORE.toolchain = Toolchain.ESP_IDF
generate_cpp_contents(CORE.config)
sdkconfig = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS]
+2 -2
View File
@@ -855,7 +855,7 @@ class TestEsphomeCore:
def test_bootloader_bin__native_idf(self, target):
"""Native ESP-IDF builds emit the bootloader under build/bootloader/bootloader.bin."""
target.data[const.KEY_NATIVE_IDF] = True
target.toolchain = const.Toolchain.ESP_IDF
assert target.bootloader_bin == Path(
"foo/build/build/bootloader/bootloader.bin"
@@ -864,7 +864,7 @@ class TestEsphomeCore:
def test_bootloader_bin__platformio(self, target):
"""For PlatformIO builds bootloader.bin lives in the env-specific .pioenvs directory."""
target.name = "test-device"
target.data[const.KEY_NATIVE_IDF] = False
target.toolchain = const.Toolchain.PLATFORMIO
assert target.bootloader_bin == Path(
"foo/build/.pioenvs/test-device/bootloader.bin"
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -604,7 +604,8 @@ def test_run_ota_wrapper(mock_run_ota_impl: Mock) -> None:
def test_progress_bar(capsys: CaptureFixture[str]) -> None:
"""Test ProgressBar functionality."""
progress = espota2.ProgressBar()
progress = espota2.ProgressBar("Uploading")
progress.enabled = True # Fake TTY
# Test initial update
progress.update(0.0)
+2
View File
@@ -88,6 +88,7 @@ from esphome.const import (
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_RP2040,
Toolchain,
)
from esphome.core import CORE, EsphomeError
from esphome.espota2 import (
@@ -148,6 +149,7 @@ def setup_core(
config[CONF_WIFI] = {CONF_USE_ADDRESS: address}
CORE.config = config
CORE.toolchain = Toolchain.PLATFORMIO
if platform is not None:
CORE.data[KEY_CORE] = {}