Commit Graph
28421 Commits
Author SHA1 Message Date
J. Nick Koston ce116a69e7 [core] Drop static on shrink_scheduler_vector_ for clang-tidy compliance
Project's .clang-tidy applies different naming rules to static methods
(ClassMethodCase, no suffix) vs instance methods (PrivateMethodCase /
ProtectedMethodCase, _ suffix required). The helper had a trailing _ but
was static, so clang-tidy flagged it. Drop static -- the helper is already
noinline'd and called only at trim time, so the hidden this arg is free
in any meaningful sense.
2026-04-30 11:58:44 -05:00
J. Nick Koston 0fcbc442fe Merge remote-tracking branch 'upstream-ssh/scheduler-pool-unbounded-freelist' into integration 2026-04-30 11:38:57 -05:00
J. Nick Koston d7594db6fd [core] Factor trim_freelist() vector shrink into out-of-line helper
The three swap-with-copy lines in trim_freelist() inlined the
construct-swap-destruct dance per call site, costing ~440 B flash on ESP32.
Move the swap-shrink into a noinline private static helper so all three
callers share one body. Net flash cost of vector shrinking drops to ~296 B
(from ~444 B) while preserving the same RAM-reclaim behaviour.

shrink_to_fit() is a non-binding hint that the toolchain ignores, so the
swap-with-copy idiom is still required.
2026-04-30 11:38:17 -05:00
J. Nick Koston 56e1ceda33 Merge remote-tracking branch 'upstream-ssh/scheduler-pool-unbounded-freelist' into integration 2026-04-30 11:31:34 -05:00
J. Nick Koston 8b840e8517 [core] Also shrink items_/to_add_/defer_queue_ vector capacity in trim_freelist()
The freelist holds the boot-peak count of recycled SchedulerItem*; items_,
to_add_, and defer_queue_ hold the boot-peak vector *capacity* of live
SchedulerItem*. std::vector grows by doubling and retains capacity even when
items drain, so post-boot the vector slack can be larger than the freelist
itself. Swap each with a same-content copy to size them exactly to their
current contents.

Live items are preserved -- the swap-copy idiom builds the new vector from
the existing pointers, then the old (over-capacity) vector is destroyed.
2026-04-30 11:31:13 -05:00
J. Nick Koston d36c56d3f5 [core] Trim scheduler freelist of post-boot peak 10s after setup
Boot churn (component init, first sensor reads, retries) inflates the freelist
beyond the steady-state high-water mark. Without a one-shot trim that peak
would be retained forever. Adds Scheduler::trim_freelist() and schedules it
from Application::setup() to fire SCHEDULER_FREELIST_TRIM_DELAY_MS (10s) after
setup completes -- well past the bulk of post-setup async work.

Items currently in items_/to_add_/defer_queue_ are untouched; only the
freelist's recycled items are deleted. Post-trim, the freelist regrows to
the new (post-startup) high-water mark.
2026-04-30 11:29:32 -05:00
J. Nick Koston e9bd84cb43 Revert "[sensor] Drop Component from timeout filters, use self-keyed scheduler"
This reverts commit 6ccc2b23b5.
2026-04-30 11:15:10 -05:00
J. Nick Koston 7d529bff12 Revert "[sensor] Drop incorrect O(1) complexity claim from timeout filter comment"
This reverts commit 87a2b623f3.
2026-04-30 11:15:09 -05:00
J. Nick Koston 633a1112d1 Merge remote-tracking branch 'upstream-ssh/sensor-timeout-filter-scheduler' into integration 2026-04-30 11:06:59 -05:00
J. Nick Koston 20743e3e9a Merge branch 'scheduler-pool-unbounded-freelist' into integration 2026-04-30 11:06:46 -05:00
J. Nick Koston 87a2b623f3 [sensor] Drop incorrect O(1) complexity claim from timeout filter comment
set_timer_common_'s self-key cancel path scans items_ and to_add_ linearly,
so the cancel-and-replace is O(N) in the global scheduler item count, not
O(1). Reword to describe the behavior without the complexity claim.

Address Copilot review feedback.
2026-04-30 11:01:24 -05:00
J. Nick Koston 2a451cb870 [core] Strengthen scheduler-pool integration test with lower-bound check
Adds an assertion that the observed peak pool size exceeds the old
MAX_POOL_SIZE=5 cap. Without this, a silent regression that re-introduced a
small cap could pass the existing pool_full_count == 0 invariant. Phase 5 + 6
of the fixture schedule 8 + 10 same-component timeouts, so the peak should
comfortably exceed 5.

