Commit Graph
27612 Commits
Author SHA1 Message Date
J. Nick Koston 629a6b4a03 Merge remote-tracking branch 'upstream/esp8266-pin-esptoolpy-5x' into integration 2026-04-18 08:12:51 -05:00
J. Nick Koston fe06684242 [esp8266] Clarify 0.0.1 is pioarduino registry tag, not tool version 2026-04-18 08:12:28 -05:00
J. Nick Koston addda4c48f [esp8266] Mirror tool-esptoolpy pin in dev platformio.ini
Keep local pio run -e esp8266-arduino / esp32-arduino invocations
(clang-tidy, IDE) in sync with the ESPHome codegen override so they
share the same installed tool-esptoolpy package.
2026-04-18 08:10:29 -05:00
J. Nick Koston 63606dd4bb [esp8266] Pin tool-esptoolpy to 5.x to match ESP32 platform
The platform-espressif8266 4.2.1 platform manifest pins tool-esptoolpy
to ~1.30000.0, while the pioarduino ESP32 platform uses 5.2.0. Because
PlatformIO stores tool-esptoolpy in a single shared package directory,
switching between esp8266 and esp32 builds forces PlatformIO to
uninstall and reinstall the package every time.

Override the pin on the ESP8266 side to the same pioarduino 5.2.0 build
so both platforms resolve to the same installed package. ESPHome already
invokes its own Python esptool 5.2.0 for uploads, and modern esptool
still accepts the legacy underscore CLI flags used by the ESP8266
platform's builder scripts.
2026-04-18 08:09:03 -05:00
J. Nick Koston ccb53e34ca [core] Default PollingComponent() to 1ms when codegen is bypassed (#15831) 2026-04-18 09:04:51 -04:00
dependabot[bot] ec9d59f3dc Bump aioesphomeapi from 44.16.0 to 44.16.1 (#15836)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-18 12:32:36 +00:00
J. Nick Koston 6abb9fd815 Merge remote-tracking branch 'upstream/decouple_scheduler_loop_cadence' into integration
# Conflicts:
#	esphome/core/application.h
2026-04-18 07:12:36 -05:00
J. Nick Koston 4e05d39f4b [core] use post-scheduler timestamp for gate check
scheduler_tick_ now returns the scheduler's advanced timestamp (free via
PR #15830's Scheduler::call return). Previously we still used the
pre-scheduler millis() for `elapsed = now - last_loop_`, which
underestimated elapsed time by whatever the scheduler dispatch took.

Adopt the returned value as `now` so the gate check, WDT feed, runtime
stats, and sleep computation all see consistent post-scheduler time.
Drops the obsolete "we deliberately reuse pre-scheduler now" comment —
that rationale was predicated on saving a millis() call, which no longer
applies.
2026-04-18 07:09:52 -05:00
J. Nick Koston 309c9463aa Merge remote-tracking branch 'upstream/dev' into decouple_scheduler_loop_cadence
# Conflicts:
#	esphome/core/application.h
2026-04-18 07:06:28 -05:00
J. Nick Koston df72aa26c0 [core] Feed WDT unconditionally in main loop to fix empty-config panic (#15830) 2026-04-18 11:58:54 +00:00
J. Nick Koston e347d65b64 Merge remote-tracking branch 'upstream/sensor/throttle-with-priority-nan-specialize' into integration 2026-04-18 06:32:18 -05:00
J. Nick Koston c122fc8c78 [ci] Apply conflict split before CI batch distribution
Previously split_conflicting_groups ran only inside test_build_components.
The CI-side batcher (split_components_for_ci.py / determine-jobs.py)
still saw the pre-split group, so its weight budgeting assumed one build
where runtime produces two. Apply the split where the groups are formed
so batch distribution reflects actual build counts.
2026-04-18 06:30:03 -05:00
J. Nick Koston 897dd7cd5e [ci] Move conflict-aware grouping helpers into script/helpers.py with tests 2026-04-18 06:26:03 -05:00
J. Nick Koston 698dbbdcb5 [ci] Simplify conflict walk 2026-04-18 06:21:43 -05:00
J. Nick Koston 875de948e8 [ci] Scope conflict parsing to batch components only 2026-04-18 06:19:35 -05:00
J. Nick Koston 9e714a3942 [ci] Clean up AST-target dispatch in _component_conflicts 2026-04-18 06:18:02 -05:00
J. Nick Koston fe00a5445e [ci] Honor CONFLICTS_WITH when grouping component tests
The test-grouping pipeline merged components that share a bus signature
into a single config without checking CONFLICTS_WITH. When
bme68x_bsec2 declared CONFLICTS_WITH=["bme680_bsec"] (and vice versa),
the merged YAML containing both failed validation.

Statically parse AUTO_LOAD and CONFLICTS_WITH from every component
__init__.py, propagate conflicts through AUTO_LOAD, and split any
group that contains a conflicting pair into separate builds.
2026-04-18 06:15:40 -05:00
J. Nick Koston 64c4eb12bc Merge remote-tracking branch 'upstream/wdt-feed-empty-loop' into integration 2026-04-18 05:29:16 -05:00
J. Nick Koston 327f03fe4f [core] Feed WDT unconditionally in main loop
Fixes the task watchdog firing on configs with no looping components and
no scheduler work (e.g. a minimal esphome: + logger: config, or a device
where every looping component has called disable_loop()).

In 2026.4.0, scheduler.call() feeds the WDT per executed item and each
component feeds it after its loop() runs. When neither fires on a tick,
the main loop task sleeps in yield_with_select_() with nothing ever
reaching arch_feed_wdt(), so the task watchdog starves and panics.

Add one feed_wdt_with_time() call right after before_loop_tasks_() in
Application::loop(). Rate-limited inline fast path, nearly free when
the 3 ms floor has not elapsed.

To keep the timestamp argument monotonic with last_wdt_feed_ (advanced
by Scheduler::execute_item_() as items fire), Scheduler::call() now
returns its internal `now` (advanced via
`now = this->execute_item_(item, now);`), forwarded through
before_loop_tasks_(). No extra millis() call needed; when no items run
the returned value equals the input.
2026-04-18 05:23:22 -05:00
Clyde Stubbs d3691c7ca5 [lvgl] Fix crash with snow on rotated display (#15822) 2026-04-18 09:17:28 +10:00
J. Nick Koston 0a794625ea Merge branch 'dev' into sensor/throttle-with-priority-nan-specialize 2026-04-17 17:54:32 -05:00
J. Nick Koston 562ce541a0 [bme680_bsec] [bme68x_bsec2] Mark the two BSEC variants as mutually exclusive (#15826) 2026-04-17 17:54:24 -05:00
J. Nick Koston 6ebe1e92eb [ci] Scope local pylint pre-commit hook to esphome/ (#15818) 2026-04-17 17:54:12 -05:00
J. Nick Koston 147e2fe9cf Merge remote-tracking branch 'origin/ci-scope-pylint' into integration 2026-04-17 17:46:26 -05:00
J. Nick Koston b54b0c24fc Merge remote-tracking branch 'origin/bme-bsec/conflicts-with' into integration 2026-04-17 17:46:23 -05:00
J. Nick Koston 0acf1873e7 Merge remote-tracking branch 'origin/sensor/throttle-with-priority-nan-specialize' into integration 2026-04-17 17:46:20 -05:00
J. Nick Koston fa2a75acd0 Merge remote-tracking branch 'origin/core-chunked-setup' into integration 2026-04-17 17:46:08 -05:00
J. Nick Koston 998337e3b1 [bme680_bsec] [bme68x_bsec2] Mark the two BSEC variants as mutually exclusive
\`bme680_bsec\` pulls in the BSEC v1 library (single-instance API:
\`bsec_init\`, \`bsec_set_configuration\`, …) and \`bme68x_bsec2\` pulls in
the BSEC v2 library (multi-instance API: \`bsec_init_m\`,
\`bsec_set_configuration_m\`, …). The two libraries export different
symbol sets and can't coexist in one firmware image — linking them
together currently fails with undefined references to the \`*_m\`
symbols. This manifests in CI when the test-component grouping logic
merges \`bme680_bsec\` and \`bme68x_bsec2_i2c\` into a single build.

Declare \`CONFLICTS_WITH\` on both components so the config validator
rejects the combination at config time with a clear message instead of
leaving users to decode a linker failure.
2026-04-17 16:50:01 -05:00
J. Nick Koston e26ce59797 for progmem 2026-04-17 16:18:39 -05:00
J. Nick Koston efe7bb1eb9 [sensor] Guard NaN specialization against explicit empty value list
Preserves the prior behavior of `value: []` (behaves like plain throttle,
no NaN bypass) by requiring a non-empty list before treating the config as
NaN-only — `all([])` is vacuously true otherwise.
2026-04-17 15:52:04 -05:00
Clyde Stubbs 1bf455cfbb [runtime_image] Fix RGB order (#15813) 2026-04-18 06:42:45 +10:00
Clyde Stubbs 290e213cd0 [mipi_spi] Add Sunton ESP32-2424S012 (#15812) 2026-04-18 06:41:33 +10:00
J. Nick Koston a5cba91729 [sensor] Specialize throttle_with_priority NaN-only case
Short form `- throttle_with_priority: 1s` and explicit `value: nan`
previously generated a `ThrottleWithPriorityFilter<1>` with a lambda
returning NAN stored in a `TemplatableFn<float>` array. Detect this
common case at codegen time and emit a new `ThrottleWithPriorityNanFilter`
that inlines the `std::isnan()` check. Behavior is preserved: NaN
sensor readings still bypass the throttle.
2026-04-17 15:39:30 -05:00
J. Nick Koston 9fa6d224c2 [core] Tighten docstrings and inline comments 2026-04-17 15:35:53 -05:00
J. Nick Koston 91b238aa97 [core] Fix grammar in ComponentMarker docstring 2026-04-17 15:34:15 -05:00
J. Nick Koston 00f08ba6ed [core] Drop per-component begin/end labels from generated main.cpp
The labels were there to help humans scanning the generated main.cpp
find component boundaries, but they were:

- Unreliable: CORE.flush_tasks can interleave coroutines on each
  await, so a component's later statements can land in another
  component's begin/end block.
- Load-bearing for a pile of complexity: a tuple return from
  _wrap_in_iifes, a has_iife flag, a comment-only detector to
  suppress trailing end-markers for comment-only components, and
  a brittle `"[]()" in line` check that could false-positive on
  YAML dumps containing lambda syntax.
- Not actually needed — generated main.cpp is a build artifact
  rarely read by anyone, and cg.LineComment("name:") already puts
  the component name at the start of its block.

ComponentMarker stays as a pure chunking sentinel — it tells
cpp_main_section where component boundaries are (for grouping) but
produces no C++ output. _wrap_in_iifes returns a plain list again.
Added a regression test for the now-defused case of a comment
containing "[]()" that was previously flagged by review.
2026-04-17 15:19:48 -05:00
Jonathan Swoboda b1b0005574 [esp32] Downgrade unneeded ignore_pin_validation_error to a warning (#15811) 2026-04-17 16:14:54 -04:00
J. Nick Koston f82401a504 [core] Address Copilot review: robust brace depth, accurate docstrings
- Count { and } characters per line instead of matching whole-line
  tokens. Current codegen only emits scope braces as standalone lines
  (from cg.with_local_variable()), but the defensive change is robust
  against future codegen emitting inline control flow like
  `if (cond) {` or `} else {` on one line.
- Add a regression test covering those inline-brace patterns.
- Fix stale docstrings on ComponentMarker and cpp_main_section that
  still claimed "stack frame released on return" and described the
  IIFEs as "noinline". The IIFEs have no noinline attribute and rely
  on scope-based lifetime shortening rather than guaranteed frames.
2026-04-17 15:06:42 -05:00
J. Nick Koston 178f23a7aa [core] Use begin/end marker pairs around each component's IIFE
Rename the bracket markers from "// === X ===" (same on both sides)
to "// === begin X ===" and "// === end X ===" so the generated
main.cpp reads unambiguously when scanning by component. Comment-only
components still get a single "begin X" marker since they have no
IIFE to close.
2026-04-17 15:06:42 -05:00
J. Nick Koston 864d31aa65 [core] Put ComponentMarker outside the IIFE as a visual bracket
The marker comment was being emitted as the first line *inside* each
IIFE:

  []() {
    // === logger ===
    // logger:
    //   ...
    ...
  }();

That works but buries the component label inside the lambda body, so
scanning generated main.cpp to find "where does component X's setup
live" is harder than it needs to be. Emit the marker before and after
the IIFE instead:

  // === logger ===
  []() {
    // logger:
    //   ...
    ...
  }();
  // === logger ===

Comment-only components (e.g. sha256, async_tcp, empty platforms like
binary_sensor:) don't grow a useless trailing duplicate marker —
when there's no IIFE to bracket, the marker is emitted once.
2026-04-17 15:06:42 -05:00
J. Nick Koston 936694af2c [core] Don't emit IIFE for comment-only chunks
Some components (sha256, async_tcp, network, empty text_sensor:, etc.)
emit only a ComponentMarker plus config-dump comments and no actual
C++ statements. Wrapping those in a `[]() { ... }();` IIFE is pure
clutter in the generated main.cpp — the IIFE has no body.

When _wrap_in_iifes sees a chunk whose lines are all // comments,
emit them verbatim instead of wrapping. Peak stack and flash are
unchanged on apollo and neargaragedoor since GCC was already
eliding the empty IIFEs; this just makes the generated code read
cleanly to humans.
2026-04-17 15:06:42 -05:00
J. Nick Koston 6a7c9af870 [core] Drop noinline from IIFE chunks and rename helper
Additional measurements showed GCC's -Os inliner re-inlines most IIFE
chunks back into setup() by choice, and the structural scoping alone
captures nearly all of the peak-stack benefit on esp32 without the
flash cost of forcing all chunks to stay as real functions.

Apollo (esp32-s3, -Os) with vs without noinline:
  peak setup stack     176 B (noinline)  vs  304 B (scope-only)
  flash delta         +388 B (noinline)  vs   -504 B (scope-only)
  chunks kept          86               vs    20

Issue #15796 is an LVGL-setup class of bug that has only surfaced on
esp32 after years in the field; the extra guarantee that noinline
provides is not worth the flash cost in practice. Also rename the
helper from _wrap_in_noinline_iifes to _wrap_in_iifes to match.
2026-04-17 15:06:42 -05:00
J. Nick Koston 29dcf9fc51 [core] Use __attribute__((noinline)) on IIFE lambdas to honor attribute
The C++ standard-attribute spelling [[gnu::noinline]] placed between a
lambda's parameter list and body binds to the return type, not the
call operator. GCC 14 silently ignores it and emits -Wattributes
warnings at every chunk site. Switch to GCC's __attribute__((...))
syntax which binds to operator() as intended.

Measured impact on apollo-r-pro-1-eth (esp32-s3, -Os) vs the broken
[[gnu::noinline]] version: setup() frame 160 B -> 32 B, peak stack
304 B -> 176 B (another -42%). Flash grows by 888 B because all 86
chunks now stay as separate functions instead of GCC inlining the
small ones (which it was free to do when the attribute was ignored).

Net vs baseline -Os: peak stack 1264 B -> 176 B (-86%); flash
+388 B (<0.05% of a typical esp32 partition).
2026-04-17 15:06:42 -05:00
J. Nick Koston 6b67224286 [core] Chunk setup() into per-component noinline IIFEs
Generated setup() is a single monolithic function whose stack frame
scales super-linearly with config size. On a 5,943-line apollo build
the frame reached 1,264 B at -Os; extrapolation onto larger configs
(e.g. the 16k-line LVGL config in #15796) plausibly overflows the
8 KB loop task stack before safe_mode can increment its boot counter.

Emit a ComponentMarker sentinel at the start of each component's
to_code output, then have cpp_main_section wrap each component's
block (and sub-splits of up to 50 statements within each block) in a
noinline IIFE lambda. Each lambda's ENTRY frame is released on
return, bounding peak stack to setup() frame + max chunk frame.

Measured on apollo-r-pro-1-eth (esp32-s3, -Os):

  setup() frame        1264 B  ->  160 B
  max chunk frame      n/a     ->  144 B
  peak setup stack     1264 B  ->  304 B  (-76%)
  total flash      792,471 B   ->  791,995 B  (-476 B)

The brace-depth guard in _wrap_in_noinline_iifes ensures we never
split between the RawStatement("{") / RawStatement("}") pair emitted
by cg.with_local_variable() (currently only wifi), so scoped locals
stay intact.
2026-04-17 15:06:41 -05:00
J. Nick Koston 291f80263a [ci] Scope local pylint pre-commit hook to esphome/ to match CI
The ``pylint`` CI job runs ``pylint esphome`` — esphome/ only. The
local pre-commit hook had no ``files:`` restriction, so it also
linted ``tests/``, flagging pre-existing protected-access usage and
class-size issues that CI never sees. That blocked local commits on
warnings CI doesn't gate on.

Add ``files: ^esphome/.+\\.py$`` to the local pylint hook so its scope
matches the CI job exactly.
2026-04-17 13:41:19 -05:00
70ea527161 Bump ruff from 0.15.10 to 0.15.11 (#15790)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-04-17 10:17:51 -05:00
J. Nick Koston 43d98cb445 Merge remote-tracking branch 'upstream/dev' into decouple_scheduler_loop_cadence
# Conflicts:
#	esphome/core/scheduler.cpp
2026-04-17 09:33:59 -05:00
J. Nick Koston 34c35c84d5 [core] Fix DelayAction compile error with non-const reference args (#15814) 2026-04-17 14:31:31 +00:00
Jonathan SwobodaandJ. Nick Koston bcbfc843ae [ethernet] Fix SPI3_HOST default breaking compile on variants without SPI3 (#15809)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-04-17 14:05:30 +00:00
J. Nick Koston 162bd415ce Merge remote-tracking branch 'upstream/fix-delay-action-mutable-lambda' into integration 2026-04-17 08:36:08 -05:00