Commit Graph

2404 Commits

Author SHA1 Message Date
J. Nick Koston bcac422e9e [cli] Address self-review on --prebuilt-dir
- CORE.firmware_bin priority is now platform-aware: RP2040 + libretiny
  prefer firmware.uf2 over firmware.bin (picotool / ltchiptool need the
  UF2 header for address/family info); ESP* prefer firmware.bin. The
  prior code returned .bin first for all platforms, which would have
  silently flashed the wrong artifact on RP2040 if a hand-staged dir
  shipped both files. New tests guard both directions.

- _rp2040_serial_reset_to_bootsel: check picotool exists *before*
  triggering the 1200bps touch. If picotool is missing, the touch
  would have left the device stranded in BOOTSEL with nothing able to
  flash it; with this order the device stays on the old firmware and
  can be retried.

- upload_using_ltchiptool: error message now mentions both firmware.uf2
  and firmware.bin since CORE.firmware_bin resolves either.

- prepare_platform_for_upload: return type tightened to int (capture_stdout
  is hardcoded False; assert the run helper returns int so a future caller
  that flips capture_stdout fails loudly instead of silently treating a
  string as success). Caller in __main__.py is now a one-liner.

- _load_idedata: narrative comment in the prebuilt branch shortened.
  _resolve_prebuilt_idedata_paths docstring now notes the
  POSIX-absolute-on-Windows quirk ("/foo/bar" is rooted but not absolute
  on win32; hand-staged dirs need OS-appropriate absolute paths).

- New defensive-coverage tests: _resolve_prebuilt_idedata_paths with
  missing prog_path, no extra section, empty flash_images list.

Issue: esphome/device-builder#572
Issue: esphome/device-builder#570
2026-05-11 13:32:36 -05:00
J. Nick Koston c870ad328e [cli] Fix Windows pytest: use platform-absolute path in --prebuilt-dir test
The 'absolute paths pass through unchanged' test for _load_idedata
hardcoded a POSIX-style absolute path ('/somewhere/else/bootloader.bin').
On Windows, Path.is_absolute() returns False for that shape -- absolute
paths there require a drive letter -- so _resolve_prebuilt_idedata_paths
classified it as relative and prepended CORE.prebuilt_dir, breaking the
test's assertion.

Replace the hardcoded POSIX path with a tmp_path-rooted one so it's
platform-absolute on every runner. No production code change; this is a
test-only fix surfaced by the windows-latest pytest matrix on PR #16348.

Note: the macOS 3.14 failure on the same CI run is a flaky timing test
(tests/dashboard/test_web_server.py::test_dashboard_subscriber_entries_update_interval,
50ms sleep expecting 2+ iterations at 10ms) unrelated to this PR.

Issue: esphome/device-builder#572
2026-05-11 11:09:36 -05:00
J. Nick Koston 1ba8b838da [cli] Address Copilot review on --prebuilt-dir
- get_ltchiptool_path: use 'Scripts/' on Windows, 'bin/' elsewhere when
  falling back to PlatformIO's libretiny penv. CPython venvs put scripts
  under Scripts/ on win32 and bin/ on POSIX; the prior hardcoded 'bin'
  would never have found ltchiptool on Windows.
- _load_idedata: wrap json.loads on the prebuilt idedata.json in a
  try/except and re-raise as EsphomeError with a one-line diagnostic so
  the failure mode is a clean error instead of an unhandled
  JSONDecodeError stack trace. Update the surrounding comment to match
  the new behavior.
- CORE.prebuilt_dir docstring: drop the dead 'docs/architecture/...'
  pointer (no docs/ tree in this repo); point at esphome-docs#6600 and
  device-builder#572 instead.

New regression test:
- test_load_idedata_prebuilt_malformed_json_raises_esphomeerror

Updated test:
- test_get_ltchiptool_path_pio_penv now uses Scripts/ on win32 to match
  the new platform-aware lookup.

