ESPHome 2026.5.0: Device Builder enters public beta

Release Overview
Section titled “Release Overview”The headline change in ESPHome 2026.5.0 is the public beta of the new ESPHome Device Builder, a from-scratch web app that replaces the legacy in-tree dashboard with a real configuration editor, a firmware job queue, multi-select bulk actions, labels and areas, out-of-sync detection, cross-config search, distributed builds, and a proper settings UI. On the firmware side, a fundamental rework of the main loop, scheduler, and task watchdog recovers measurable CPU and power on every platform, alongside a broad set of measured optimizations across the API, audio, and helper hot paths. A native ESP-IDF toolchain ships next to PlatformIO with ESP-IDF v6.0.1 readiness work, and the audio decoder pipeline is modernized on top of the new microMP3 / microWAV / microFLAC streaming libraries. OTA gains its most expansive feature set in years with partition-table and bootloader updates, web-server OTA, and soft-brick recovery, while ESP32-based Zigbee, the new Sendspin multi-room audio component family, a fresh radio_frequency entity type, expanded nRF52 / Zephyr platform work, and a substantial codebase correctness sweep round out the release.
Upgrade Checklist
Section titled “Upgrade Checklist”- If you use
modbus_controllerin server mode, migrate to the newmodbus_servercomponent and renameserver_registerstoregistersandserver_courtesy_responsetocourtesy_response - If you rely on more than 5 concurrent API connections on
esp32,bk72xx,rtl87xx, orln882x, setapi.max_connectionsexplicitly (the default dropped from 8 to 5) - If you play WAV files only via YAML actions to arbitrary URLs (not embedded, not set as the preferred pipeline format), add
audio: codecs: wav:so WAV decoding is still compiled in - If you use
codec_support_enabledon the speaker media player, drop it and use the pipelineformat:setting instead (format: NONEincludes all codecs,format: WAVmatches the oldnone/falsemode) - If you embed audio files larger than 5 MB into your speaker media player via the
files:block, compress them or pick a more efficient codec before upgrading - If you have multiple
ota: - platform: esphomeentries on different ports, consolidate to a single entry on one port - If you use
throttle_averagefilters with atime_periodlonger than 24 hours, lower it (the new schema cap is 24 h) - If your lambdas call
id(...).set_min_power(...),set_max_power(...), orset_zero_means_zero(...)on aFloatOutput, addmin_power: 0%(or any of the scaling keys) to one of youroutput:entries so the runtime setters stay compiled in - If your external component implements
ComponentIteratorand doesn’t handle media players, addbool on_media_player(media_player::MediaPlayer *obj) override { return true; }guarded by#ifdef USE_MEDIA_PLAYER - If your external component or lambda calls
OneWireBus::skip(), handle the newboolreturn value (falsemeans the reset/presence pulse failed; bail out instead of writing to a dead bus) - If your external component constructs
PollingComponent()with no argument and never callsset_update_interval(), pass an interval explicitly; the default constructor no longer polls at all - If your external component uses
esphome::RingBufferfromesphome/core/ring_buffer.h, switch toesphome::ring_buffer::RingBufferfromesphome/components/ring_buffer/ring_buffer.hand addAUTO_LOAD = ["ring_buffer"] - If your external component uses heap-allocating helpers like
str_lower_case,format_hex,format_mac_address_pretty,value_accuracy_to_string, orbase64_encode, includeesphome/core/alloc_helpers.hdirectly (thehelpers.hre-export is temporary) - If you maintain an external
climateplatform, replace the deprecatedset_supports_*/get_supports_*accessors withadd_feature_flags()/has_feature_flags()(notetwo_point_target_temperaturemaps toCLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE) - If you hold long-lived BLE connections via
ble_clientand notice WiFi throughput drops, this is the newesp32_ble_trackercoex behavior; open an issue with your config if it materially affects your workload - If your
ektf2232config still usesrts_pin, rename it toreset_pin(the friendly migration error has been removed)
Introducing the New ESPHome Device Builder (Beta)
Section titled “Introducing the New ESPHome Device Builder (Beta)”The new ESPHome Device Builder, announced at State of the Open Home 2026 and now in public beta, is a from-scratch replacement for the legacy in-tree dashboard. It lives in two new repos, device-builder (Python backend) and device-builder-frontend (web UI), and consumes ESPHome through stable public interfaces instead of reaching into internal modules. The work is driven by the Open Home Foundation Ecosystems department, which expanded significantly last year so OHF could deliver more to the community, working alongside the existing maintainer community. Most of the rewrite goes toward dashboard capabilities the existing user base has been blocked on for years (job queue, multi-select bulk actions, labels/areas, out-of-sync detection, cross-config search, distributed builds, real settings UI). Visual editing and pairing with hardware like the Apollo Automation ESPHome Starter Kit (ESK-1) make the same app a reasonable starting point for newcomers as well.
Capabilities the legacy dashboard didn’t offer:
- Visual component and automation builder alongside Monaco YAML, with a left-sidebar device navigator. Legacy was an Ace text editor over a
.yamlfile with no first-class component or automation objects. - Component catalog with dependency resolution and a per-board pin info viewer that maps GPIO capabilities and shows which component is using each pin.
- Firmware job queue with progress, history, and cancel for compile / install / clean. Legacy ran one operation at a time with no concurrency or history.
- Remote builder. One Device Builder instance can offload compile/install jobs to another over a peer-paired link (mDNS discovery, SHA-256 fingerprint confirmation, identity rotation, per-peer auto-route).
- Labels (colored, searchable, filterable), areas as a first-class field, friendly name as a separate editable field, device cloning, and multi-select bulk actions (update / delete / archive on an arbitrary subset of devices). Legacy’s only bulk action was an all-or-nothing “update all”.
- Out-of-sync detection with per-device badges for version, config-hash, and encryption-state mismatches. Legacy showed only “update available.”
- YAML diff view, cross-config YAML search with surrounding context, and a command palette (⌘K / Ctrl-K).
- Card and table views with configurable columns and faceted filters (platform / status / area / labels). Legacy was cards only.
- Real settings UI with light/dark/system theme, English / Français / Nederlands localization, editor layout, and remote-builder controls. Legacy exposed almost nothing in-UI.
- First-run Wi-Fi onboarding and USB-plug detection with a “set this up” prompt when a board is connected.
Existing features like Web Serial flashing and Take Control / Adopt carry over and are surfaced more prominently in the new install-method dialog, which adds server-side USB, Home Assistant host USB auto-detection, and web.esphome.io download as transports.
Supporting ESPHome-side plumbing landed in 2026.5.0: stable backend API surface (#16206) with documented helper contracts like write_file (#16290); stable WiFi capability helpers (#16300) and the new esphome.upload_targets module (#16346); the config-hash CLI command (#15548) plus an mDNS config_hash TXT record (#16145) that let Device Builder skip re-flash when the running config matches; schema visibility hints for the visual editor (#16267, #16276); tightened esphome rename (#16296); restored ProgressBar under --dashboard mode (#16357); downstream CI against PR Python code (#16214); and legacy-dashboard guardrails on contributor PRs (#16378).
The legacy dashboard remains the default in 2026.5.0. Home Assistant users can try Device Builder today by installing the ESPHome (beta) app (formerly add-on), where it is enabled by default. The stable and beta apps run side by side, or stable-app users can opt in via the Use new Device Builder Preview toggle in the add-on’s Configuration tab:

Feedback from beta testers shapes the next release, and we’re excited to already be seeing the first community pull requests landing on the device-builder and device-builder-frontend repos.
Main Loop and Watchdog Architecture Overhaul
Section titled “Main Loop and Watchdog Architecture Overhaul”This release lands one of the most consequential changes to ESPHome’s runtime in years: a fundamental rework of how the main loop, scheduler, and task watchdog interact. Led by @bdraco, with @rwrozelle contributing the configurable ESP32 watchdog timeout (#15908) that the new auto-scaling feed interval keys off and the OpenThread proof-of-concept that produced the 2.0 mA → 1.1 mA power-savings measurement, these changes recover meaningful CPU on every supported platform and finally make App.set_loop_interval() work the way the documentation has always described.
Main-loop cadence decoupled from the scheduler (#15792):
- Every component’s
loop()now actually runs at the configuredloop_interval_cadence (default ~62 Hz) instead of being silently pulled forward to ~128 Hz by unrelated scheduler activity. - Background-driven events (MQTT RX, USB RX, BLE, mWW, espnow, lwIP sockets, etc.) still wake their component within a single tick via
wake_loop_threadsafe(), even with multi-secondloop_interval_values for deep power-save configurations. App.set_loop_interval()now actually enables power savings. Independent testing on an OpenThread proof-of-concept measured average current drop from 2.0 mA to 1.1 mA on top of an earlier version of this work.
Watchdog feed rate corrected after a 1000x cost regression (#15846, #15984):
- The 3 ms feed throttle dated to ESPHome’s 2019 C++ port, when feeding the watchdog cost a hundred nanoseconds. The 2021 switch to ESP-IDF made each feed cost ~10-12 µs without the constant being revisited. The bug only surfaced under raised
loop_interval_, where 26% of CPU was being burned insidearch_feed_wdt(). - The idle-tick feed interval is now per-platform instead of a universal 3 ms: 1000 ms by default on ESP32 (1/5 of the configured watchdog timeout, scaling automatically with the new
esp32.watchdog_timeoutknob from #15908), 100 ms on ESP8266, 2000 ms on LibreTiny BK72xx, and 300 ms on the remaining platforms. Real ESP32+BT proxy measurements show thewdtbucket dropping from 46.5 ms to 21.6 ms per 60 s window with no loss of safety margin.
Loop-when-idle gated everywhere it makes sense (#15636, #15642, #15884 and many more): the esphome OTA component, status_led, bl0906, and others now disable their own loop() when idle and rely on wake hooks (socket-event callbacks, ISR-safe wake flags, etc.) to come back to life only when there is real work to do. OTA in particular ran every tick forever just to check whether a client had connected; on Xtensa each idle tick pulled a memw plus a volatile load, paid for the entire lifetime of every ESPHome device that exists.
Scheduler self-keyed timer API (#16127) lets small action and filter classes drop Component inheritance entirely. Follow-ups across #16129, #16131, #16132, and #16191 migrate DelayAction and the binary_sensor / sensor filter family (DelayedOnFilter, DelayedOffFilter, DebounceFilter, HeartbeatFilter, SettleFilter, AutorepeatFilter, and others), saving ~8 bytes per instance baseline (and ~32 bytes more when runtime_stats: is enabled).
Performance Optimizations
Section titled “Performance Optimizations”Beyond the structural changes above, this release contains an unusual concentration of measured micro-optimizations across the codebase, particularly on the API and audio paths. Led by @bdraco and @kahrendt, with @swoboda1337 contributing the CallbackManager copy reduction (#16093) and a sweep of performance-unnecessary-copy-initialization fixes (#16101).
Cross-platform millis() overhaul. A coordinated rewrite hits every supported runtime. ESP8266 (#15662) drops from 3348 ns/call to 1077 ns/call (2.7x) by wrapping Arduino’s millis() at link time with a 32-bit accumulator, replacing four 64-bit multiplies routed through the LX106’s software __umulsidi3 helper. ESP32 (#15661) switches to xTaskGetTickCount() directly when the FreeRTOS tick rate is 1 kHz; LibreTiny (#15918) inlines the same xTaskGetTickCount() fast path; the host platform (#15994) replaces floating-point math with integer arithmetic; and Millis64Impl::compute() is force-inlined on single-threaded platforms (#15684). On a typical 10-component ESP8266 device, runtime stats showed main_loop active_total drop by 41 ms per minute of CPU, roughly 7% of the scheduler+overhead budget reclaimed for actual work.
BLE advertisement encode is 20-33% faster (#15988). A new (mac_address) proto field option unrolls the varint encoder for 48-bit MAC addresses into a 7-byte fast path. CodSpeed measured the CalculateSize_BLERawAdvs12 benchmark improving by 33% and the full CalcAndEncode_BLERawAdvs12 benchmark by 22.5%. On real ESP32 hardware, encode dropped 27.6% (11647 ns/op to 8430 ns/op) on a 12-advertisement batch.
API socket fast-path tightened (#15996, #15888, #15889). The Xtensa memw instruction in the per-socket ready check is now hoisted to once per main-loop iteration; api_is_connected() is inlined and reduced to a single byte load; the proxy message families (Z-Wave, IR/RF, serial) are now marked speed_optimized and have CodSpeed coverage (#16157, #16159).
Scheduler fast paths force-inlined (#15683, #15685, #15686, #15947). cleanup_(), process_to_add(), and process_defer_queue_() are inlined at the call site; the multi-threaded-no-atomics path now uses lock-free __atomic builtins.
Defer queue: don’t sleep while non-empty (#15968). The scheduler now skips its select/sleep when the defer queue still has items pending, eliminating a per-tick latency spike on action chains that defer between steps (script.execute, delay, etc.). Latency-sensitive automations no longer pay an extra select round-trip per deferred step.
HAL trivial dispatches inlined (#15977, #16111-#16116, #16183). The HAL split moves bodies into per-platform hal.cpp files and inlines the trivial wrappers (millis(), micros(), feed_wdt(), etc.) at the call site, removing a per-call function-call overhead that compounded across every component’s main-loop work.
zwave_proxy and bluetooth_proxy loop() fast paths inlined. On zwave_proxy (#15887) the response_handler_ and process_uart_ hot-path branches are now inlined at the loop() call site. On bluetooth_proxy, a partial revert of an earlier loop()→set_interval migration (#15992) restores tighter scheduling for proxy traffic, and a redundant remote_bda_ write in the connect handler (#16000) is gone. Bluetooth proxy is one of the most-deployed ESPHome configurations, so these compound across thousands of devices.
mDNS update polling event-driven on ESP8266 and RP2040 (#15961). MDNS.update() is now driven by IP-state events on ESP8266 and RP2040 instead of being polled every main-loop tick. Idle devices stop touching the mDNS state machine entirely.
i2s_audio software volume control is 45% cheaper (#16278). The new Q31 scaling factor uses Xtensa’s mulsh instruction (30 bits of precision) and a precomputed scalar generated only when volume changes. Speaker-task CPU usage at 48 kHz stereo drops from 7.8% to 4.3%.
value_accuracy_to_buf got a non-snprintf fast path (#15596). Sensor value formatting now uses a direct integer-to-decimal conversion for the common case (accuracy 0-3 with finite, in-range values), with snprintf kept as the fallback for NaN, infinity, larger accuracies, and out-of-range values. This is a major win for web_server, which formats every sensor state into JSON on each SSE push and on every REST request; high-update-rate dashboards and devices with many sensors see noticeably lower CPU per push, with the largest gains on ESP8266 where snprintf carries the most overhead.
Light, callback, and helper hot paths all saw improvements: LightCall flag accessors are force-inlined (#15729), format_hex_internal was rewritten to avoid snprintf (#15594), and CallbackManager call paths reduce copies (#16093).
Memory Footprint Reductions
Section titled “Memory Footprint Reductions”A parallel effort by @bdraco and @kahrendt, with @schdro contributing the static loop-task allocation, focused on shrinking the per-instance and static-RAM cost of common components.
APIServer client list moved to a compile-time array (#15889) eliminates the persistent heap-held vector that grew via doubling reallocations. Measured -92 B flash on ESP32-S3, -152 B flash on ESP8266, plus zero heap fragmentation. At one client connected (the typical Home Assistant deployment), net RAM is -4 B. The new compile-time MAX_API_CONNECTIONS constant lets the array size and accept-time cap derive from one source.
FloatOutput power scaling gated (#15998) saves 12 bytes per output instance plus ~248 bytes flash, applied across every PWM channel, DAC channel, LEDC output, and dimmer-chip channel. On a 5-channel H801 RGBWW LED Controller this measured at -64 B static RAM and -248 B flash.
Per-instance action fields folded into stateless lambdas: every YAML automation that calls a light.control / light.toggle / light.dim_relative (#16037, #16038, #16039, #16118), climate.control (#16044), cover.control / cover.template.publish (#16046), fan.turn_on (#16122), or valve.control (#16123) action previously stored every optional parameter (transition lengths, RGB/brightness targets, modes, position, direction, etc.) as fields on the Action instance, costing 60-120 bytes of RAM per action instance depending on the action type. These are now packed into a single stateless lambda captured at compile time, so unused parameters cost nothing per action instance and every action drops to 20 bytes or less per instance. A config with 20 light.* automations reclaims well over a kilobyte of RAM, and configurations with many such automations see the largest savings.
ThrottleAverageFilter packed (#16169) drops from 28 B to 24 B per instance by packing the have_nan_ flag alongside the 31-bit n_ field in a single 32-bit word.
LD24xx sensors and template restorers inlined (#15676, #15883) eliminate heap allocations for small fixed-purpose objects.
Loop task uses static allocation (#15659): ~152 bytes of heap metadata reclaimed by moving the ESP32 loop task TCB and stack into .bss instead of being allocated at boot from the heap.
Ring buffer can target internal memory (#16187). A new memory-preference parameter on RingBuffer::create() allows audio paths to skip the slow ESP32 PSRAM cache when small buffers benefit from the faster internal SRAM.
Scheduler pool replaced with intrusive freelist (#16172). The previous std::vector-backed SchedulerItem pool grew via doubling reallocations and hung on to its peak capacity for the lifetime of the device. The replacement is an unbounded intrusive freelist that recycles freed items in O(1) without ever resizing or fragmenting an external container. Scheduler-heavy configurations stop carrying high-watermark vector capacity around as dead RAM.
safe_mode and rtttl callback storage gated (#16002, #16003). on_safe_mode callbacks now use a StaticCallbackManager, and rtttl’s on_finished_playback callback storage is gated behind a #define that only activates when the YAML actually subscribes. Both are tiny per-instance wins, but safe_mode is on essentially every device and rtttl is on every speaker setup that uses RTTTL playback.
light validate_ clamp shrunk (#15728). The unit-range clamp helper used by every light component dropped its branch count and code size, with the speed-up showing up across PWM, RGB, RGBW, RGBWW, monochromatic, and addressable light platforms.
dsmr no longer allocates during parsing (#15875). The DSMR rewrite eliminates dynamic allocations in the parse path entirely and decrypts in place, saving roughly 1500 bytes of runtime allocation per telegram. See Other Notable Features for the security-fix and OBIS-sensor side of the same PR.
Native ESP-IDF Toolchain Support
Section titled “Native ESP-IDF Toolchain Support”@diorcety delivered a substantial new build pathway in #14678: ESPHome can now build firmware directly with the native ESP-IDF toolchain via idf.py, alongside the existing PlatformIO build path. This is independent of framework.type, which still selects the runtime framework (arduino vs esp-idf); the new toolchain key selects the build system used to compile it.
Opt in by setting toolchain under the esp32: block:
esp32: board: esp32dev toolchain: esp-idf # or 'platformio' (default) framework: type: esp-idfThe same selection is available on the command line and overrides the YAML value for one-off builds:
esphome --toolchain esp-idf compile my-device.yamlPrecedence is --toolchain (CLI), then esp32.toolchain (YAML), then platformio (default toolchain).
Key Benefits:
- Automatic ESP-IDF installation on first run with
toolchain: esp-idf, into<data_dir>/idf/(or$ESPHOME_ESP_IDF_PREFIXwhen set). ESPHome downloads the matching ESP-IDF release plus its Python environment, then builds throughidf.pydirectly. - Automatic conversion of PlatformIO library entries into ESP-IDF components (including the patches required), so projects that rely on
lib_depsfrom the PlatformIO ecosystem keep working under the native toolchain. - CI workflow added to compile components against the native toolchain on every PR so the path stays building as the codebase evolves.
The default toolchain remains platformio in 2026.5.0, so existing projects keep building exactly as they did before. The native path is opt-in for power users and contributors who want to work against the unmodified upstream IDF.
ESP-IDF v6.0.1 readiness lands across a chain of supporting PRs, primarily by @swoboda1337 with @diorcety and @luar123 contributing the dependency bumps: a new ESP-IDF 6.0.1 platform entry (#16146), an esp_wireguard bump to 0.4.5 for v6 compatibility (#15804), newlib compatibility for the Zigbee SDK on IDF 6 (#16174) plus init-order and missing-field warning fixes on native ESP-IDF (#16389), relaxed -Werror=reorder and -Werror=maybe-uninitialized for managed components built under IDF 6 (#16392), and a PlatformIO-style RAM/Flash summary printed after native ESP-IDF builds for parity with the existing flow (#16394).
Audio Stack Modernization
Section titled “Audio Stack Modernization”@kahrendt led a comprehensive rewrite of the audio decoder pipeline across more than a dozen PRs, aligning ESPHome with a new family of streaming codec libraries published by the esphome-libs organization.
New microDecoder family of libraries:
microMP3(#16236),microWAV(#16251),microFLAC(#16279) replace the older Helix/esp-audio-libs decoders. Every codec now either streams from its source or buffers internally, which eliminates amemmoveoperation on the transfer buffer that, for MP3, used more CPU than the decoding itself.- The unified
microDecoderlibrary (#15679, #16237) handles HTTP source reads, decoding, and threading internally.audio_fileand the newaudio_httpmedia source (#15741) both build on it. esp-audio-libswas bumped to v3.0.0 (#16263), keeping the resampler and optimized gain helpers but dropping the codec decoders to reduce compile time.
New audio_http media source (#15741) plays audio from arbitrary HTTP URLs and serves as a replacement for prior workarounds. 48 kHz stereo MP3 now decodes with ~40% less CPU on an ESP32-S3 thanks to the eliminated staging buffer and memmove.
Advanced codec configuration (#16166) exposes per-codec options under audio.codecs.* so users can pick memory locations (internal vs PSRAM), enable specific codecs additively, and tune options like Opus’s pseudostack size. Users of slower ESP32s (no fast PSRAM) can now configure decoder buffers to live in internal memory and reduce stuttering at the start of playback.
RingBuffer made a first-class component (#16298) by moving the core ring buffer into a dedicated ring_buffer helper component. External component authors can now iterate on it via external_components; the deprecation period for the old esphome/core/ring_buffer.h location is 6 months. A new RingBufferAudioSource (#16314, #16315, #16316) consolidates the speaker and mixer audio-source plumbing.
Users with audio files referenced only via YAML actions (rather than embedded or set as the preferred format) may need to add audio.codecs.wav: to keep WAV decoding compiled in; see the Breaking Changes section for migration details.
OTA Platform Enhancements
Section titled “OTA Platform Enhancements”OTA gained the broadest set of capabilities it has had in any single release, led by @Mat931.
Partition table updates (#15780) make it possible to convert devices from other firmwares (such as Tasmota) to ESPHome over the air. The updater verifies the new partition table, runs sanity checks, and only commits if it can prove the device will boot. Setting allow_partition_access: true under ota: platform: esphome enables the workflow; the docs walk through the Tasmota conversion step by step.
Bootloader updates (#16238) build on the new extended OTA protocol (#16164) and let esphome upload --bootloader push a fresh bootloader image.
Soft-brick recovery via factory partition (#16339). When OTA is impossible because the alternate app partition is the wrong type but contains a valid app (for example, a Tasmota safeboot image), safe_mode can now boot into the factory partition so the device can be re-flashed instead of requiring a USB cable.
Web server OTA platform (#16207). A new --ota-platform {esphome,web_server} flag on esphome upload and esphome run lets users force the HTTP-based web_server OTA path, which is auto-selected when only platform: web_server is configured. The native API path remains the default because it uses challenge-response auth with hashed nonces (the OTA password is never on the wire), while web_server uses HTTP Basic auth.
Host platform OTA backend (#16304) implements working OTA for the host target via execv, unlocking integration-test coverage of one of the highest-risk regression surfaces in ESPHome. Tests can now exercise the full native OTA wire protocol (handshake, MD5, chunked transfer, automation triggers, reboot) on every CI run without needing real hardware.
Better OTA error messages (#16327) and the use of WatchdogManager during ESP32 OTA writes (#16138) round out the package.
Faster Configuration Validation and CLI Startup
Section titled “Faster Configuration Validation and CLI Startup”Four related changes by @bdraco make esphome compile, esphome upload, and especially esphome logs noticeably faster on projects with remote dependencies, and shave wall-clock time off every CLI invocation.
Deferred heavy module-scope imports (#15955). esphome.__main__, esphome.loader, and esphome.config no longer pull voluptuous, codegen, and the component registry at import time; the heavy imports happen lazily inside the codepaths that actually need them. Every esphome CLI invocation, including esphome version, esphome config, and the dashboard’s per-request shells, starts faster, and the dashboard’s import-time regression is now guarded by CI.
Skip external file refresh on esphome logs (#16016). The skip_external_update flag is unified into a single CORE.skip_external_update source of truth and external_files.download_content() now honors it. For a Home Assistant Voice PE config (16+ remote audio files, several micro_wake_word models), the validation phase no longer spends ~20 sequential HTTP HEAD requests before logs can start streaming.
Parallel external_files downloads (#16021) fan out per-file checks across an 8-worker thread pool. Wall time drops from sum(latency) to roughly max(latency) when the cache is warm.
Cached validated configs (#16381). esphome compile now writes the fully validated config to disk; upload and logs reload it instead of re-running read_config() from scratch. The cache is regenerated by every compile, so a follow-up upload to a freshly compiled binary skips the entire validation pipeline. No flag, no opt-in.
Sendspin Synchronized Multi-Room Audio
Section titled “Sendspin Synchronized Multi-Room Audio”@kahrendt built a complete new component family for Sendspin, a multi-room synchronized audio protocol, across a chain of PRs:
- Hub component (#15924) provides the basic connection and group state distribution.
- Controller role and switch action (#15929) lets devices move between active groups.
- Group media player platform (#15948) controls playback for a whole group (volume, transport, repeat, shuffle) without producing audio itself.
- Media source platform (#15950) plays synchronized audio, with fixed and runtime-tunable delay compensation for downstream DAC/amp latency.
- Metadata text and numeric sensors (#15969, #15971) report title/artist/album plus a polling track-progress sensor designed to drive displays.
- Stutter reduction via sendspin-cpp v0.4.0 (#16178) marks decoder paths hot, allows player buffers in internal memory, and raises the player task priority above the websocket server. Combined with the new audio codec configuration, this enables real-time stereo Opus playback on a plain ESP32.
Radio Frequency Entity Type
Section titled “Radio Frequency Entity Type”@kbx81 introduced a new top-level radio_frequency entity type (#15556) for representing RF transceivers in Home Assistant, alongside an ir_rf_proxy platform (#15744) that extends the existing infrared proxy with RF capability advertising (tunable frequency range, supported modulations).
A driver-agnostic on_control trigger (#16368) lets any RF front-end chip (CC1101, RFM69, SX127x, custom externals) integrate with radio_frequency entities through YAML triggers alone, with no custom C++ wiring required. Chip-state turnaround for transmit/receive is handled through remote_transmitter’s existing on_transmit / on_complete triggers, keeping radio_frequency itself driver-agnostic. The implementation shares wire messages with the IR proxy to conserve protobuf message IDs.
LVGL Improvements
Section titled “LVGL Improvements”@clydebarrow continued landing LVGL refinements throughout the release:
- Flexible grid layouts (#16041) accept new shorthands like
3x(3 rows, columns auto-derived) andx4(4 columns, rows derived from widget count). Eithergrid_rowsorgrid_columnscan now be a single integer that expands to that many equalFR(1)cells. - Checked-state binary sensors (#16073) report toggle widget state directly, not just press state.
- Percentage line points (#16209) make resolution-independent line widgets straightforward.
- Touch coordinates in event lambdas (#16272).
on_pressed,on_pressing, andon_releasenow receive an additionalpointparameter with object-relative coordinates. on_updatetrigger andtrigger:option (#16312) distinguish programmatic value changes from user interactions for numbers and sensors.
The standalone mapping component also gained default values and metadata for cross-component validation (#15861).
Zigbee Expanded to ESP32 H2 and C6
Section titled “Zigbee Expanded to ESP32 H2 and C6”@luar123 brought ESP32-based Zigbee support to ESPHome in #11553, porting the standalone zigbee_esphome external component into core. The implementation runs on the radio-equipped ESP32-C6 and ESP32-H2 variants and publishes ESPHome binary_sensor entities as Zigbee binary_input clusters, recognized automatically by ZHA and zigbee2mqtt. Zigbee router and end devices are supported.
Follow-ups in this release expand the feature set:
- Sensors over Zigbee (#16026) exposes ESPHome
sensorentities through the analog Zigbee data model. on_jointrigger (#16060) fires when the device joins or rejoins the Zigbee network, with a bool indicating which case.power_sourceoption (#16062) lets devices advertise their power source (battery vs mains).
nRF52 and Zephyr Platform Improvements
Section titled “nRF52 and Zephyr Platform Improvements”@tomaszduda23 continued landing the work that turns nRF52/Zephyr into a first-class ESPHome target, with substantial follow-ups across deep sleep, Zigbee, OTA, and native builds:
- Deep sleep with Zigbee wakeup (#13950) lets battery-powered nRF52 Zigbee devices sleep between events while still being woken by the radio.
- nRF52 Zigbee router (#16034) extends the existing Zigbee end-device support to router devices.
- Loop-wake primitives implemented (#16032):
wake_loop_threadsafe()andwakeable_delay()now have real nRF52 implementations, so components that depend on these primitives (sockets, BLE, etc.) work the same way they do on ESP32. - Optional reset pin for DFU (#11684) makes the nRESET pin optional during DFU entry; less reliable, but feasible on boards that don’t break the pin out.
zephyr_ble_servernumeric comparison pairing (#14400) adds theon_numeric_comparison_requesttrigger for secure BLE pairing flows.- Bootloader reserve area (#16204) carves out a dedicated flash region so bootloader updates don’t collide with app flash.
- Crash logging on Zephyr (#16203, #16330) gives the logger a chance to print before reset, with a separate fix for a long-standing logger crash on Zephyr.
- OTA-safe watchdog feeding (#16218) feeds the watchdog early during OTA so the device doesn’t roll back mid-update.
west updateprogress messaging (#16321) makes it clear when Zephyr is fetching modules rather than hanging.- Native build preparation (#16193) and
run_compilehook for external components (#16179) set up the path for the nRF52/Zephyr toolchain to follow the same native build model as the new ESP-IDF pathway above. - CI coverage (#16188) adds nRF52 component tests so Zephyr-only changes actually get exercised in CI, and the Zephyr main loop drops a redundant
yield()(#15694).
@rwrozelle landed two OpenThread fixes that ride along on the same platform pathway: a coroutine-with-priority COMMUNICATION profile (#16318) and removal of a stale freertos/portmacro.h include that broke builds against newer toolchains (#16338).
New Hardware and Display Support
Section titled “New Hardware and Display Support”- ESP32-P4 USB High Speed (#14584) by @p1ngb4ck: a new
max_packet_sizeoption onusb_hostlets ESP32-P4 use 512-byte USB transfers instead of being fragmented to 64-byte Full Speed packets. - Configurable ESP32 watchdog timeout (#15908) by @rwrozelle:
esp32.watchdog_timeoutaccepts 5-60 seconds for power-managed configurations. esp32_blePSRAM allocation (#15644) by @edwardtfn: ause_psram: trueoption directs Bluedroid to allocate from SPIRAM, freeing approximately 40 kB of internal RAM on PSRAM-equipped ESP32 boards.- SPDIF speaker output (#8065) by @johnboiles: a new I2S-based SPDIF speaker platform sends digital audio to optical receivers via any GPIO pin.
modbus_serversplit (#15509) by @exciton: a new dedicatedmodbus_servercomponent, with flash savings of roughly 60% over the old wedged-in server mode (1.8 KB vs 4.5 KB) and 40% off the client-modemodbus_controller(3.9 KB vs 6.4 KB).- New display variants: epaper SSD1683 + Goodisplay GDEY042T81 (#13910), Waveshare 3.97” e-paper (#15466), Waveshare ESP32-C6 LCD 1.47 (#15776), Sunton ESP32-2424S012 (#15812), Sunton 5”/7” mipi_rgb displays (#15858), Seeed reTerminal D1001 DSI display (#15867).
- WiFi
phy_modefor ESP8266 (#16055) lets users pin the radio to11B,11G, or11Nfrom YAML to work around routers that misbehave with ESP8266 802.11n associations.
BLE Reliability Fix for Bluetooth Proxies
Section titled “BLE Reliability Fix for Bluetooth Proxies”@plazarre tracked down a long-standing status=0x85 (133) error class in #16036 that hit bluetooth_proxy users with sustained WiFi traffic, most visibly Yale/August lock owners. The fix tracks CONNECTED and ESTABLISHED clients (not just transient connecting states) and holds ESP_COEX_PREFER_BT for the lifetime of any active connection. Same lock, same firmware, before vs after the patch:
- Heavy WiFi traffic:
status=133timeout after 19 s becomesstatus=0success in 228 ms. - Production validation against a Yale BETA211123 lock with host BlueZ disabled: clean unlock via the patched proxy.
Other Notable Features
Section titled “Other Notable Features”- Voice assistant second audio channel (#16265) by @synesthesiam: a
MULTI_CHANNEL_AUDIOfeature flag and a second microphone source let voice-pipeline stages receive separately optimized audio. config-hashCLI command (#15548) by @ccutrer: determines whether a re-flash is required by comparing a config hash to what is currently on the device.round_to_significant_digitsfilter (#11157) by @gapple: useful for ambient-light style sensors that span six orders of magnitude.- AC dimmer zero-crossing interrupt type (#15862) by @aselafernando: user-configurable interrupt edge for zero-cross detection.
lockopen states (#15120) by @egormanga: thelockentity gainedOPENINGandOPENstates.- SX126x cold sleep (#16144) by @swoboda1337: a new
cold:option on the sleep action reaches ~0.6 µA (roughly 1000x lower than warm sleep) for ESP32-deep-sleep-paired use. - DSMR rewrite (#15875) by @PolarGoose removes the
Crypto-no-arduinodependency, eliminates dynamic allocations during parsing, in-place decrypts (saving ~1500 bytes), fixes a potential event-loop hang, fixes a missing GCM tag verification (a security vulnerability), and adds several missing OBIS sensors. - Climate / water heater temperature unit API (#15815) by @jhenkens: protobuf-level support for native Fahrenheit operation; the C++ implementation is staged for a follow-up release.
- Mitsubishi CN105 remote temperature API (#15558) by @crnjan: override the AC unit’s internal sensor with a value from an external sensor.
- nRF52 optional reset pin (#11684) by @tomaszduda23: DFU entry without the nRESET pin (less reliable but feasible).
Codebase Correctness and Developer Tooling
Section titled “Codebase Correctness and Developer Tooling”A substantial sweep of correctness work landed alongside the features, led by @swoboda1337 (59 PRs this release) with help from @bdraco on the HAL split and CodSpeed coverage, @jpeletier on substitution error messages, and @Komzpa on reproducible builds.
- clang-tidy 22.1 (#16078) replaces the years-old 18.1.8, surfacing latent issues across components.
- Nested namespace concatenation (#16294-#16307) walks the entire component tree (a→c, d→h, i→m, n→r, s, t→z plus tests) collapsing
namespace foo { namespace bar { ... } }into the modernnamespace foo::bar { ... }and enabling the check to prevent regression. bugprone-unchecked-optional-accessfixes (#16102, #16103, #16107, #16121, #16124) eliminate a class of uncheckedstd::optionalaccess bugs acrosssprinkler,pn532,feedback,time(CronTrigger),tormatic, andhaier.- CodSpeed coverage expansion (#15593, #15688, #15696, #15995, #16157, #16402) adds benchmarks for hot helper functions, SubscribeLogsResponse encode, Z-Wave/IR/RF/serial proxy messages, and the compiled-config cache fast path. The benchmark target now matches firmware optimization level (
-Os). - HAL split per platform (#15977, #15978, #16111-#16116, #16183) moves HAL bodies into per-platform files under
components/<platform>/hal.cppand inlines trivial dispatches. - Substitution error messages (#15874) by @jpeletier: undefined-variable errors now show a full include stack trace (
Included from packages[3] in my_project.yaml 11:2) instead of a flatpackages->3->packages->net->wifi->ssidpath. - Reproducible ESP-IDF builds (#16008, #16053) make ESPHome builds bit-for-bit reproducible.
Thank You, Contributors
Section titled “Thank You, Contributors”This release includes 395 pull requests from over 50 contributors. A huge thank you to everyone who made 2026.5.0 possible:
- @swoboda1337 - 59 PRs including the seven-part nested-namespace clang-tidy sweep across the entire component tree, a clang-tidy 22 upgrade with the resulting correctness fixes, the SX126x cold sleep option, and substantial native ESP-IDF build polish
- @kahrendt - 37 PRs including the audio stack rewrite on top of the new microMP3, microWAV, microFLAC, and microDecoder libraries, the
ring_bufferhelper component, the newaudio_httpmedia source, and the new Sendspin multi-room synchronized audio component family - @clydebarrow - 17 PRs including LVGL layout, gradient, percentage-line-point, touch-point, and
on_updatetrigger improvements, new mipi_spi and mipi_dsi display models, and ESPNOW method-visibility cleanup - @jesserockz - 16 PRs including least-privilege workflow tokens, CI tooling tightening, and various build/release infrastructure improvements
- @tomaszduda23 - 13 PRs including nRF52 + Zephyr native-build preparation, deep sleep with Zigbee wakeup, an nRF52 Zigbee router, and Zephyr OTA/watchdog/logger fixes
- @Mat931 - 10 PRs including OTA partition-table and bootloader updates, the extended OTA protocol,
safe_modesoft-brick recovery via the factory partition, and WatchdogManager fixes - @kbx81 - 7 PRs including the new experimental
radio_frequencyentity type, their_rf_proxyRF extension and driver-agnosticon_controltrigger, and i2s_audio refactoring - @luar123 - 5 PRs expanding ESP32-based Zigbee support with binary sensors, sensors, the
on_jointrigger, and thepower_sourceoption - @crnjan - 5 PRs including
mitsubishi_cn105remote temperature API, half-degree setpoints, unified timeout handling, and vane / wide-vane support - @diorcety - 4 PRs including the native ESP-IDF toolchain support and ESP-IDF v6 readiness work
- @rwrozelle - 3 PRs including the configurable ESP32
watchdog_timeoutand OpenThread improvements - @Komzpa - 3 PRs making ESP-IDF and Arduino builds bit-for-bit reproducible
- @egormanga - 2 PRs adding
OPENINGandOPENstates to thelockcomponent and its API protocol - @guillempages - 2 PRs including the
tm1637brightness setter and Sunton mipi_rgb display definitions - @edwardtfn - 2 PRs including the
esp32_bleuse_psramoption and anextionTFT upload fix - @jpeletier - 2 PRs including the include-stack-trace substitution error messages and a remote-packages
varssubstitution fix - @PolarGoose - 2 PRs rewriting
dsmrfor performance, dropping theCrypto-no-arduinodependency, and adding missing sensors and a GCM tag verification fix - @johnboiles - the new SPDIF speaker output platform
- @exciton - the new
modbus_servercomponent split out ofmodbus_controller - @plazarre - the
esp32_ble_trackercoex fix that resolves long-standingstatus=133GATT failures onbluetooth_proxysetups - @synesthesiam - the second audio channel for
voice_assistant
Also thank you to @bdraco, @gapple, @lboue, @balloob, @Eelviny, @CircuitSetup, @ximex, @p1ngb4ck, @mikeytdisco, @angelnu, @puddly, @rwalker777, @TimoPtr, @Farmer-shin, @ccutrer, @SaVi456, @DidierA, @schdro, @rishabmehta7, @yvesf, @Bl00d-B0b, @jhenkens, @ruimarinho, @aselafernando, @dbl-0, @GelidusResearch, @oarcher, @RubenKelevra, @dmik, @ssieb, and @ggalt for their contributions, and to everyone who reported issues, tested pre-releases, and helped in the community.
Breaking Changes
Section titled “Breaking Changes”Component Changes
Section titled “Component Changes”- Modbus: server mode has been split out of
modbus_controllerinto a newmodbus_servercomponent. Moveserver_registerstoregistersandserver_courtesy_responsetocourtesy_responseunder a top-levelmodbus_server:block #15509 - API: default
max_connectionslowered from 8 to 5 onesp32,bk72xx,rtl87xx, andln882x. Setapi.max_connectionsexplicitly if you need more than 5 concurrent API connections #15889 - OTA (esphome platform): multiple
ota: - platform: esphomeentries with differentport:values are now rejected at config time. Use a single entry on one port #15636 - Output:
FloatOutputruntime power scaling (min_power,max_power,zero_means_zero,output.set_min_power/output.set_max_poweractions) is now gated behind a build flag. You are only affected if you callid(...).set_min_power(...)/set_max_power(...)/set_zero_means_zero(...)from a lambda without ever using the YAML keys or actions. Addmin_power: 0%(or any of the other keys) to a singleoutput:entry to re-enable the runtime setters; the build will emit a clearstatic_asserterror with the fix when this applies #15998 - Sensor: the
throttle_averagefilter now rejectstime_periodvalues longer than 24 hours #16169
Audio Stack
Section titled “Audio Stack”- Audio / Media Player / Speaker: WAV decoding is no longer always compiled in. WAV is still automatically enabled when you embed a WAV file, set
format: WAVas the preferred pipeline format, or use the speaker media player withformat: NONE. If you only ever play WAV from arbitrary URLs via YAML actions, addaudio: codecs: wav:to your configuration #16244 - Media Player / Speaker / Speaker Source:
codec_support_enabledon the speaker media player is now inert and deprecated. Codec inclusion is determined from the pipelineformat:setting (useformat: NONEto include all codecs, equivalent to the oldallmode;format: WAVis the closest equivalent to the oldnone/falsemode) #14771 - Speaker Media Player: files built into firmware via the
files:block now have a 5 MB per-file size limit. Compress oversized files (or switch to a more efficient codec) before upgrading #16266
Behavior Changes
Section titled “Behavior Changes”- Core (main loop): every component’s
loop()now actually runs at the configuredloop_interval_cadence (default ~62 Hz) instead of being pulled forward to ~128 Hz by unrelated scheduler activity. Background events (MQTT RX, USB RX, BLE, etc.) still wake their components within one tick. If your YAML implicitly depended onloop()being pulled forward by other scheduled work, components will now run less often; useHighFrequencyLoopRequesterfor fast wakes #15792 - ESP32 BLE Tracker:
ESP_COEX_PREFER_BTis now held for the full lifetime of any active BLE connection rather than reverting to balanced coex as soon as the handshake settles. This fixesstatus=0x85/ 133 GATT failures onbluetooth_proxysetups under heavy WiFi load (notably Yale/August locks). Configurations holding long-lived BLE connections (for example able_clientpersistent sensor) may see lower WiFi throughput while the connection is up #16036 - One Wire:
OneWireBus::skip()now performs a bus reset before issuing theSKIP ROMcommand, fixing a long-standing 1-Wire protocol violation that could cause unreliable readings (such as the DS18B20 power-on25 °C/85 °Cvalues). External components or lambdas callingskip()must handle the newboolreturn value (falseif reset/presence pulse failed) #14669
Undocumented API Changes
Section titled “Undocumented API Changes”Breaking Changes for Developers
Section titled “Breaking Changes for Developers”OneWireBus::skip()signature: now returnsbool(true on successful presence pulse, false on reset/no-device); also performs a bus reset before theSKIP ROMcommand. Callers must handle the return value #14669ComponentIterator::on_media_playeris now pure virtual: subclasses that don’t handle media players must add anoverride { return true; }stub guarded by#ifdef USE_MEDIA_PLAYER#15618- Heap-allocating helpers moved to
alloc_helpers.h/alloc_helpers.cpp: functions likestr_lower_case,str_snprintf,format_hex,format_hex_pretty,format_mac_address_pretty,value_accuracy_to_string,base64_encode,base64_decode(vector overload),get_mac_address,get_mac_address_prettyand similar have been moved.helpers.hre-exportsalloc_helpers.hfor backward compatibility until 2026.11.0; update your includes before then #15623 PollingComponent()default constructor: now initializes toSCHEDULER_DONT_RUN(UINT32_MAX) instead of1. External components that bypass codegen, callPollingComponent()with no argument, and never callset_update_interval()will stop polling. Pass an interval to the constructor or callset_update_interval()explicitly #15832- API
clients_storage:APIServer::clients_changed fromstd::vectorto compile-timestd::array; theset_max_connections()runtime setter was removed and the cap is now theMAX_API_CONNECTIONScompile-time define populated from YAML #15889 FloatOutputpower scaling members gated:min_power_,max_power_,zero_means_zero_and the corresponding setters are now gated behindUSE_OUTPUT_FLOAT_POWER_SCALING. Lambdas that call these setters require the YAML to opt in (see the user breaking-changes section); a compile-timestatic_assertdocuments the fix at the call site #15998- ESPNOW method visibility and naming:
play()is now correctly protected (was public); some methods/properties have been renamed for consistency with the YAML triggers (e.g.broadcasted→broadcast). External components extending the ESPNOW classes will need to update method names and visibility expectations #16109 - Climate
ClimateTraitsaccessors removed: the 10 deprecatedget/set_supports_*accessors (current_temperature,current_humidity,two_point_target_temperature,target_humidity,action) are gone after their 6-month window. Useadd_feature_flags()/has_feature_flags()with the matchingCLIMATE_*flag; notetwo_point_target_temperaturemaps toCLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE#16289 - EKTF2232
rts_pinmigration shim removed: the schema-levelrts_pin→reset_pinrename helper is gone; configs still usingrts_pinget a generic schema rejection instead of the friendly renamed-to error #16289 - Core ring buffer moved to
ring_bufferhelper component: include fromesphome/components/ring_buffer/ring_buffer.hand use theesphome::ring_buffer::RingBuffernamespace instead ofesphome::RingBuffer. The oldesphome/core/ring_buffer.hlocation is deprecated with a 6-month removal window; addAUTO_LOAD = ["ring_buffer"]to your component’s codegen #16298
For detailed migration guides and API documentation, see the ESPHome Developers Documentation.
Release 2026.5.1 - May 25
Section titled “Release 2026.5.1 - May 25”- Bump zeroconf from 0.149.12 to 0.149.13 esphome#16520 by @dependabot[bot]
- Bump zeroconf from 0.149.13 to 0.149.16 esphome#16533 by @dependabot[bot]
- [espidf] Filter noisy ‘git rev-parse’ errors when .git is stripped esphome#16521 by @swoboda1337
- [espidf] Fix tarfile extract crashing on Python 3.11 with None mode esphome#16530 by @swoboda1337
- [espidf] Write version.txt after extract so bootloader shows the real version esphome#16532 by @swoboda1337
- [core] Persist & restore CORE.toolchain through StorageJSON esphome#16531 by @swoboda1337
- [espidf] Backport ninja linux-arm64 entry into tools.json on aarch64 hosts esphome#16527 by @swoboda1337
- [espidf] Default to remote HEAD when cg.add_library URL has no #ref esphome#16535 by @swoboda1337
- [espidf] Honor the dict shorthand for library.json dependencies esphome#16537 by @swoboda1337
- [esp32] Defer esp_panic_handler wrap so arduino-esp32 IDF component skips it esphome#16538 by @swoboda1337
- [tuya] Restore null guard on status_pin lost in #16353 esphome#16539 by @swoboda1337
- [api] Break api_connection/api_server include cycle to drop custom unique_ptr deleter esphome#16542 by @bdraco
- [sx126x] Assert NSS before wait_busy so commands wake the chip from sleep esphome#16546 by @swoboda1337
- [core] Refresh compiled config cache after upload/logs fallback esphome#16548 by @bdraco
- [sendspin] Bump sendspin-cpp to v0.6.1 esphome#16553 by @kahrendt
- [dashboard] Fix flaky test_websocket_refresh_command on Windows CI esphome#16565 by @bdraco
- [libretiny] Fix LN882H IRAM_ATTR injection point in patch_linker.py esphome#16570 by @Bl00d-B0b
- [esp8266] Use os_timer-based esp_delay() in delay() esphome#16563 by @bdraco
- [uart] Wake main loop on ESP8266 software serial RX esphome#16562 by @bdraco
- [esp32] Demote IDF #warning deprecations from error under ESP-IDF toolchain esphome#16584 by @swoboda1337
- [bluetooth_proxy] Recover slot stuck in DISCONNECTING when CLOSE_EVT is dropped esphome#16588 by @bdraco
- [esp32] Replace per-class -Wno-error=X demotes with blanket -Wno-error for ESP-IDF toolchain esphome#16599 by @swoboda1337
- [esp32] Disable IDF’s COMPILER_DISABLE_DEFAULT_ERRORS so -Wno-error actually undoes -Werror esphome#16604 by @swoboda1337
Release 2026.5.2 - June 2
Section titled “Release 2026.5.2 - June 2”- [writer] Mark storage_should_clean as public API for device-builder esphome#16443 by @bdraco
- [esp32] Decode crash PCs via IDF toolchain on IDF builds esphome#16626 by @swoboda1337
- [packages] Resolve git symlinks on Windows when materialized as text esphome#16657 by @jesserockz
- [libretiny] Fix RTL8710B IRAM_ATTR section being dropped from flashed image esphome#16616 by @bdraco
- [sx126x] fix a typo in image calibration on 863 - 870 Mhz frequency esphome#16731 by @Fyleo
- [core] Persist esphome.area in StorageJSON esphome#16710 by @bdraco
- [core] Clean build when the toolchain changes esphome#16744 by @swoboda1337
- [api] Fix crash loop on VoiceAssistantConfigurationRequest esphome#16757 by @bdraco
Release 2026.5.3 - June 5
Section titled “Release 2026.5.3 - June 5”- [remote_base] Fix RC5 decoding at either receive polarity esphome#16767 by @swoboda1337
- [esp32][core] Restore ESP-IDF version on logs/upload fast path and clean build on framework change esphome#16770 by @swoboda1337
- [rp2040] Fix lwipopts template load on Windows extended-length paths esphome#16783 by @swoboda1337
- [esp32_ble_server] Fix duplicate Device Information Service with string UUIDs esphome#16784 by @swoboda1337
Full List of Changes
Section titled “Full List of Changes”For the complete list of every merged pull request in this release, see the full 2026.5.0 changelog.

Comments