Address Copilot review feedback on PR.
2026-04-30 10:57:11 -05:00
J. Nick Koston 6ccc2b23b5 [sensor] Drop Component from timeout filters, use self-keyed scheduler
Migrates TimeoutFilterBase / TimeoutFilterLast / TimeoutFilterConfigured off
Component, mirroring the migration #16132 did for the other Component-based
sensor filters. They now arm/re-arm via App.scheduler.set_timeout(this, ...)
keyed on the filter pointer; the scheduler cancels and replaces any pending
arm in O(1) on each new_value(). Filters live for the program's lifetime, so
the self-key never dangles.

Net effect: this reverts the design from #11922, which moved timeout filters
off the scheduler specifically because the bounded SchedulerItem pool churned
on devices with many timers (LD2450 etc.). With #16172 replacing that pool
with an unbounded intrusive freelist, the original churn problem is gone and
the scheduler is once again the right primitive for this workload.

Per-instance footprint shrinks: lose Component (second vptr, packed bookkeeping
bytes, ComponentRuntimeStats block when runtime_stats: is enabled) and drop
the now-unused timeout_start_time_ field. get_setup_priority() and the loop()
poll go with it.

Sensor timeout-filter syntax is unchanged. No user-facing change.
2026-04-30 10:49:18 -05:00
J. Nick Koston f9b87d0ede [core] Replace scheduler pool vector with unbounded intrusive freelist
The fixed MAX_POOL_SIZE=5 cap was the source of the heap churn the pool was
meant to prevent: any device with more than 5 concurrent timers (e.g. a board
with 30+ LD2450 sensors) hit a steady-state oscillation of recycle->delete and
acquire->new on every loop iteration.

Replace std::vector<SchedulerItem*> with a singly-linked freelist threaded
through SchedulerItem::next_free, which shares storage with `component` via an
anonymous union (zero per-item overhead -- the component pointer is dead while
pooled). Drop the cap entirely: the freelist quiesces at the application's
natural concurrent-timer high-water mark, which is the working set the device
already needs while those timers are active.

