Commit Graph
28397 Commits
Author SHA1 Message Date
J. Nick Koston acc15ff495 [api] Use custom deleter to fix incomplete-type error on macOS libc++
libc++ eagerly instantiates the unique_ptr<APIConnection> destructor
when std::array<std::unique_ptr<APIConnection>, N> is parsed, requiring
sizeof(APIConnection). api_server.h only forward-declares APIConnection
(via list_entities.h), so the destructor instantiation fails for any
translation unit that includes api_server.h without also including
api_connection.h first.

Wrap the unique_ptr in a custom deleter (APIConnectionDeleter) whose
operator() is defined out-of-line in api_server.cpp where APIConnection
is complete. The default_delete<APIConnection> path is never
instantiated, so libc++'s incomplete-type assertion is avoided.

GCC/libstdc++ already deferred this instantiation, so this only affects
macOS host-platform builds (used by integration tests).
2026-04-27 05:59:21 -05:00
J. Nick Koston 801cfe621b [ota] Fix clang-tidy goto-jumps-init and set error_code on timeout
Address two issues on the new data-loop timeout:

- clang-tidy ESP32 Arduino flagged "cannot jump from this goto statement to its label" at the existing pre-loop `goto error` sites because the new `uint32_t last_data_ms = millis();` declaration sat between them and the `error:` label, so the jumps would skip past its initialization. Move the declaration up next to the other handle_data_ locals (`total`, `last_progress`) and just assign it before the loop entry. The earlier gotos no longer cross an init.

- Copilot pointed out the new timeout `goto error` left `error_code` at `OTA_RESPONSE_OK` (set by `backend_->begin()`), so the device would write OK back to the uploader even though it was aborting. Set `error_code = ota::OTA_RESPONSE_ERROR_UNKNOWN` before the goto so the uploader receives a correct failure code.
2026-04-27 05:54:32 -05:00
J. Nick Koston 9089cc968e [climate] Use heat_cool_mode (not auto_mode) to enable HEAT_COOL in test fixture 2026-04-27 05:42:18 -05:00
J. Nick Koston 6770a6b87f [cover] Address Copilot review on integration test fixture and macro comments 2026-04-27 05:39:21 -05:00
J. Nick Koston fbafade78c [ota] Add wall-clock timeout to OTA data transfer loop
The main `while (total < ota_size)` loop in `ESPHomeOTAComponent::handle_data_()`
had a `// TODO: timeout check` and no wall-clock guard. If the uploader
side dropped the TCP connection without a FIN/RST being delivered to
the device (uploader process killed mid-transfer, NAT/router state
dropped, packet loss eating the RST), `recv_fn` would never see a
close and `s_err_fn` would never fire, so:

- `pcb_` stays non-null
- `rx_closed_` stays false
- `waiting_for_data_()` stays true forever
- the loop spins on `EWOULDBLOCK`, fed by `App.feed_wdt(); continue;`,
  indefinitely

LwIP TCP keepalive is not enabled on the OTA socket, and even when
enabled the default keepalive timer is on the order of hours, so the
device is effectively unresponsive until power cycle. This matches
the "device unresponsive until I do a hard power-reset" symptom in
issue #15953.