Issue: esphome/device-builder#572
Issue: esphome/device-builder#570
2026-05-11 10:58:17 -05:00
J. Nick Koston 956c2a9780 Merge remote-tracking branch 'upstream/dev' into core-prebuilt-dir-upload
# Conflicts:
#	esphome/__main__.py
2026-05-11 10:55:31 -05:00
J. Nick Koston a52ca4f80a [ota] Implement host platform OTA backend with re-exec for integration testing (#16304) 2026-05-11 10:51:08 -05:00
luar123 4ac7bc4606 [zigbee] Add sensor support on esp32 (#16026) 2026-05-11 10:51:47 -04:00
tomaszduda23 2edb7ca5c2 [nrf52] add message that west update is running (#16321) 2026-05-11 10:05:41 -04:00
J. Nick Koston 4d9d6e02e5 [core] Add stable esphome.upload_targets module for port classification (#16346) 2026-05-11 09:13:16 -04:00
J. Nick Koston b967adeb9d [wifi] Accept lowercase variant in variant_has_wifi (#16345) 2026-05-11 09:11:36 -04:00
Jonathan Swoboda e4d9786f00 [core] Move platformio files to subfolder (#16347) 2026-05-11 21:34:47 +12:00
J. Nick Koston 5cc7719ae4 [cli] Use pio run -t idedata to set up libretiny penv on cold hosts
The prior commit invoked 'pio pkg install' for the on-demand prep step,
which installs the libretiny platform package but does NOT recreate the
~/.platformio/penv/.libretiny/ virtualenv -- that's set up by libretiny's
ConfigurePythonVenv SCons step, which only fires during 'pio run'. So a
cold host (penv missing) still failed to find ltchiptool after the install
returned 'Already up-to-date'.

Wet test against bw15-device.yaml on a host with the libretiny penv
deleted:
  esphome upload bw15-device.yaml --device /dev/null --prebuilt-dir ...
  INFO ltchiptool not found on this host; installing the PlatformIO
       rtl87xx platform package ...
  Resolving bw15-device dependencies...
  Already up-to-date.
  ERROR ltchiptool not found. ...

Replace pkg install with 'pio run -t idedata'. The idedata target runs
SConscript without the actual compile target, so penv creation (libretiny)
and tool-package install (RP2040) happen as side effects of the SCons
configure phase -- but the build itself is skipped. Cost is a few seconds
of SCons configure, paid once per cold host.

Rename run_pkg_install -> prepare_platform_for_upload to match the new
semantics.

Issue: esphome/device-builder#572
Issue: esphome/device-builder#570
2026-05-11 00:30:06 -05:00
J. Nick Koston aab7177f07 [cli] Add write_cpp-failure test for --prebuilt-dir pkg-install prep
Symmetric with test_upload_program_prebuilt_dir_pkg_install_failure_aborts_upload:
covers the early-return path when write_cpp fails (codegen error, disk
full, validation regression) before pkg install runs. Closes the
coverage matrix for _ensure_platform_packages_for_prebuilt_upload's
two failure points.

Issue: esphome/device-builder#572
2026-05-10 23:58:16 -05:00
J. Nick Koston 7674170600 [cli] Auto-install PlatformIO platform when --prebuilt-dir needs its flash tool
Revert ltchiptool as a hard dependency so the vast majority of ESPHome
users (ESP32 / ESP8266 / nRF52 OTA) don't pay for libretiny tooling.
Instead, on-demand install just the PlatformIO platform package when a
--prebuilt-dir upload needs a flash tool that isn't on disk yet.

Mechanism: when upload_program receives --prebuilt-dir for a libretiny
or RP2040 target and the corresponding flash tool (ltchiptool / picotool)
isn't found by get_ltchiptool_path / _find_picotool, run the same prep
that 'esphome compile' would (write_cpp + write platformio.ini), then
invoke 'pio pkg install -e <env>' instead of 'pio run'. PlatformIO
downloads the platform without compiling, the flash tool ends up on disk,
and the upload dispatch picks it up on the next lookup.

After install, the tool path lookups (PATH + PIO penv for ltchiptool,
PIO packages dir for picotool) succeed and the upload helper takes over.
ESP32 / ESP8266 / nRF52 paths are unchanged (esptool is already bundled
in requirements.txt; smpclient too for nRF52 mcumgr).

Issue: esphome/device-builder#572
Issue: esphome/device-builder#570
2026-05-10 23:48:05 -05:00
J. Nick Koston f4607cb521 [cli] Accept basenames in prebuilt idedata.json
The dashboard's source-routed runner ships idedata.json with bare
basenames in prog_path and extra.flash_images[*].path (the receiver's
build-host absolute paths don't resolve on the offloader, and the
in-memory Web Serial consumer keys by basename). Without this change,
the dashboard would have to write a fresh idedata.json into the staging
tmpdir on every install just to flip basenames to absolute paths.

Resolve relative paths in the prebuilt idedata against CORE.prebuilt_dir
so both the dashboard's wire format and a hand-built directory with
absolute paths work. cc_path is left alone because it points at a
PlatformIO toolchain binary outside the prebuilt dir; the offloader's
own PIO install provides the matching binary.

Issue: esphome/device-builder#570
Issue: esphome/device-builder#572
2026-05-10 23:21:23 -05:00
J. Nick Koston 0adfd08270 [cli] Address review feedback on --prebuilt-dir
- upload_using_ltchiptool: rename unused config arg to _config and note
  the signature parity with upload_using_platformio/upload_using_picotool
  so dispatch stays symmetric.
- upload_program: explicit exit_code = 1 when _rp2040_serial_reset_to_bootsel
  fails, instead of relying on the function-level default.
- upload_using_picotool: distinct error message for the prebuilt-dir case
  that names both candidates (idedata ELF + prebuilt firmware) instead of
  just pointing at the missing ELF.
- _load_idedata: document the prebuilt-dir idedata.json contract more
  loudly (absolute paths under prebuilt_dir, no schema validation, dashboard
  owns the rewrites).
- --prebuilt-dir help text: drop the placeholder docs URL; describe the
  dashboard-internal intent inline so users who hit the flag in --help
  understand they don't want it.
- New test: upload_using_esptool with ESP-IDF toolchain + --prebuilt-dir
  uses <prebuilt-dir>/firmware.factory.bin at offset 0x0.

Issue: esphome/device-builder#572
2026-05-10 23:18:48 -05:00
J. Nick Koston d96ad02b9f [nrf52] Honor --prebuilt-dir for mcumgr/BLE OTA uploads
nRF52 has its own upload_program that runs before the default dispatch
in esphome.__main__. The mcumgr/BLE OTA path reads the MCUboot-signed
update image from CORE.relative_pioenvs_path(name, 'zephyr',
'app_update.bin'), which assumes a local Zephyr build tree.

Consult CORE.prebuilt_artifact_path('app_update.bin') first so the
dashboard's transparent BLE install on a Bluetooth proxy can flash a
prebuilt update image without compiling locally.

Serial uploads on non-MCUboot bootloaders still go through
adafruit-nrfutil via _upload_using_platformio and are out of scope here;
they need their own bypass to work with --prebuilt-dir (tracked
separately).

Issue: esphome/device-builder#572
2026-05-10 23:07:04 -05:00
J. Nick Koston a6a0a404ae [cli] Flash RP2040 serial prebuilt-dir uploads via 1200bps touch + picotool
Mirror the libretiny/ltchiptool shape for RP2040 serial when
--prebuilt-dir is set: open the user-supplied serial port at 1200 baud
(arduino-pico's USB CDC interprets the open/close as a request to reboot
into BOOTSEL), poll picotool until the BOOTSEL device shows up on the
USB bus, then dispatch to upload_using_picotool with the prebuilt .uf2.

This removes the last path that required --prebuilt-dir to contain a
platformio.ini + .pioenvs/<name>/ tree, so upload_using_platformio is
now only invoked when no prebuilt dir is set (i.e. the existing
compile+upload flow on a developer machine).

Issue: esphome/device-builder#572
2026-05-10 23:04:18 -05:00
J. Nick Koston b8336cddf2 [cli] Flash libretiny prebuilt-dir uploads via ltchiptool
The libretiny upload path on `upload_program` SERIAL dispatch re-invokes
PlatformIO (`pio run -t upload -t nobuild`), which needs a full build
tree and `platformio.ini`. That makes it incompatible with a dashboard
that only has prebuilt artifacts.

Bypass PlatformIO for the libretiny+SERIAL+--prebuilt-dir case by calling
`ltchiptool flash write -d <port> <firmware.uf2>` directly. The .uf2
encodes the chip family in its header so no extra config is needed.

ltchiptool ships with the libretiny PlatformIO platform under
~/.platformio/penv/.libretiny/bin/ltchiptool; `get_ltchiptool_path()`
prefers PATH first (pip install ltchiptool) and falls back to the
PlatformIO penv. Without --prebuilt-dir the existing PlatformIO path
remains in place, so this is purely additive.

Issue: esphome/device-builder#572
2026-05-10 22:52:17 -05:00
J. Nick Koston 255d4c6b65 [cli] Add esphome upload --prebuilt-dir <path>
Adds a --prebuilt-dir flag to esphome upload that points the per-platform
upload helpers at a directory of prebuilt artifacts shipped from a paired
build server, instead of re-deriving paths from the local build tree.

Covers every upload-dispatch shape:
- ESP32 / ESP8266 serial (esptool) reads firmware.bin + extras via the
  prebuilt idedata.json the dashboard ships next to the artifacts.
- ESP32 / ESP8266 OTA (native API + web_server) reads CORE.firmware_bin,
  CORE.partition_table_bin and CORE.bootloader_bin which now consult the
  prebuilt-dir first.
- RP2040 BOOTSEL (picotool) falls back from the idedata ELF (absent in
  the flat layout) to the prebuilt firmware.uf2.
- RP2040 serial / libretiny serial / OTA (PlatformIO upload -t nobuild)
  point platformio at the prebuilt build tree via CORE.build_path so the
  -t upload -t nobuild path finds platformio.ini and .pioenvs/<name>/.

Issue: esphome/device-builder#572
2026-05-10 22:28:19 -05:00
Diorcet Yann e9cc10fedc [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>
2026-05-11 02:12:07 +00:00
J. Nick Koston 66e2dcffc4 [cli] Tighten command_rename: scoped name rewrite, target-collision check (#16296) 2026-05-11 13:00:09 +12:00
J. Nick Koston 17080ddce6 [wifi][rp2040] Add stable wifi-capability helpers for device-builder (#16300) 2026-05-11 12:56:56 +12:00
J. Nick Koston 930d539969 [config_validation] Add a visibility UI-hint kwarg (#16267) 2026-05-11 12:07:15 +12:00
Clyde Stubbs 3c042e2e44 [lvgl] Ensure that on_value events fire on checked change (#16119) 2026-05-11 11:58:18 +12:00
Mat931 1365251365 [ota] Add bootloader update functionality to ota component (#16238)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-05-08 21:36:06 +00:00
John Boiles 88c2a1c096 [speaker] Add SPDIF output support (#8065)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
Co-authored-by: Kevin Ahrendt <kevin.ahrendt@openhomefoundation.org>
2026-05-08 14:43:55 -04:00
Mat931 3d8fffbea9 [htu31d][kamstrup_kmp][ms8607] Use CRC functions from helpers.h (#16313) 2026-05-08 09:17:14 -04:00
Jonathan Swoboda 696a654733 [clang-tidy] Concatenate nested namespaces (7/7: tests, platform-gated, enable check) (#16307) 2026-05-07 23:05:17 -04:00
Clyde Stubbs 9301f76482 [sensor] Add alternate calibration format for ntc (#15937) 2026-05-07 06:59:22 +10:00
Clyde Stubbs 004aa49131 [lvgl] Pass touch point to touch event lambdas (#16272) 2026-05-07 06:57:53 +10:00
Mat931 90693fb39a [core] Fix WiFi connection in safe mode (#16269)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-05-06 09:56:33 -05:00
J. Nick Koston 6e1a59da3e [packages] Make package !include vars visible to its substitutions block (#16274) 2026-05-06 09:53:14 -05:00
J. Nick Koston caaa1aefc7 [substitutions] Fix sibling references inside dict-valued substitutions (#16273) 2026-05-06 08:41:17 -05:00
J. Nick Koston ff0c5f575e [bundle] Include secrets.yaml when !secret keys are quoted (#16271) 2026-05-06 07:32:35 -05:00
J. Nick Koston f06ad8c436 [http_request] Add regression test for light action inside on_response (#16270) 2026-05-06 07:32:19 -05:00
J. Nick Koston 85f33978e7 [core] Skip external component update on esphome clean (#16268) 2026-05-06 07:23:35 -04:00
Clyde Stubbs 79786f1cc7 [lvgl] Allow line points as percentages (#16209) 2026-05-06 07:22:43 -04:00
Kevin Ahrendt febf8815c7 [audio_file][speaker] Eliminate code duplication for files built into firmware (#16266) 2026-05-06 05:59:51 -04:00
J. Nick Koston 4404dd68ba [cover] Fix ControlAction / CoverPublishAction trigger args with reference types (#16227) 2026-05-05 18:27:18 -05:00
J. Nick Koston 67491c3194 [packages] Add resolve_packages single-call seam (#16235) 2026-05-05 18:26:52 -05:00
J. Nick Koston 39b2b901f7 [core] Replace scheduler pool vector with unbounded intrusive freelist (#16172) 2026-05-05 18:26:19 -05:00
J. Nick Koston f30ad588ea [cli] Add --ota-platform flag to pick web_server or native API OTA (#16207) 2026-05-05 18:25:53 -05:00
Jesse Hills 7c2a63bf82 [api] Use safe_print for log output and fix safe_print bytes-repr fallback (#16160) 2026-05-05 08:12:20 +12:00
Diorcet Yann 690a197346 [main] Move stacktrace handling out of platformio_api and FlashImage into platform components/util (#16186) 2026-05-04 15:07:31 -04:00
J. Nick Koston 53c4e6f386 [tests] Drop duplicate on_boot_restore_from in host_mode_climate_basic_state (#16228) 2026-05-03 20:12:51 -05:00
J. Nick Koston 1d63158480 [zephyr] Add nRF52 component tests so CI runs on zephyr-only changes (#16188) 2026-05-03 20:05:56 -05:00
J. Nick Koston 013dee44eb [binary_sensor] Drop Component from AutorepeatFilter, use self-keyed scheduler (#16191) 2026-05-03 20:05:27 -05:00
J. Nick Koston 9ddb828da3 [api] Don't tear down log connection on stack-trace decode failure (#16196) 2026-05-03 20:05:13 -05:00
J. Nick Koston 120d1e51fb [tests] Fix flaky host_mode_climate_basic_state integration test (#16192) 2026-05-03 20:04:34 -05:00
J. Nick Koston 72a75f2d3f [cover] Fold ControlAction/CoverPublishAction fields into stateless lambdas (#16046) 2026-05-03 20:02:07 -05:00