Commit Graph

14522 Commits

Author SHA1 Message Date
J. Nick Koston 4ea417966d [core] Poison brace-depth tracking once it goes negative
Address Copilot review: the prior comment promised that a negative depth would never re-enable splitting, but an arithmetically-balanced later { could bring depth back to 0 and resume flushing mid-stream. Track an explicit 'poisoned' flag set once depth < 0 that permanently disables further flushes for the rest of the input. Adds a regression test where a leading } and a later { would have re-enabled splitting without the poison flag.
2026-04-17 19:01:12 -05:00
J. Nick Koston 6446f309c1 [core] Replace mutable-list-flag pattern with _ComponentGroup dataclass
The mutable-list-of-bool trick for rebinding-safe flag mutation works but reads poorly. Replace with a _ComponentGroup dataclass carrying lines + unsafe + no_split fields. cpp_main_section now reads as a straight iteration over typed groups; no apologetic comments needed.
2026-04-17 18:56:59 -05:00
J. Nick Koston 093c34d4a4 [core] Review cleanup: docstring accuracy, rationale comments, unsafe+no_split test
- Fix the ComponentMarker docstring's incomplete 'either placement-news or mutates a global' claim — acknowledge that function-local patterns also exist and note the bare-local detection covers them.
- Document that _emits_bare_local's RawExpression detection is intentionally safety-biased: false negatives break compilation, false positives just keep a slightly larger IIFE. Note the CallExpression(..., RawExpression) negative case explicitly.
- Explain the mutable-list-flag pattern in cpp_main_section — dataclass would read cleaner but the pattern is localized.
- Add regression test for a group with BOTH IIFEUnsafeStatement and a bare-local: unsafe wins (flat emission) because a return inside any IIFE, even a single big one, only exits the lambda.
2026-04-17 18:55:39 -05:00
J. Nick Koston 3ab935bebb [core] Expose IIFE_MAX_STATEMENTS constant and derive test sizes from it
Tests were hardcoding 120 statements and expecting 3 sub-chunks from a 50-cap. Extract the cap as a named module constant and compute the test-input size from it, so bumping the cap doesn't silently invalidate the tests.
2026-04-17 18:50:41 -05:00
J. Nick Koston bb0067f517 [core] Strengthen RawExpression-as-arg test
Exercise the actual CallExpression(..., RawExpression) pattern that components use for passing raw arguments. The previous test used RawStatement filler which didn't exercise the detection path we wanted to assert doesn't trigger.
2026-04-17 18:49:04 -05:00
J. Nick Koston 550f6e7c72 [core] Detect bare-local emission and disable sub-split for those groups
A component's group is wrapped in a single IIFE with no sub-splitting when its to_code emits any of: scope-brace RawStatement, direct RawExpression via cg.add (raw bare-local or field-assignment like 'tz.field = x'), or typed AssignmentExpression (cg.variable). Detection is content-aware so entity_helpers' inline-comment RawStatements and RawExpression-as-CallExpression-arg don't false-positive. Adds 4 regression tests covering each detection path and the non-triggering inverse cases.
2026-04-17 18:46:30 -05:00
J. Nick Koston 5f2582efcd [safe_mode] Fix setup()-exit return getting trapped in IIFE
safe_mode emits `if (should_enter_safe_mode(...)) return` via
cg.add(RawExpression(...)) to short-circuit the rest of setup() and
boot into safe mode. With setup() split into per-component IIFEs,
that `return` was only exiting the lambda, so the rest of setup() ran
anyway — breaking safe-mode recovery.

Add IIFEUnsafeStatement, a Statement wrapper that marks its containing
component's block for flat emission (no IIFE). safe_mode wraps its
return expression in it. cpp_main_section detects any such statement
in a group and emits that group flat so control-flow constructs like
`return` still affect setup() itself.

IIFEUnsafeStatement.__str__ routes its inner through statement() so
bare Expression subclasses pick up the terminating semicolon. Reported
by @swoboda1337.
2026-04-17 18:12:14 -05:00
J. Nick Koston e26ce59797 for progmem 2026-04-17 16:18:39 -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
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
dependabot[bot] 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 34c35c84d5 [core] Fix DelayAction compile error with non-const reference args (#15814) 2026-04-17 14:31:31 +00:00
Jonathan Swoboda 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 d4fe46bb24 [core] Expose App.wake_loop_isrsafe() on ESP8266 (#15797) 2026-04-17 02:46:12 -10:00
J. Nick Koston 523c6f2376 [core] coerce set_interval(0) / update_interval: 0ms to 1ms (#15799) 2026-04-17 02:45:50 -10:00
Clyde Stubbs b018ac67bc [image] Fix byte order handling (#15800) 2026-04-17 22:11:05 +10:00
Clyde Stubbs 1a529a62aa [mipi_spi] Drawing fixes for native display (#15802) 2026-04-17 21:17:16 +10:00
Edvard Filistovič 6a46437a5f [wifi] Guard retry_phase_to_log_string with log level check to fix warning (#15801)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-04-17 10:58:39 +00:00
Diorcet Yann cfe8c0eeee [wireguard] Bump esp_wireguard to 0.4.5 for ESP-IDF v6 (#15804) 2026-04-17 06:20:55 -04:00
J. Nick Koston b232fc91ab [runtime_stats] Track main loop active time and report overhead (#15743) 2026-04-16 14:07:26 -10:00
Yves Fischer ac50f33388 Fix typo in devcontainer.json (#15791) 2026-04-16 18:27:50 -04:00
Jonathan Swoboda ff52bb3029 [lvgl] Guard lv_image_set_src wrapper with LV_USE_IMAGE (#15789) 2026-04-16 18:16:58 -04:00
J. Nick Koston 627e440bd6 [libretiny] Make IRAM_ATTR functional on RTL87xx and LN882H (#15766) 2026-04-16 19:38:49 +00:00
Jonathan Swoboda 6bb90a1268 [esp32] Accept unquoted minimum_chip_revision values (#15785) 2026-04-16 19:07:04 +00:00
J. Nick Koston 7d8add70a7 [ili9xxx] Guard against null buffer in display_() when allocation fails (#15786) 2026-04-16 09:01:55 -10:00
rwalker777 9094392870 [gpio] Keep interrupts enabled for gpio binary_sensor shared with deep_sleep wakeup pin (#15020)
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>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-04-16 09:01:32 -10:00
J. Nick Koston c6ad23fbc0 [bundle] Force-resolve nested IncludeFile during file discovery (#15762) 2026-04-16 08:45:33 -10:00
Jonathan Swoboda 6af7a9ed8f [qmc5883l] Move per-update log line from DEBUG to VERBOSE (#15781) 2026-04-16 14:36:06 -04:00
SaVi 0b051289f5 [core] Add missing exception chaining (raise from) across codebase (#15648)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
2026-04-16 15:19:33 +00:00
Clyde Stubbs d8329dba22 [mipi_spi] Add Waveshare C6 LCD 1.47 (#15776) 2026-04-16 11:17:51 -04:00
guillempages ee70a4aa72 [tm1637] Add set_brightness method (#15322)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-04-16 09:46:27 -04:00
tomaszduda23 04a58159d0 [zephyr_ble_server] add support for on_numeric_comparison_request (#14400)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-04-16 09:43:03 -04:00
J. Nick Koston 4c758fa1da [time] Fix RTC is_valid() rejecting valid times after day_of_year cleanup (#15763) 2026-04-16 09:40:22 -04:00
J. Nick Koston c8e21802db [core] Diagnose missing cg.templatable in codegen for TEMPLATABLE_VALUE fields (#15758) 2026-04-16 09:36:55 -04:00
Boris Krivonog b40ffacb8d [mitsubishi_cn105] use HEAT_COOL mode to enable temperature slider (#15748) 2026-04-16 09:35:24 -04:00
Clyde Stubbs e0118dd8eb [lvgl] Clean the build if lv_conf.h changes (#15777) 2026-04-16 09:19:42 -04:00
J. Nick Koston e7194dce75 [core] Deduplicate entity type boilerplate with X-macro pattern (#15618) 2026-04-15 17:45:01 -10:00
J. Nick Koston 01b5bef37f [status_led] Disable loop when idle (#15642) 2026-04-15 17:44:42 -10:00
dependabot[bot] 403a9f7b7e Bump github/codeql-action from 4.35.1 to 4.35.2 (#15759)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 10:12:30 -10:00
dependabot[bot] 10f52f2056 Bump aioesphomeapi from 44.15.0 to 44.16.0 (#15757)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-15 09:07:49 -10:00
Jonathan Swoboda 274c01ca74 [sx126x][sx127x] Fix frequency precision loss from float32 codegen (#15753) 2026-04-15 14:32:33 -04:00
Jonathan Swoboda 3b82c6e38b [st7789v] Fix swapped offset_width/offset_height in model presets (#15755) 2026-04-15 14:32:11 -04:00