No std::vector means no growth-doubling slack and no realloc copies during
warm-up. Caller of get_item_from_pool_locked_() must overwrite item->component
before unlocking (already true at the sole call site); nullptr remains a valid
live `component` value for SELF_POINTER items, so we cannot pre-clear it.
2026-04-30 10:42:46 -05:00
J. Nick Koston db5040a468 Merge remote-tracking branch 'upstream/dev' into integration 2026-04-30 10:17:42 -05:00
J. Nick Koston e32ce0f006 Merge remote-tracking branch 'upstream/sensor-throttle-average-pack-bitfield' into integration 2026-04-30 08:15:59 -05:00
Kevin Ahrendt 2758aa5517 [audio] bump microOpus to v0.4.0 to use fixed-point by default on ESP32 (#16168) 2026-04-30 09:12:39 -04:00
J. Nick Koston bf917144c4 [sensor] Pack ThrottleAverageFilter have_nan_ into n_ bitfield
Saves 4 B per ThrottleAverageFilter instance (28 B → 24 B on 32-bit).
have_nan_ is a single boolean that previously cost 4 B due to padding;
fold it into the high bit of n_ as a 31-bit + 1-bit bitfield.

To guarantee n_ cannot overflow under realistic configurations, cap
the YAML time_period at 24 h. At a pessimistic 1 kHz source rate the
counter peaks at 86.4M, leaving 25x headroom against 2^31. Anyone
needing multi-day rolling averages should be using a different
filtering strategy anyway.

Breaking change: configurations with throttle_average periods longer
than 24 h will now fail validation. No real-world configs are
expected to use such values.
2026-04-30 08:11:31 -05:00
J. Nick Koston d05f4f82e1 Merge remote-tracking branch 'upstream/core-loop-wake-take-first' into integration 2026-04-30 07:52:35 -05:00
Kevin Ahrendt a8b0133ec1 [audio] Enable specific codecs and configure advanced features (#16166) 2026-04-30 08:49:28 -04:00
J. Nick Koston 59a8057e9a [core] Inline loop gate expression to avoid stale local reuse
The previous gate captured each subexpression in a named local
(high_frequency, elapsed, woke). Locals are easy to reuse later in
the function, and we have hit bugs from stale reuse before — most
recently is_high_frequency being read again instead of re-checked.
Inline the expression directly into do_component_phase so there is
nothing tempting to reuse downstream.
2026-04-30 06:34:14 -05:00
J. Nick Koston d403f472fc Merge remote-tracking branch 'upstream/api-infrared-rf-speed-optimized' into integration 2026-04-30 06:22:15 -05:00
Clyde Stubbs 1398dcebb4 [st7789v] Add deprecation warnings (#16162) 2026-04-30 00:53:37 -05:00
dependabot[bot] 096d0c4279 Bump aioesphomeapi from 44.22.0 to 44.23.0 (#16161)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-30 04:45:19 +00:00
Jesse Hills e127268dac [core] Strip \\?\ prefix from sys.executable for PlatformIO subprocess (#16158) 2026-04-30 16:04:52 +12:00
J. Nick Koston 27116e1239 Merge remote-tracking branch 'origin/followup/hal-libretiny' into integration 2026-04-29 22:50:45 -05:00
J. Nick Koston 6097d5d7f8 Merge branch 'dev' into followup/hal-libretiny
# Conflicts:
#	esphome/core/hal/hal_libretiny.h
2026-04-29 22:43:47 -05:00
J. Nick Koston f0bffed3c0 [esp8266] Move HAL bodies into components/esp8266/hal.cpp + inline arch_init (#16112) 2026-04-30 15:42:17 +12:00
J. Nick Koston 663f856076 [api] Mark ZWaveProxyFrame and SerialProxyDataReceived as speed_optimized
Both messages are unconditionally exercised in the proxy hot paths
(every Z-Wave frame received from the controller; every UART read on a
configured serial proxy), so the modest flash cost of forcing -O2 on
their encode/calculate_size pays off on every device that has the proxy
enabled. Same treatment as InfraredRFReceiveEvent in the prior commit
and the other high-volume server-emitted messages.
2026-04-29 22:40:53 -05:00
J. Nick Koston 1d4dbf5476 [api] Mark InfraredRFReceiveEvent encode/calculate_size as speed_optimized
This message is emitted on every IR/RF receive event with a packed sint32
timings array (typically ~50-200 entries). The encode walks the vector
calling ProtoEncode::encode_sint32 per element; under -Os GCC does not
inline those helpers, so each element pays the call overhead.

Adding option (speed_optimized) = true to the proto definition causes
the codegen to emit __attribute__((optimize("O2"))) on encode and
calculate_size, matching what already exists on SensorStateResponse,
SubscribeLogsResponse, and BluetoothLERawAdvertisementsResponse — the
other high-volume server-emitted messages.

The CodSpeed Encode_InfraredRFReceiveEvent / CalculateSize_InfraredRFReceiveEvent
benchmarks added in the parent PR will quantify the improvement.
2026-04-29 22:40:52 -05:00
J. Nick Koston 8c0e5e9d9a [api] Address Copilot review on proxy benchmarks PR
- Make UARTFlushResult in the serial_proxy stub a scoped enum class with
  matching scoped enumerator return in flush_port(), so the stub
  signature lines up with the real esphome::uart::UARTFlushResult.
- Replace heap-leaking lazy-init in get_ir_timings_100() with a
  function-local static const std::vector populated by a regular helper
  function. Same lazy-init behavior, no leak in valgrind/ASan, no lambda
  IIFE.
- Emit field 6 (modulation = 1) in build_infrared_rf_transmit_wire() so
  the bytes match the documented field list and the decode benchmark
  also exercises the field-6 decode_varint path.
2026-04-29 22:40:45 -05:00
J. Nick Koston a0532d657f [api] Drop escape() helper and return-by-value APIBuffer in proxy decode benchmarks
Simplifies the decode benchmarks to mirror the encode pattern more
closely: no per-iteration asm volatile barrier, no return-by-value of
APIBuffer through encode_message_for_proxy. CodSpeed callgrind has been
crashing inside Decode_ZWaveProxyFrame and the previous setup was the
main thing it had that the (passing) Encode_ZWaveProxyFrame did not.
2026-04-29 21:46:32 -05:00
J. Nick Koston 483d294ef6 [api] Move proxy message benchmarks into bench_proto_proxy.cpp
Splitting these out from bench_proto_encode.cpp and bench_proto_decode.cpp
moves them to the end of the linker's static-init order. CodSpeed's
callgrind runner has been segfaulting immediately after measuring the
last existing decode benchmark (Decode_SwitchCommandRequest), and
isolating the new code into its own translation unit lets us see whether
the crash is triggered by one of the new benchmarks or by something
about the new USE_*_PROXY/USE_INFRARED/USE_RADIO_FREQUENCY defines
changing how api_pb2.cpp compiles.
2026-04-29 21:38:01 -05:00
J. Nick Koston f841de0664 [api] Avoid lambda IIFE and per-byte APIBuffer growth in proxy benchmarks
The InfraredRFReceiveEvent encode benchmark used a C++17 lambda IIFE
(`[]{...}()`) to seed a function-static vector, and the
InfraredRFTransmitRawTimingsRequest decode benchmark grew its APIBuffer
one byte at a time (~210 grow_() calls), each allocating a fresh
exact-fit buffer and memcpy'ing the prior contents. Both patterns are
fine under direct execution but appear to hit a CodSpeed/valgrind
edge case during the simulated benchmark run.

Switch to a plain heap-init pattern for the vector and build the wire
bytes into a stack array first, then resize+memcpy into the APIBuffer
once.
2026-04-29 21:25:46 -05:00
J. Nick Koston 4c027e87ba [api] Add encode/decode benchmarks for Z-Wave, IR/RF, and serial proxy messages
Mirrors the existing BluetoothLERawAdvertisementsResponse benchmarks for
the remaining proxy message families: ZWaveProxyFrame/ZWaveProxyRequest,
SerialProxyDataReceived/SerialProxyWriteRequest, and
InfraredRFReceiveEvent/InfraredRFTransmitRawTimingsRequest.

Adds minimal stub headers under tests/benchmarks/stubs/ for the
zwave_proxy, infrared, radio_frequency, and serial_proxy components so
api_connection.cpp compiles without dragging in their UART/RMT/BLE
hardware dependencies.
2026-04-29 21:07:39 -05:00
Jesse Hills 1a871e231d [ci] Use client-id for GitHub App token generation (#16155) 2026-04-30 13:09:37 +12:00
Jesse Hills 47765bd2d0 [ci] Correct version comment on create-github-app-token pin (#16156) 2026-04-30 13:08:56 +12:00
dependabot[bot] 8066325e0b Bump esphome/workflows/.github/workflows/lock.yml from 2026.4.0 to 2026.4.1 (#16143)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-30 12:52:25 +12:00
J. Nick Koston b8d24c9e49 [mcp23xxx_base] Reject unsupported interrupt_pin options (inverted, allow_other_uses) (#16149) 2026-04-30 11:14:07 +12:00
J. Nick Koston 9b1f5c59bb [core] Fix null deref in WarnIfComponentBlockingGuard for self-keyed scheduler timers (#16150) 2026-04-29 23:05:38 +00:00
J. Nick Koston b16aeb8c60 Merge remote-tracking branch 'upstream/core-delay-action-drop-internal-id' into integration 2026-04-29 17:54:04 -05:00
J. Nick Koston 534fd3f8da Merge remote-tracking branch 'upstream/mdns-config-hash' into integration 2026-04-29 17:53:59 -05:00
J. Nick Koston 6259935065 update host stub 2026-04-29 17:53:14 -05:00
J. Nick Koston 8dea8135fa [core] Drop unused DELAY_ACTION from InternalSchedulerID enum
Followup to #16129. DelayAction now keys its scheduler entry by
self-pointer (NameType::SELF_POINTER), so the DELAY_ACTION enum
value is no longer referenced anywhere.
2026-04-29 17:49:06 -05:00
Jonathan Swoboda e4b33fddf5 [esp32] Add ESP-IDF 6.0.1 platform entry (#16146) 2026-04-29 18:43:15 -04:00
J. Nick Koston 46dd1ced2c Merge remote-tracking branch 'upstream/fix-runtime-stats-null-component' into integration 2026-04-29 17:29:01 -05:00
J. Nick Koston 295e8563eb [core] Fix null deref in WarnIfComponentBlockingGuard for self-keyed timers
Self-keyed scheduler items (Scheduler::set_timeout(self, ...) and
set_interval(self, ...)) intentionally store component == nullptr. When
USE_RUNTIME_STATS is enabled, WarnIfComponentBlockingGuard::finish()
dereferenced component_ to call runtime_stats_.record_time(), causing a
load access fault on RISC-V (e.g. ESP32-C3) when these timers fire.

Skip the per-component bookkeeping when component_ is null, but still
accumulate into ComponentRuntimeStats::global_recorded_us so
Application::loop() overhead accounting (which subtracts scheduled
callback time from before_loop_tasks_) stays accurate.
2026-04-29 17:27:25 -05:00
J. Nick Koston 15bb89ed64 Merge remote-tracking branch 'upstream/mdns-config-hash' into integration 2026-04-29 16:45:16 -05:00
J. Nick Koston 5a59e99016 [mdns] Move setup helper to .cpp and use format_hex_to 2026-04-29 16:44:03 -05:00
J. Nick Koston 4b6b5fe275 Merge remote-tracking branch 'upstream/mdns-config-hash' into integration 2026-04-29 16:30:20 -05:00