Track the timestamp of the last successful read and abort if no data
arrives for `OTA_SOCKET_TIMEOUT_DATA` (90s, same constant the handshake,
`readall_()`, and `writeall_()` already use). The timeout resets on
every successful read so a slow but live link does not false-trigger.
2026-04-27 05:29:40 -05:00
J. Nick Koston fdb183f772 [climate] Add auto_mode to thermostat fixture so HEAT_COOL is supported 2026-04-27 05:21:27 -05:00
J. Nick Koston 1ab588f10e [core] Drop redundant inline_continuation test (covered by test_continuation_actions) 2026-04-27 05:11:11 -05:00
J. Nick Koston 65b5615a31 [light] Use InitialStateHelper in DimRelativeAction integration test 2026-04-27 05:10:02 -05:00
J. Nick Koston 83f83c96a8 [light] Use InitialStateHelper in ToggleAction integration test 2026-04-27 05:09:27 -05:00
J. Nick Koston 497665f59f [climate] Use InitialStateHelper in integration test 2026-04-27 05:08:35 -05:00
J. Nick Koston a532f0adc9 [cover] Use InitialStateHelper in integration test 2026-04-27 05:08:08 -05:00
J. Nick Koston 23e64de392 [core] Add integration test for inline ContinuationAction 2026-04-27 05:07:14 -05:00
J. Nick Koston 2eac9f5121 [light] Add integration test for DimRelativeAction 2026-04-27 05:05:00 -05:00
J. Nick Koston e9ced485ea [light] Add integration test for ToggleAction 2026-04-27 05:04:28 -05:00
J. Nick Koston 66115c117a [climate] Add integration test for ControlAction 2026-04-27 05:03:57 -05:00
J. Nick Koston 3522eef8ee [cover] Add integration test for ControlAction/CoverPublishAction 2026-04-27 05:02:58 -05:00
J. Nick Koston feaa903056 [cover] Use bitmask template parameter for ControlAction/CoverPublishAction
Apply the bitmask pattern from LightControlAction (#16039) to
cover::ControlAction (3 fields: stop, position, tilt) and
cover::CoverPublishAction (3 fields: position, tilt, current_operation).
Unused fields are elided via [[no_unique_address]] and skipped at
compile time in play() via if constexpr.

Codegen for cover.control: and cover.template.publish: builds the
bitmask from the YAML keys present. CONF_STATE and CONF_POSITION
both map to the same position bit (they are mutually exclusive YAML
keys for the same C++ field).

Per-instance: 16-28 B depending on which fields are set, down from
~28 B baseline.
2026-04-27 04:58:35 -05:00
J. Nick Koston c30aa4aad4 [core] wakeable_delay: yield on already-woken fast path (ESP8266, RP2040)
When `g_main_loop_woke` is already set on entry to `wakeable_delay()`,
both the ESP8266 and RP2040 paths consume the flag and return without
yielding. That's safe in isolation, but if a caller loops on
`wakeable_delay()` (e.g. `LWIPRawImpl::wait_for_data_()` waiting for
SO_RCVTIMEO), and ISR sources (GPIO, timer, WiFi RX on ESP8266; alarm
/ async on RP2040) keep re-setting the flag between iterations, every
iteration takes the fast path and the loop never yields.

That can starve the SDK / async context, blocking actual TCP delivery
to our socket and causing OTA reads to time out on busy devices even
though there is data in flight. The PR that introduced
`wait_for_data_()` (#14675) relied on `wakeable_delay()` to yield on
every iteration; this restores that property on the fast path.

Adds `delay(0)` on ESP8266 and `yield()` on RP2040 to the fast path,
matching the yield behaviour those platforms already use for the
`ms == 0` poll case.
2026-04-27 04:55:43 -05:00
J. Nick Koston 1a02f7e41f [climate] Use bitmask template parameter for ControlAction unused fields
Apply the bitmask pattern from LightControlAction (#16039) to
climate::ControlAction. Parameterize on a uint16_t Fields bitmask
encoding which of the 10 templatable fields are configured. Unused
fields are elided via [[no_unique_address]] and skipped in play()
via if constexpr.

Also drop the unused `away` TEMPLATABLE_VALUE -- its YAML key is
cv.invalid("Use preset instead") and no caller of set_away exists
in the repo.

Per-instance: 20 B (mode only) to 28 B (mode + 2 temps), down from
~64-72 B baseline.
2026-04-27 04:36:44 -05:00
J. Nick Koston 35f043d671 Merge branch 'inline-continuation-actions' into integration 2026-04-26 22:51:19 -05:00
J. Nick Koston 83d879265e [core] Document add_then/add_else single-call precondition 2026-04-26 22:32:58 -05:00
J. Nick Koston e85de4d34a [core] Document add_then/add_else single-call precondition 2026-04-26 22:32:26 -05:00
J. Nick Koston 35163e5bca Merge branch 'inline-continuation-actions' into integration 2026-04-26 22:22:06 -05:00
J. Nick Koston f40eb9b780 [core] Inline ContinuationAction in IfAction/WhileAction/RepeatAction
Replace heap-allocated ContinuationAction/WhileLoopContinuation/
RepeatLoopContinuation instances with inline members, eliminating one
heap allocation per IfAction/WhileAction/RepeatAction at setup.

Each parent already needs exactly one continuation as the chain
terminator that hands control back. Heap-allocating it costs the
~16-byte object plus an ~8-byte heap header per instance, plus heap
fragmentation. Inlining moves the same 16 bytes from heap to BSS
and drops the heap header overhead.

Per-parent net change:
- BSS: +16 B (the inline continuation; +32 B for IfAction<true>)
- Heap: -24 B per heap allocation eliminated (16 B object + ~8 B header)
- Net RAM saved: ~8 B per simple parent, ~16 B for IfAction<true>
- Plus: one fewer heap allocation per parent at setup, less fragmentation

For IfAction<HasElse=false>, the else continuation is elided via
[[no_unique_address]] + an empty wrapper struct, so it costs 0 B.

Note: CI memory analysis only measures static RAM (BSS), not heap.
This change moves bytes from heap to BSS, so the report will show
BSS increasing while the actual heap savings (and fragmentation
reduction) are not directly visible.
2026-04-26 22:17:26 -05:00
J. Nick Koston 79b741b8dc [core] Combine entity register + configure_entity_ into one call (#16030) 2026-04-27 03:03:39 +00:00
tomaszduda23 112646a9c4 [zigbee] add router for nrf52 (#16034) 2026-04-26 23:02:09 -04:00
J. Nick Koston 63e7e70d18 Merge remote-tracking branch 'upstream-ssh/light-control-action-bitmask' into integration 2026-04-26 21:55:05 -05:00
J. Nick Koston 9a468eef8c [light] Undef LIGHT_CONTROL_FIELDS macro and assert <=16 fields 2026-04-26 21:54:33 -05:00
J. Nick Koston 2e096bb036 [core] Combine set_component_source_ + register_component_ into one call (#16029) 2026-04-26 21:54:15 -05:00
J. Nick Koston ad9f21cf04 Merge remote-tracking branch 'upstream-ssh/light-control-action-bitmask' into integration 2026-04-26 21:53:27 -05:00
J. Nick Koston 13250897ad [light] Add dim_relative test case with transition_length 2026-04-26 21:47:52 -05:00
J. Nick Koston dae15301e1 [light] Shorten Empty<Tag> comment to one line 2026-04-26 21:41:24 -05:00
J. Nick Koston 9f776eb517 [light] Shorten FIELDS comment 2026-04-26 21:40:52 -05:00
J. Nick Koston 0b8c04983e [light] Fold CONF_EFFECT into FIELDS table 2026-04-26 21:40:05 -05:00
J. Nick Koston 3f0c669b4c [light] Reduce LOC via X-macro pattern, drop unused tag/BIT args
Apply the X-macro pattern from #15132 so the field list is declared
once and expanded into setters, play(), and storage. Drop the unused
BIT_NAME and tag arguments — only (type, name, idx) is needed since
idx doubles as both the bit position and the Empty<> tag.

Net change vs upstream is +10 LOC instead of +64.
2026-04-26 21:37:55 -05:00
J. Nick Koston 19df452771 [light] Use bitmask template parameter for LightControlAction unused fields
Parameterize LightControlAction on a uint16_t Fields bitmask encoding
which of its 14 templatable fields are configured. Unset fields are
elided from the instance via [[no_unique_address]] and skipped at
compile time in play() via if constexpr.

Real-world configs typically use only 1-5 of the 14 fields. Codegen
computes the bitmask from the YAML and passes it as the leading
template argument, so each unique field combination produces its own
type with only the storage and play() branches it actually needs.

Measured on apollo-pump-1-5d9bdc.yaml (7 instances, 3 unique masks):
- state-only (2 instances): 72 B -> 20 B = 52 B saved each
- state+RGB (2 instances): 72 B -> 32 B = 40 B saved each
- state+brightness+RGB (3 instances): 72 B -> 36 B = 36 B saved each
- Total: ~292 B RAM saved
- Flash cost: ~30-70 B for 3 play() variants + vtables
2026-04-26 21:33:57 -05:00
J. Nick Koston 771e58eb00 Merge branch 'light-dim-relative-constexpr-template' into integration 2026-04-26 21:27:15 -05:00
J. Nick Koston 19e0d05219 [light] Use constexpr template parameter for DimRelativeAction transition_length
Parameterize DimRelativeAction on a HasTransitionLength bool, mirroring
the same trick applied to ToggleAction. When transition_length is not
configured in YAML, the TemplatableFn field is elided via
[[no_unique_address]] and LightCall::set_transition_length is skipped
via if constexpr.

Saves 4 bytes RAM per dim_relative action instance and shrinks play()
by ~48 bytes when transition_length is unused.
2026-04-26 21:23:26 -05:00
J. Nick Koston 14d61c67bc Merge remote-tracking branch 'upstream-ssh/light-toggle-action-constexpr-template' into integration 2026-04-26 21:17:54 -05:00
J. Nick Koston 0cebb9014f [light] Use constexpr template parameter for ToggleAction transition_length
Parameterize ToggleAction on a HasTransitionLength bool, mirroring the
IfAction<HasElse> pattern. When transition_length is not configured in
YAML, the TemplatableFn field is elided via [[no_unique_address]] and
LightCall::set_transition_length is skipped via if constexpr.

Saves 4 bytes RAM per toggle action instance and shrinks play() from
79 to 28 bytes when transition_length is unused.
2026-04-26 21:13:58 -05:00
Paul Lazarre 02674199da [esp32_ble_tracker] Address review: clarify gate comments
- 'all clients are idle' was accurate pre-patch; now this branch can run
  while CONNECTED/ESTABLISHED clients exist. Clarify which states actually
  block, and note that the inner coex revert has its own gate.
- Generalize the coex-revert rationale: 'lock's GATT Write Response' was
  Yale-specific; the bug is generic to any peer GATT response that needs
  to reach us while WiFi competes for the shared radio.
2026-04-26 19:32:45 -05:00
pre-commit-ci-lite[bot] debddac0fe [pre-commit.ci lite] apply automatic fixes 2026-04-27 00:26:25 +00:00
Paul Lazarre 8c0b0b1302 [esp32_ble_tracker] Hold COEX_PREFER_BT for the lifetime of any active connection
count_client_states_() only tracks transient states (CONNECTING, DISCOVERED,
DISCONNECTING). When a connection settles into CONNECTED/ESTABLISHED, all
counted states go to zero, and loop() reverts coex from PREFER_BT back to
BALANCE while the connection is still live. Sustained WiFi traffic then
crowds out the BT radio's ability to receive the peer's GATT response,
causing esp_ble_gattc_write_char_descr to time out at ~20s with a
host-synthesized status=0x85 (commonly reported as status=133).

Fix: count CONNECTED + ESTABLISHED clients as "active" and gate the coex
revert on the active count. Coex now stays at PREFER_BT for the entire
lifetime of any active connection and only returns to BALANCE when all
connections are torn down.

Validated end-to-end: aioesphomeapi-driven write_descriptor on a Yale
BETA211123 lock at LOG_LEVEL_VERBOSE (heavy WiFi traffic, the
bug-triggering condition) — pre-patch: 19s hang then status=133.
Post-patch: 228ms SUCCESS. Verified live on an M5 Atom Lite running as a
bluetooth_proxy with host BlueZ stopped (M5 as the only path): lock ops
succeed cleanly with zero status=133 errors. A multi-day production
soak window will be added to the PR body before merge.
2026-04-26 19:23:20 -05:00
J. Nick Koston 84aa308557 Merge remote-tracking branch 'upstream-ssh/analyze-memory-main-cpp-attribution' into integration 2026-04-26 15:12:28 -05:00
J. Nick Koston 82ba873ea7 [analyze_memory] Attribute main.cpp setup()/loop() to esphome core
The generated src/main.cpp.o was being skipped by _scan_source_symbols
(which only walked src/esphome/) and _source_file_to_component had no
rule for it. As a result setup()/loop() fell through to the
"app_framework" heuristic in const.py instead of being counted as
[esphome]core, both via the nm scan path and the linker map path.

Scan all of src/ and recognize .../src/main.cpp.o as core.
2026-04-26 15:10:48 -05:00
J. Nick Koston c32f6f629b [infrared,radio_frequency] queue_entity_register before setup_core_
The setup_<X>_core_ functions are decorated with @setup_entity, which calls
finalize_entity_strings() before returning. queue_entity_register() must run
before that so the combined App.register_<entity>(var, name, hash, packed)
emission is selected; otherwise finalize falls back to configure_entity_ and
no register call is emitted.

Reported by Copilot review on PR #16030.
2026-04-26 14:54:15 -05:00
J. Nick Koston 97e8417ac6 Merge branch 'combine-entity-register-clean' into integration 2026-04-26 14:38:54 -05:00
J. Nick Koston 61976ca117 Merge branch 'register-component-source-overload' into integration 2026-04-26 14:38:50 -05:00
J. Nick Koston 01e0cda3c0 [core] Combine entity register + configure_entity_ into one call
Add 4-arg overloads to App.register_<entity>() that call configure_entity_()
and push_back in a single function. Codegen defers App.register_<entity>(var)
emission until finalize_entity_strings(), which then emits a single combined
App.register_<entity>(var, name, hash, packed) call instead of the previous
two-statement pair (App.register_X(var); var->configure_entity_(...)).

Apollo R-PRO-1 (ESP32-S3 IDF, 122 components, 164 entities), same toolchain:
  text:     -1248 bytes
  main.cpp: -164 lines

No behavior change. configure_entity_ remains protected on EntityBase; the
Application class is now a friend so the new overloads can call it.
2026-04-26 14:37:37 -05:00
J. Nick Koston e1ca77e025 Update tests for combined register_component_ call 2026-04-26 14:19:43 -05:00