Commit Graph
27734 Commits
Author SHA1 Message Date
J. Nick Koston bfa5cf697f Merge remote-tracking branch 'upstream/dev' into integration 2026-04-21 16:11:22 +02:00
43c6b839cd [sensor] Filter to round to significant digits (#11157)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2026-04-21 14:00:03 +00:00
J. Nick Koston 2aa410c5a9 Merge remote-tracking branch 'origin/fast-millis-esp32' into integration 2026-04-21 15:32:00 +02:00
J. Nick Koston 9ad1bc259f cleanup 2026-04-21 15:31:04 +02:00
J. Nick Koston 54086057e7 fix merge 2026-04-21 15:28:03 +02:00
Edward Firmo 0c9d443a5c [esp32_ble] Add use_psram option to offload BT memory allocation to SPIRAM (#15644) 2026-04-21 15:18:46 +02:00
J. Nick Koston 3d0cf4f87c Merge remote-tracking branch 'origin/core-component-phase-raii' into integration 2026-04-21 15:15:41 +02:00
J. Nick Koston 8ebbebceb7 Merge remote-tracking branch 'origin/fast-millis-esp32' into integration 2026-04-21 15:15:02 +02:00
J. Nick Koston ab1fd9b0ba Merge remote-tracking branch 'origin/dev' into integration 2026-04-21 15:14:45 +02:00
J. Nick Koston 3ead00d3fb Merge remote-tracking branch 'upstream/dev' into fast-millis-esp32
# Conflicts:
#	esphome/core/application.h
2026-04-21 15:13:44 +02:00
J. Nick Koston 5b912e0c7c [core] RAII guard for component loop phase
Replace paired before_component_phase_() / after_component_phase_() calls
with a ComponentPhaseGuard whose constructor processes pending enable_loop
requests and sets in_loop_ = true, and whose destructor clears in_loop_
at scope exit.

The guard methods are ESPHOME_ALWAYS_INLINE. Verified byte-identical
.text/.data/.bss output on an ESP32 IDF build, and identical disassembly
for both esphome::loop_task (where Application::loop is inlined) and
Application::setup.

Benefit: symmetry is now enforced by the type system. Future early
returns or refactors inside the component phase can't accidentally leave
in_loop_ set without a matching reset.
2026-04-21 15:11:46 +02:00
J. Nick Koston 14defb69b6 [template] Use placement new for template text restore saver (#15883) 2026-04-21 13:04:13 +00:00
Egor Vorontsovpre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>J. Nick Koston
3a6f3dfb94 [lock] Implemented open states support (#15120)
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-04-21 13:03:07 +00:00
J. Nick Koston 7bd36e0c8d [debug] Migrate trivial buf_append_printf sites to buf_append_str (#15885) 2026-04-21 12:51:45 +00:00
J. Nick Koston e4f413adad [core] decouple main loop cadence from scheduler wake timing (#15792) 2026-04-21 14:48:21 +02:00
J. Nick Koston 1504ac3d19 [core] Replace strnlen in buf_append_str for Zephyr compatibility (#15892) 2026-04-21 12:32:29 +00:00
J. Nick Koston 947c714f89 [core] Inline api_is_connected() for hot-path callers (#15888) 2026-04-21 13:48:33 +02:00
J. Nick Koston e4d5886383 [zwave_proxy] Inline loop() hot-path fast-paths for response_handler_ and process_uart_ (#15887) 2026-04-21 13:48:16 +02:00
J. Nick Koston f504099485 [api] Replace clients_ std::vector with compile-time std::array + uint8_t count (#15889) 2026-04-21 13:47:37 +02:00
Jonathan Swoboda cb56f9a9bf [qmc5883l] Use GPIO interrupt when DRDY pin is configured (#15876) 2026-04-21 07:47:16 -04:00
Jonathan Swoboda 26a656af29 [ld2412] Fix null deref in set_basic_config when entities unconfigured (#15893) 2026-04-21 07:47:07 -04:00
J. Nick Koston 9fa8dcc116 Merge remote-tracking branch 'upstream/api-clients-static-array' into integration 2026-04-21 12:13:45 +02:00
J. Nick Koston 69896446c5 [api] Address review: make ActiveClientsView read-only WRT ownership
Change the view to hold `const unique_ptr<APIConnection>&` so external
callers can't reset() or move slots — which would break the
api_connection_count_ invariant. Callers still get a non-const
APIConnection& on dereference (via `(*ptr).method()`), so all existing
call sites work unchanged.

active_clients() is now const-qualified since it only returns a view
over const unique_ptrs.
2026-04-21 12:13:12 +02:00
J. Nick Koston df53dff97a [api] Restore original per-platform RAM rationale comments 2026-04-21 12:11:40 +02:00
J. Nick Koston e89836d0d4 [api] Trim overlong/stale comments from PR 2026-04-21 12:10:52 +02:00
J. Nick Koston 3302903c39 [api] Lower default max_connections to 5 on esp32/libretiny
With N=5 the static-RAM trade becomes net-negative at just 1 client
(the typical ESP32 deployment — just Home Assistant):

  +8 B static (5 slots × 4 B − 12 B removed vector object)
  −12 B heap (8 B header + 4 B slot) at 1 client
  = −4 B net

N=6 had breakeven at 2 clients; N=5 puts breakeven at 1 client so the
common case is pure win.

5 slots covers HA + dashboard + 1 reconnecting socket, which is all
most deployments ever use. Users needing more can bump explicitly in
YAML (schema max is 20).

host stays at 8 (no BSS-slot concern). esp8266 stays at 4, rp2040
stays at 4.
2026-04-21 12:07:18 +02:00
J. Nick Koston 101eaa3222 [api] Keep host default at 8, only lower esp32/libretiny to 6
Revert host=6 to host=8 — the host platform has no BSS-slot concern
(no flash/RAM pressure). The lowering to 6 still applies to
esp32/bk72xx/rtl87xx/ln882x where every array slot costs static RAM.

Users who need more than 6 concurrent clients can bump max_connections
up to the schema's max of 20.
2026-04-21 11:59:53 +02:00
J. Nick Koston 82e9613631 [api] Drop ConstActiveClientsView — single caller reads cleaner as indexed loop
is_connected_with_state_subscription() was the only const caller of
active_clients(). Iterating by index directly in that method lets us
drop the entire ConstActiveClientsView class from the header, leaving
a single ActiveClientsView for the mutable range-for sites.
2026-04-21 11:58:58 +02:00
J. Nick Koston 250d28f69c [api] Replace clients_ std::vector with compile-time std::array + uint8_t count
Convert APIServer's client container from
`std::vector<std::unique_ptr<APIConnection>>` to a compile-time sized
`std::array<..., MAX_API_CONNECTIONS>` with an inline `uint8_t
api_connection_count_` tracking the active slice.

Why:
- Eliminates a persistent heap-held buffer (one of APIServer's
  fragmentation sources). The vector grows via doubling reallocations
  (0 -> 1 -> 2 -> 4 -> 8) and keeps its capacity for the life of the
  process.
- Kills ~100-150 bytes of `_M_realloc_insert` template instantiation
  per build (measured: -92 B on ESP32-S3, -152 B on ESP8266).
- is_connected() becomes `api_connection_count_ != 0` — a single-byte
  load, down from two pointer loads + compare.
- The count lives in what was already 1 byte of padding after
  shutting_down_, replacing the removed max_connections_ runtime
  setter. Zero size overhead in that slot.
- Max connections is now a compile-time constant via
  cg.add_define("MAX_API_CONNECTIONS", ...), so the accept cap check
  and the array size derive from the same value.

Also lower default max_connections on mid/high-RAM platforms from 8 to
6. With a compile-time array, every slot costs 4 bytes of static RAM
whether a connection ever uses it or not — 6 covers HA + dashboard +
spares without paying the full 8-slot static cost. esp8266 stays at 4,
rp2040 stays at 4.

Mechanical churn:
- 13 `for (auto &c : this->clients_)` -> `for (auto &c : this->active_clients())`,
  where active_clients() returns a pointer-range view over the active
  slice.
- `.empty()` / `.size()` -> comparisons against api_connection_count_.
- `.emplace_back()` / `.pop_back()` / `.back()` rewritten in the
  swap-and-pop path to index into the array and reset() the freed slot
  (maintains the invariant that slots [count, N) are always nullptr).

Measured on ESP32-S3 (zwave-proxy-seeedw5500, N=6):
  RAM: 32464 -> 32480 B  (+16 B static, but no heap buffer)
  Flash: 469011 -> 468919 B  (-92 B)

Measured on ESP8266 (basic8266, N=4):
  RAM: 29268 -> 29268 B  (unchanged at this granularity)
  Flash: 323847 -> 323695 B  (-152 B)
2026-04-21 11:54:13 +02:00
J. Nick Koston 110911eddd Merge remote-tracking branch 'upstream/zwave-api-is-connected-inline' into integration 2026-04-21 11:27:18 +02:00
J. Nick Koston e99492a6e3 Merge branch 'zwave-proxy-inline-loop-fast-paths' into integration 2026-04-21 11:27:14 +02:00
J. Nick Koston 3a72deddf9 [core] Address review: drop redundant defines.h include from util.cpp
util.h now includes defines.h unconditionally, so the explicit include in
util.cpp is duplicative.
2026-04-21 11:26:45 +02:00
J. Nick Koston 413b9ca9ad [zwave_proxy] Address review: pin enum ordering and document slow-path precondition
Add static_asserts that the SEND_ACK/CAN/NAK states stay contiguous, so the
inline range check in response_handler_() fails at compile time if the enum
is ever reordered.

Document that process_uart_slow_() requires available() > 0 at the
declaration (the .cpp definition already carries the rationale).
2026-04-21 11:25:46 +02:00
J. Nick Koston ac08d644c7 Merge branch 'zwave-proxy-inline-loop-fast-paths' into integration 2026-04-21 11:15:27 +02:00
J. Nick Koston 2578ee6061 Merge branch 'zwave-api-is-connected-inline' into integration 2026-04-21 11:15:23 +02:00
J. Nick Koston b8de93aabe [core] Inline api_is_connected() for hot-path callers
Move the api_is_connected() definition from util.cpp to util.h and mark it
ESPHOME_ALWAYS_INLINE. The body is trivial — a nullptr check on
global_api_server plus APIServer::is_connected() (which is
!clients_.empty()) — so the out-of-line call8 was pure overhead for hot
paths that check connectivity every loop tick (e.g. zwave_proxy::loop,
serial_proxy::loop).

With USE_API disabled the function collapses to "return false" at compile
time and folds away entirely.

util.h now pulls in api_server.h under USE_API. Only 25 .cpp files include
util.h and all of them are on USE_API builds in practice (wifi, safe_mode,
nextion, web_server, etc.), so the additional transitive include is
essentially free on the platforms that matter.

Measured on ESP32-S3: the call8 in ZWaveProxy::loop() is replaced by
5 inline Xtensa instructions (load global_api_server, deref, null check,
clients_ start/finish compare) — no call overhead, no stack frame.
2026-04-21 11:14:12 +02:00
J. Nick Koston f74c3c0489 [zwave_proxy] Inline loop() hot-path fast-paths for response_handler_ and process_uart_
Split response_handler_ and process_uart_ into tiny inline wrappers in the
header that short-circuit the common case, plus _slow_ bodies in the .cpp.

- response_handler_: most loop() ticks have parsing_state_ outside the three
  SEND_* states; inline the range check and skip the call8 entirely.
- process_uart_: most ticks have no UART bytes pending; inline an available()
  check and skip the out-of-line call + its stack frame. Inside the slow
  path, switch the while() to do/while() since the caller has already
  confirmed available() > 0.

ESPHOME_ALWAYS_INLINE is required — with -Os gcc otherwise clones the
wrapper into a shared \$isra\$ outline and keeps the call8.

Measured on ESP32-S3 zwave-proxy-seeedw5500 build: idle-tick out-of-line
calls in ZWaveProxy::loop() drop from 4 (response_handler_, api_is_connected,
virtual parent_->is_connected, process_uart_ which nested available()) to 3
(api_is_connected, virtual is_connected, available).
2026-04-21 11:09:57 +02:00
J. Nick Koston ecefef4e08 Merge remote-tracking branch 'upstream/debug-buf-append-str' into integration 2026-04-21 07:37:58 +02:00
J. Nick Koston 27dac71670 no strnlen on zephyr 2026-04-21 07:37:25 +02:00
J. Nick Koston dd1c8a83be Merge remote-tracking branch 'upstream/debug-buf-append-str' into integration 2026-04-21 07:05:31 +02:00
J. Nick Koston 05fd2f9a2f [debug] Migrate trivial buf_append_printf sites to buf_append_str
Convert buf_append_printf calls that use only literal format strings
or %s specifiers over to buf_append_str, which avoids pulling in
printf machinery on all platforms and keeps literals in flash on
ESP8266 via the PSTR()-wrapping macro introduced in #15738.

Format strings with numeric specifiers (PRIu32, %u, PRIX32, etc.)
are kept on buf_append_printf. Runtime %s calls in debug_esp8266.cpp
stay as-is because the ESP8266 buf_append_str macro requires a
string literal for PSTR().
2026-04-21 07:02:50 +02:00
J. Nick Koston c87c1e27f5 Merge remote-tracking branch 'upstream/decouple_scheduler_loop_cadence' into integration 2026-04-21 06:23:25 +02:00
J. Nick Koston ecdbe0f9bb [runtime_stats] report tail_us=0 on Phase A-only ticks
Previously loop_tail_start_us defaulted to loop_before_end_us, so on
ticks where Phase B was gated out (loop_interval_ not yet elapsed, no
wake, no high-frequency request) tail_us measured "time from end of
Phase A to stats recording" — i.e. gate-check + stats-prefix overhead —
and was accumulated into the per-tick "tail" bucket even though no
component tail had actually run.

That mis-attributed gate-check + stats-prefix overhead to the tail
metric, which is supposed to represent trailing overhead of the
component phase specifically (after_component_phase_ + the little bit
before record_loop_active). On a device whose loop_interval_ is raised
for power savings, Phase A-only ticks dominate and the mis-attributed
tail would skew the stats report.

Fix: gate the tail_us computation on do_component_phase. Initialize
loop_tail_start_us to 0 (unused on Phase A-only ticks) and only
subtract from loop_now_us when Phase B ran. The overhead that used to
land in "tail" now falls into "residual" (active − before − components
− tail), which is the correct bucket for per-iteration bookkeeping that
is not phase-specific.
2026-04-21 06:22:55 +02:00
J. Nick Koston d3e9ccc83a Merge remote-tracking branch 'origin/decouple_scheduler_loop_cadence' into integration 2026-04-21 06:10:22 +02:00
J. Nick Koston 66e0c866d7 [core] rename after_loop_tasks_ → after_component_phase_
Matches the before_component_phase_ / after_component_phase_ symmetry
now that Phase A and Phase B are separated. Also move the call to the
very end of the `if (do_component_phase) { ... }` block so the helper
semantically closes out the phase, rather than sitting before the
last_loop_ / now bookkeeping.

No functional change: in_loop_ is read only inside the component
iteration (disable_looping_component's swap fixup), so setting it false
at any point after the for-loop ends is equivalent. Runtime-stats tail
timing still captures the same region (the micros() sample happens
before any of the moved lines).
2026-04-21 06:08:34 +02:00
J. Nick Koston 6e37503667 Merge branch 'dev' into decouple_scheduler_loop_cadence 2026-04-21 04:55:41 +02:00
J. Nick Koston 00da5d0a26 Merge remote-tracking branch 'upstream/fast-millis-esp32' into integration 2026-04-21 04:55:03 +02:00
J. Nick Koston 02f77cce79 Merge remote-tracking branch 'upstream/dev' into fast-millis-esp32
# Conflicts:
#	esphome/core/application.cpp
2026-04-21 04:53:01 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a8bd035b62 Bump CodSpeedHQ/action from 4.13.1 to 4.14.0 (#15880)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 04:44:25 +02:00
J. Nick Koston f05fa45747 [sensor] Specialize throttle_with_priority NaN-only case (#15823) 2026-04-21 04:41:13 +02:00