Replace floating-point comparison (value != 0.0f) with integer bit-pattern
check via a shared float_to_raw() helper in both encode_float() and
calc_float(). This avoids software float library calls on platforms without
hardware FPU (ESP8266, some LibreTiny chips) and ensures both functions
use identical zero-check logic so size calculation always matches encoding.
Callback::create() can store callables inline (no heap allocation)
when they fit in sizeof(void*) — i.e., a single [this] capture on
32-bit platforms. Many automation triggers captured [this, parent]
in their callback lambdas, doubling the capture size and forcing
heap allocation.
Store the parent/state pointer as a class member and capture only
[this] in the lambda. This trades 4 bytes of member storage for
avoiding a permanent heap allocation per callback registration.
Components changed: cover, valve, lock, fan, media_player, datetime,
display_menu_base, graphical_display_menu, esp32_improv, mqtt_fan.
The notify_state_deferred_ lambda captured [this, state, progress, error]
(16 bytes on 32-bit), exceeding std::function SBO and forcing a heap
allocation on every OTA progress update.
Pack the three values into a single uint32_t:
- state (8 bits) + error (8 bits) + progress as fixed-point (16 bits)
The lambda now captures [this, packed] (8 bytes), fitting in SBO.
Progress resolution of 0.01% is more than adequate for OTA reporting.
Guard against launching a second FreeRTOS update task while one is
already running. Without this, calling update() twice (e.g. from both
the polling interval and the initial check interval) could spawn two
concurrent tasks both writing to the same UpdateInfo.
With the single-defer structure from the race fix PR, clearing the
handle only needs to happen in one place — at the top of the deferred
lambda, before any other work.
Address review feedback:
- Move error_str out of public UpdateInfo into file-local TaskResult
- Add container.reset() before vTaskDelete (doesn't call destructors)
- Remove redundant has_progress/progress assignments after move
- Add comment on delete after std::move
Address Copilot review feedback:
- Call container->end() when status_code != HTTP_STATUS_OK (pre-existing
bug, but easy to fix while we're here)
- Update error_str comment to say "update check failure" not "fetch failure"
- Move state computation to main loop callback (avoids 2 extra lambda captures)
- Add error_str field to UpdateInfo so error info rides with the pointer
- Allocate UpdateInfo once at top of fetch_manifest_ (single ownership path)
- Lambda captures only 2 pointers (8 bytes), fits std::function SBO
Reduces ESP8266 flash delta from +480 to ~+288 bytes.