ESPHome Release Notes

Follow

3 release notes curated from 1 source by the Releasebot Team. Last updated: Jul 16, 2026

Get this feed:
  • Jul 1, 2026
    • Date parsed from source:
      Jul 1, 2026
    • First seen by Releasebot:
      Jul 16, 2026
    ESPHome logo

    ESPHome

    ESPHome 2026.7.0 - July 2026

    ESPHome releases 2026.7.0 with native default toolchains for ESP32 and nRF52, major security groundwork, faster builds, and broad feature growth across Modbus, LVGL, Zigbee, networking, and new display and sensor hardware. It also retires the legacy web dashboard and drops Python 3.11.

    ESPHome 2026.7.0

    ESPHome 2026.7.0 flips the default ESP32 and nRF52 toolchains to native builds (ESP-IDF and the nRF Connect SDK) and lands the first coordinated wave of security infrastructure targeting EN18031 compliance: NVS encryption, OTA downgrade protection, and a new transport-agnostic provisioning component. Broad performance and memory work runs through the release, including a tree-wide single-precision float sweep across roughly 50 components, focused ESP8266 DRAM savings, ccache on by default for ESP-IDF, and shared machine-global toolchain caches.

    On the feature side, the release brings a major Modbus overhaul with a rewritten parser and heap-free send path, substantial LVGL expansion (animations, dynamic rotation, boot pausing), gigabit Ethernet on the new ESP32-S31, ESP-NOW v2 payloads, RTC-backed preferences on ESP32, 11 new components covering touch controllers, e-paper panels, IMUs, and the Divoom Pixoo 64, and Zigbee endpoint merging on the new 2.0.2 SDK. The legacy Tornado web dashboard is retired in favor of the external Device Builder, and Python 3.11 support is dropped.

    Upgrade Checklist

    • If you have an ESP32 config without an explicit toolchain: setting, it now compiles with native ESP-IDF; add toolchain: platformio to keep the previous behavior
    • If you have an nRF52 config without an explicit toolchain: setting, it now builds with the native nRF Connect SDK; add toolchain: platformio to keep the previous behavior
    • If you install ESPHome with pip on Python 3.11, upgrade to Python 3.12 or newer
    • If you install ESPHome with pip and use the built-in esphome dashboard command, install and run esphome-device-builder instead
    • If you have ESP32 Zigbee devices, you must re-join, re-interview, and re-configure each device after upgrading
    • If you set enabled or coordinator under Zigbee reporting:, remove those keys
    • If you use web_server: version: 1, plan to migrate to v2 or v3 before 2027.1.0
    • If you address web_server URLs by object ID, switch to the entity name
    • If a browser page hosted elsewhere calls your web_server endpoints, add its origin to web_server: allowed_origins:
    • If you relied on web_server Private Network Access being on by default, set enable_private_network_access: true and add the calling origins to allowed_origins:
    • If you use packages: !include mypackage.yaml, wrap the include in a list: packages: [!include mypackage.yaml]
    • If you use FOUR_SCAN_16PX_HIGH, FOUR_SCAN_32PX_HIGH, or FOUR_SCAN_64PX_HIGH for hub75 scan_wiring, rename to SCAN_1_4_16PX_HIGH, SCAN_1_8_32PX_HIGH, or SCAN_1_8_64PX_HIGH
    • If you set disable_crc under modbus:, remove it
    • If you set send_wait_time or turnaround_time on a modbus: server, remove those keys
    • If you rely on modbus client timing, note that defaults changed to send_wait_time: 2000ms and turnaround_time: 600ms
    • If you use the generic-ln882hki board, switch to generic-ln882h or generic-ln882h-tuya depending on factory firmware
    • If you use the LN882H wb02a board with D1, D7, D8, D9, or default I2C pin aliases, update your pin numbers to match the new mapping
    • If you have lambdas or automations that set light brightness to 0, note that the value is now preserved instead of being clamped to 1.0
    • If two Home Assistant instances share the same bluetooth_proxy, the newest subscriber now wins instead of the oldest
    • If you build for nRF52 boards without a DCDC regulator, DCDC settings are no longer forced and generated code changes slightly
    • If your lambdas call value_accuracy_to_string() or MideaData::to_string(), migrate to the buffer variants (value_accuracy_to_buf() and to_str(buffer))

    Native Toolchains Are Now the Default on ESP32 and nRF52

    The default toolchain for the esp32 platform switches from PlatformIO to native ESP-IDF (#16910). Configurations that do not set an explicit toolchain: option will be compiled with ESP-IDF from this release forward. The framework migration notice that had been shown since 2026.1.0 has also been removed now that the default has landed (#17023).

    The toolchain selects the build backend, not the framework: both the Arduino and ESP-IDF frameworks build on either toolchain, with the native toolchain pulling in Arduino as an ESP-IDF component. Configurations that explicitly set toolchain: platformio continue to build exactly as they did before, while new configurations generated by the wizard or from packages will pick up the native ESP-IDF toolchain automatically. ESP-IDF is now the recommended choice for both the toolchain and the framework.

    The nrf52 platform makes the same move: native nRF Connect SDK builds, added earlier this cycle by @tomaszduda23 (#16898), are now the default toolchain (#17319). Configurations without an explicit toolchain: option build against nRF Connect SDK 2.9.2, installed into the new shared toolchain cache described below, and toolchain: platformio remains available as an opt-out here as well.

    ESPHome Device Builder

    The bundled ESPHome Device Builder has been updated, and most of the work this cycle went into the remote build server.

    Compiling firmware is the slowest thing the Device Builder does, and on a small host such as a Home Assistant Green or a Raspberry Pi a single build can take several minutes. A remote build server lets that dashboard hand the compile to a faster machine on your network, such as a desktop or laptop running ESPHome Desktop, which builds the firmware and sends it back to be installed as usual. Your devices, configurations, and dashboard all stay where they are; only the build moves.

    Pairing With a Build Server Is Much Easier

    A machine that builds for other dashboards now says so on its own dashboard (frontend#1240). A Build server section appears above the device list once another dashboard is paired with it, or once remote building is turned on, and it walks you through pairing in three steps instead of leaving you to find the pieces in Settings.

    From there you can open the pairing window and watch its countdown, copy the address to enter on the sending dashboard (#2075), and compare the identity fingerprint against the one the sender shows. Once peers are paired the section lists them with their connection state and the build queue, and any pairing request waiting on you appears at the top. Settings gained a matching explainer, and the old Only use this for remote compute switch is now Remote compute dashboard, which decides which section opens first rather than hiding anything.

    Remote builds are steadier once they are running, too. If a receiver restarts, the sender now reconnects as soon as it reappears on mDNS instead of waiting out its backoff (#2084). Receivers can now also provision beta versions of ESPHome, so a build server running a different version to a sender on the beta channel no longer quietly falls back to building locally (#2082). The first build for a version installs ESPHome from PyPI and can take a few minutes, which the job log now says out loud instead of sitting silent (#2083).

    If you have never tried this, the terminal now shows a build timer, and a local compile that runs longer than five minutes on a dashboard with no build server paired suggests sending your builds to a faster machine (frontend#1183).

    Offline and Deep Sleep Updates

    Updating a device that is not awake has always been a matter of timing. An OTA aimed at a device that is asleep or otherwise unreachable simply fails, which is a familiar problem if you run anything on deep sleep.

    Queued offline updates, contributed by @rwalker777, fix that (#1430, frontend#911). If a device is offline when you install to it, the Device Builder compiles the firmware, queues the update, and flashes it when the device next appears on the network, with no need for you to be watching at the time.

    The groundwork for this landed in 2026.6.0, and it is ready for production use now. It works across a whole fleet: select any number of devices and hit update, and the ones that are unreachable are compiled once and queued for their next appearance rather than failing (#1929). A device that drops offline between hitting install and the build finishing is queued as well, instead of failing at the last step (#1964), and a queued update reopens as an install, carrying its queued message, when you come back to it (frontend#1181).

    Quickstart Tour

    Getting from a fresh install to a working device has always been the hardest part of ESPHome, and this release takes a real run at it. Setup now ends by offering you a guided tour, and the tour teaches the Device Builder by using it rather than by describing it.

    It spotlights one real control at a time, explains what it is for, and on the steps that matter it waits for you to click it rather than moving on without you. Over eleven steps it walks you through creating your first device, picking a board, naming it, entering your WiFi details, finding your way around the device navigator and the editor, watching the YAML pane stay in sync as you go, and how installing to your board works, finishing on your new device sitting on the dashboard. It takes about five minutes, it leaves you with a real device rather than a throwaway, you can close it and pick it up where you left off, and it is always available afterwards from the menu in the top right.

    The tour was a team effort from @Erioldoesdesign, @stvncode, and @marcelveldt, across design, wireframes, and build (frontend#1045, frontend#1232). The setup flow around it was rebuilt at the same time: a new welcome screen, an experience question that lets beginners take the recommended path with a single click, and a closing screen that hands you to the tour. If you set up the desktop app while another Device Builder is already running on your network, setup now points that out and explains how to pair the two, so you can put the new machine to work building for the dashboard you already have (frontend#1235).

    YAML Errors Explained and Auto-Fixed

    A number of issues reported against ESPHome turn out not to be bugs at all, but a small mistake made while editing YAML. This release goes after that whole class of problem.

    Getting a space wrong used to earn you the raw parser output, mapping values are not allowed here or could not find expected ':', and only if you thought to hover over the squiggle. The line it pointed at was usually where the parser gave up rather than where you made the mistake, which is little help if you are not already fluent in YAML.

    The editor now reads the error and tells you what is actually wrong, in a banner that stays put, naming the line you need to change and what to do to it: “Indentation mismatch: line 12 - platform is indented less than its properties. Indent line 12 by 2 spaces so - platform lines up with them.” Next to it is a Try auto-fix button that makes exactly that change for you. It is a best-effort guess rather than magic, so it is validated against your config before it is applied, it tells you if other errors remain, and it lands as a normal edit you can undo (frontend#1121, frontend#1147, frontend#1148).

    Indentation is not the only thing it explains. Tabs used where YAML wants spaces, a value starting with @, %, or * that needs quotes, an unterminated quote, a duplicate key, and an unclosed bracket or brace all get the same plain-language treatment. A list marker missing its space, -platform: gpio, is offered a one-click repair to - platform: gpio (frontend#1149). So is a key that has drifted out of the block it belongs to, such as an encryption key left sitting directly under api:, in either direction; the Device Builder checks its component catalog first to confirm where the key really belongs, and offers nothing rather than a wrong guess if it cannot tell (frontend#1164).

    Device log lines now link to the documentation that explains them (frontend#1116, frontend#1118). Ten common warnings and errors get a curated explanation, including took a long time for an operation, WiFi reconnect loops, repeated resets, OTA rollbacks, and running out of Bluetooth proxy connection slots, and any other line tagged with a component links straight to that component’s page.

    Headless Build Servers

    @jesserockz added --remote-build-only, which runs Device Builder as a dedicated build box with no web interface at all (#1861):

    It serves only the peer link, so there is no interface for anyone to click Accept in. Instead it opens a pairing window on first start and prints its identity fingerprint and a one-time key to the console. You pair from the sending dashboard, check that the fingerprint matches the console, and type in the key, which the pair dialog knows to ask for up front (#1881, frontend#1120). That window pairs exactly one dashboard and then closes.

    If you would rather keep the web interface but still dedicate the machine to building, turn on Remote compute dashboard in Settings and a Hide the Device builder option appears beneath it, leaving only the Build server view (#2077, frontend#1244). Device Builder can also listen on a UNIX socket with --socket for reverse-proxy setups, contributed by @vexofp (#1862).

    Builds

    Starting a build no longer greys out the buttons that would take you back to it. Update and Install now open the running job, and Edit stays clickable so you can read your YAML while it compiles (frontend#1191, frontend#1193, frontend#1197). Download and Retry now wait for a running build to finish instead of cancelling and restarting it, which also means Download can no longer hand you a half-written binary (frontend#1199, frontend#1201, frontend#1204).

    A bulk Compile only action lets you check a change across many devices before deploying any of it (frontend#1176). ESP-IDF builds now show compile progress, which is read from the ninja build counters rather than the percentage that ESP-IDF never prints (#1880).

    Special thanks to @breti, whose QA testing and detailed issue reports ran through this whole release and do not show up in any pull request count.

    Security and Provisioning Groundwork for EN18031

    This release lands three coordinated pieces of security infrastructure, led by @kbx81, that together move ESPHome toward EN18031 compliance for network-connected consumer devices.

    NVS Encryption on ESP32 (#17004)

    Opt-in encryption for Non-Volatile Storage using the HMAC peripheral. Keys are derived at runtime from an HMAC key in a user-selected eFuse block, so flash encryption is not required. Supported on the ESP32 variants that ship the HMAC peripheral: S2, S3, C3, C5, C6, H2, and P4. Burning the HMAC key to an eFuse block is a permanent, irreversible operation, so users should read the esp32 documentation carefully before enabling this.

    OTA Downgrade Protection (#17315)

    Software anti-rollback for signed ESP-IDF OTAs. The version baked into the staged image (from project: version:) is compared against the running version before the boot partition is switched. Covers all OTA sources: native, http_request.ota, and web_server. Requires a signed OTA to be effective.

    Provisioning Component (#17152)

    A new transport-agnostic provisioning component that manages a provisioning window for devices shipping unprovisioned. Sources such as api and esp32_improv register with the component, and the window is closed on timeout, at which point any provisioning-related API clients are disconnected with DISCONNECT_REASON_PROVISIONING_CLOSED.

    Performance: Single-Precision Float Math

    Led by @swoboda1337, a tree-wide sweep replaces silent double-promotion patterns with single-precision equivalents across roughly 50 components (#17252, #17253, #17254, #17255, #17256, #17260).

    The issue was invisible on ESP32 because ESP-IDF does not enable -Wdouble-promotion: literals like 2.0 and functions like fmod, round, and fabs were quietly promoting single-precision values to double, running the result through the soft-double library on chips without hardware doubles, then narrowing back to float.

    Measured Impact

    • hsv_to_rgb code size: 108 bytes → 69 bytes (-36%) on ESP32
    • Soft-double helper calls eliminated: roughly 8 per call site
    • Components touched: displays, lights, climate, most sensor drivers, openthread, mqtt, speaker, graph, and many more

    The final straggler pass (#17260) also rewrites hot expressions such as std::pow(2, sf) in sx126x to a constexpr bit shift, and switches a few call sites to std::numbers::pi_v.

    Memory: ESP8266 DRAM Savings

    Every byte of RAM matters on ESP8266, and three focused changes by @bdraco reclaim a useful chunk of DRAM without changing runtime behavior.

    • libstdc++ throw message strings (#17341) — Overrides std::_throw* helpers so the never-read message arguments are dropped. Saves roughly 96 bytes of RAM on a ratgdo build. Crash behavior is unchanged.
    • lwIP glue DHCP stub strings (#17395) — Silent equivalents for dhcp_cleanup() and dhcp_release() stubs. Frees 48 bytes of RAM and 88 bytes of flash, and removes the noisy STUB: dhcp_cleanup log on every WiFi disconnect.
    • dashboard_import package URL (#17127) — Moves the mDNS TXT package_import_url string from .rodata (DRAM) to flash. It was the last mDNS TXT value pinned in RAM.

    Build System: ccache and Shared Toolchain Caches

    Led by @swoboda1337, the ESP-IDF and nRF SDK toolchains gain both compiler-level caching and installation-level sharing.

    ccache is now on by default for ESP-IDF builds (#17163, #17136) when the ccache binary is present, using depend mode (depfile hashing instead of preprocessing) and a cache directory under the IDF tools path. Opt out with IDF_CCACHE_ENABLE=0. On the 23-batch CI matrix, summed runner time dropped from 543 to 504 minutes (~7%) and wall time from 154 to 139 minutes.

    Toolchains now install into a machine-global cache directory (#17306, #17353). ESP-IDF installs to ~/.cache/esphome/idf and the nRF Connect SDK installs to ~/.cache/esphome/sdk-nrf, so multi-gigabyte toolchain installs are shared across every project on the machine instead of being duplicated per config. Docker and Home Assistant add-on users pin these to persistent volumes, and existing installs re-download once after upgrade.

    Config validation got faster too, thanks to import deferral by @frenck: aioesphomeapi.posix_tz deferral cuts esphome config median wall time by roughly 33% on a host config from 445 ms to 297 ms (#17214), and deferring requests imports shaves another 70 ms (16%) (#17215).

    Component Alias Infrastructure

    A new core mechanism (#16826) lets components be renamed without breaking existing YAML configurations. Components declare ALIASES = [...] (and an optional ALIAS_REMOVAL_VERSION), and a pre-pass in config validation rewrites legacy keys while a sys.meta_path finder resolves Python imports for esphome.components.. Aliases are discovered by an AST scan so components do not need to be imported to enumerate them.

    The first user of the new infrastructure is the rp2 rename (#17145) by @jesserockz, which renames the rp2040 platform to rp2 to reflect its coverage of both RP2040 and RP2350. The legacy rp2040: YAML key and esphome.components.rp2040 imports continue to work as deprecated aliases and will be removed in 2027.7.0. New rp2_2040 and rp2_2350 variant kwargs are added to SplitDefault and require_framework_version, mirroring the ESP32 pattern.

    Modbus Overhaul

    @exciton landed a major rework of the modbus component across multiple PRs.

    Client and server split (#11969)

    Modbus is split into ModbusClientHub and ModbusServerHub, retiring the role enum entirely. Client components move to ModbusClientDevice (#11987), and the core API surface was renamed and settled before the release (#17434, #17378). External components need to update against the new API now, and further client-mode API cleanup is planned for the next release.

    Rewritten parser

    Byte-by-byte parsing is replaced with a buffer-based, frame-length-aware parser that handles peer server responses, unsupported function codes, and custom function codes correctly. Response tracking uses std::optional, enabling address+function-code matching, multiple devices per address, and two new callbacks: on_modbus_no_response() and on_modbus_not_sent().

    Heap-free send path

    A new SmallInlineBuffer<8> and StaticVector<uint8_t, MAX_PDU_SIZE> mean that 8-byte Modbus frames (the common case for reads and single-register writes) live inline in the deque node with zero per-frame heap activity (#17282).

    Server improvements (#17205)

    Multi-register values now match by full span, a new allow_partial_read option is available, overlapping registers are rejected, and the obsolete RAW value type has been removed from server mode.

    Default send_wait_time moves from 250ms to 2000ms and turnaround_time from 100ms to 600ms. If you were overriding these to compensate for the old parser, review whether the overrides are still needed.

    LVGL: Animations, Rotation, and Runtime Updates

    @clydebarrow drove a substantial expansion of the lvgl component this release.

    • Animations (#16796) — Full LVGL animation support with from/to interpolation, timing functions (gravity and friends), duration, looping, and auto_start.
    • Dynamic rotation (#16773) — Templated lvgl.display.set_rotation in degrees, new on_landscape and on_portrait triggers, and layout option updates in lvgl.update.
    • Direct mapping syntax (#15863) — Cleaner LVGL text and image src updates using a mapping directly.
    • Boot pausing (#16973) — A new paused config option suppresses LVGL updates on boot. Especially useful for e-paper displays that should not render invalid state before the API connects.
    • Continued activity while display busy (#17374) — When update_when_display_idle is used (typical for e-paper), LVGL now still processes user input and runs timers while the display refresh is in progress. The LVGL refresh interval, previously fixed at 16 ms, is now configurable.

    Networking and Connectivity

    Gigabit Ethernet on ESP32-S31 (#17277) — Adds type: GENERIC and type: YT8531 PHYs for the new RGMII-capable ESP32-S31 variant, which requires ESP-IDF 6.1. The YT8531 handler covers auto-negotiation re-enable and RGMII Tx/Rx delays. New power_pin and phy_registers options are available on the ethernet component.

    ESP-NOW v2 payloads (#17360) — The espnow component gains a max_payload_size option that accepts frames up to 1470 bytes (previously capped at 250). The default stays at 250; going to 1470 costs roughly 44 KB of RAM versus about 8 KB at the default. Requires ESP-IDF ≥ 5.4 or Arduino ≥ 3.2.

    LN882X MQTT support (#17297) — LN882H boards can finally use the mqtt component. LN882X was previously blocked despite sharing the LibreTiny backend.

    Data whitening for SX126x (#17102) — Configurable FSK data whitening (CCITT algorithm) via new whitening_enable and whitening_initial options.

    Bluetooth proxy subscriber semantics (#17423) — A later subscriber now takes over the single subscriber slot from a stale one instead of being silently rejected. This fixes the “connected but no advertisements” state that could occur after a Home Assistant restart within the ~150 second keepalive window. Users don’t need to change configs, but the observed behavior does change.

    OpenThread on Zephyr/nRF52 and MTD Polling Control

    Basic Zephyr/nRF52 support (#16854) — First-cut openthread support on the nRF52 Zephyr platform, by @Ardumine. Handles the “join network” flow.

    Runtime poll period control (#11766) — A new set_poll_period action lets a Minimal Thread Device (Sleepy End Device) temporarily switch to radio-always-on for OTA. http_request.ota becomes roughly 10x faster on MTDs, and esphome.ota starts working at all.

    RTC-Backed Preferences on ESP32

    @bootc taught the ESP32 preferences backend to honor the in_flash=false flag of make_preference() (#17073), adding RTC-memory-backed preferences in the same way ESP8266 already had them. The shared word-buffer plus checksum format is extracted into preferences_rtc.h.

    New configuration surface:

    • safe_mode: storage: rtc or flash
    • wifi: fast_connect: dict form with enabled and storage sub-options
    • preferences: rtc_storage: true

    The ESP32-C2 and ESP32-C61 have no RTC RAM and fall back gracefully. This is groundwork for a planned DHCP-lease cache and was validated across ESP32 PICO, S3, C3, and C6 hardware.

    New Displays, Touch Controllers, and Peripherals

    The release adds broad support for new display and touch hardware, largely on M5Stack, Waveshare, and Seeed reTerminal families.

    Touch controllers — cst328 by @latonita (#8011), st7123 integrated display-touch driver for the M5Stack Tab5 by @miniskipper (#12075), gsl3670 for Seeed reTerminal D1001 by @clydebarrow (#16285), and cst9220 covering CST9220 and CST9217 by @clydebarrow (#16888).

    E-paper displays — The epaper_spi component picks up several new panels: Waveshare 7.5” V2 BWR 800×480 by @twisterss (#15719), Seeed reTerminal E1004 13.3” 6-color (T133A01, dual-CS SPI) by @limengdu (#16706), Waveshare 2.13” V4 BWR (SSD1683) by @profplump (#16828), Soldered Inkplate 2 by @arunderwood (#16856), and Seeed reTerminal-sticky by @clydebarrow (#16950). The it8951 e-paper controller for M5Paper and Seeed reTerminal E1003/EE03 (up to 1872×1404, 16-level grayscale) arrives as a new component by @Passific (#15346).

    MIPI-SPI displays — M5STACK ATOM3SR by @clydebarrow (#17344) and the Waveshare ESP32-S3-Touch-AMOLED-1.64 (SH8601) by @crimike (#17386).

    Divoom Pixoo 64 — A new pixoo component by @jesserockz (#16974) drives the 64×64 RGB LED matrix over SPI and exposes a light platform for panel brightness alongside the image API.

    Sensors and peripherals — A ufm01 ScioSense UFM-01 ultrasonic flow meter by @ljungqvist (#16582) reports accumulated flow, flow rate, water temperature, and four diagnostic binary sensors over UART. A qmi8658 6-axis IMU by @clydebarrow (#16889) adds accelerometer, gyroscope, and on-chip temperature sensing with a motion platform. The waveshare_io_ch32v003 I/O expander by @latonita (#10071) covers the CH32V003-based expander shipped on Waveshare’s newer S3 boards, providing EXIO GPIO, PWM backlight control, an ADC battery voltage sensor, and RTC interrupt status.

    Audio — The pcm5122 component grows analog gain (0/-6 dB), channel mixing (stereo/left/right/swapped), configurable volume range, a standby/powerdown switch, and an XSMT enable pin, along with a fix for a clock register page bug that had been silently corrupting clock setup (#17313) by @remcom. The i2s_audio speaker now accepts wider streams (e.g. 24-bit into a 16-bit device) and narrows them in place using esp-audio-libs’ pcm_convert::copy_frames (#16821).

    Image Component Becomes a Platform

    @jesserockz restructured image: into a platform component (#17416). The new form uses platform: file, platform: animation, and platform: online_image, with animation becoming a proper sub-component of the new file platform. The legacy top-level image:, animation:, and online_image: keys continue to work through 2027.1.0 with copy-paste migration warnings shown at validation time.

    Zigbee: SDK 2.0.2 and Endpoint Merging

    @luar123 bumped the zigbee component on ESP32 to the new esp-zigbee-sdk 2.0.2 for better ESP-IDF compatibility and picked up ESP32-H4, H21, and S31 variants (#16869). The dedicated Zigbee storage partition has been retired in favor of the default NVS, and reporting configuration is simplified: the enabled/coordinator distinction is gone.

    A follow-up (#17402) merges endpoints of components on ESP32 and lets users manually combine them. This enables single-endpoint devices with multiple sensors, matching Zigbee standard expectations. Both PRs require existing Zigbee devices to be re-joined, re-interviewed, and reconfigured after upgrading.

    YAML Merge Warnings

    @clydebarrow added a warning when a YAML merge (<<:) silently drops a key (#17246). Two top-level api: blocks combined via <<: would previously drop one without any indication; ESPHome now prints the source location, explains that a merge is happening, and suggests packages: for a deep merge instead. Toggle with esphome: merge_warnings: false.

    Other Notable Features

    • Legacy web dashboard removed (#17124) — The built-in Tornado-based dashboard is gone in favor of the external ESPHome Device Builder. The dashboard CLI command remains but is hidden and prints a migration hint. Docker and Home Assistant add-on users are unaffected: they already route to Device Builder.
    • Python 3.11 support dropped (#17280) — Minimum Python version is now 3.12. Home Assistant add-on and Docker images are already on 3.12.
    • BK7238 and Tuya T1 board support (#17288) — The LibreTiny 1.13.0 update adds BK7238 boards to the bk72xx platform: generic-bk7238, generic-bk7238-tuya, and the Tuya T1 module family (t1-2s, t1-3s, t1-m, t1-u), which is becoming common in devices on the market.
    • PSRAM for ESP32-S31 and H4 (#17192) — Both new variants gain PSRAM wiring: S31 octal at 40/100/200/250 MHz and H4 quad at 32/64 MHz. Values come from the esp_psram Kconfig on ESP-IDF release/v6.1, and octal-mode validation is now data-driven rather than hardcoded to S3.
    • Host platform preferences directory (#11160) — A new ESPHOME_PREFDIR environment variable overrides where the host platform stores preferences, useful when running ESPHome as a system service where HOME may not be set.
    • Web server SSE optimization (#17400) — Logger’s known message length is now threaded through the SSE send path, avoiding strlen and strchr rescans on every log event. No wire changes.
    • Web server HTTP digest authentication (#17541) — web_server auth: gains a new type: option to select basic or digest. Digest keeps the password off the network (only hashes are sent) instead of the trivially reversible Base64 that Basic uses; both look identical in the browser. The scheme is fixed at build time so the unused code path is compiled out. The default stays basic for now to avoid breaking non-browser REST clients and will change to digest in 2027.1.0; set type: basic explicitly to keep the current behavior after that release.
    • WiFi roaming scan suppression (#17012) — A new internal API lets components request that post-connect roaming scans be paused. First user is sendspin, which now suppresses roaming scans while playing to prevent audible hiccups (#17133).
    • Runtime UART reconfiguration (#16990) — Baud rate, parity, and other UART settings can now be changed at runtime on open channels without reconnecting. Landed for both the standard uart and usb_uart components.
    • Light brightness on turn-off preservation (#17103) — Setting brightness to 0 no longer silently clamps to 1.0. If a subsequent turn-on has no explicit brightness and the current brightness is 0, brightness is set to 1.0 at turn-on time instead.
    • coex/sdkconfig reconciler (#17008) — A single reconciler now handles WiFi/Ethernet/BLE coexistence sdkconfig flags on ESP32, killing conflicting values (for example ethernet versus esp32_ble_tracker on CONFIG_SW_COEXIST_ENABLE). Behavior is preserved and internal helpers gain semantic names such as request_wifi, request_ethernet, and request_software_coexistence.
    • Visual editor visibility hints (#17449) — Build-system and framework internals are now marked with cv.Visibility so the Device Builder visual editor can hide them from users who edit YAML from the browser.

    Thank You, Contributors

    This release includes 217 pull requests from over 30 contributors. A huge thank you to everyone who made 2026.7.0 possible:

    • @swoboda1337 - 45 PRs including making ESP-IDF the default esp32 toolchain, the tree-wide single-precision float math sweep, wiring up ESP32-S31/H4/H21 support, ccache and shared toolchain caches, gigabit ethernet, ESP-NOW v2 payloads, and dropping Python 3.11
    • @jesserockz - 45 PRs including the generic component alias infrastructure, the final class sweep across 21 batches, the [pixoo] Divoom Pixoo display component, the [rp2] platform rename, the [image] platform restructure, and removing the legacy web dashboard
    • @clydebarrow - 17 PRs including LVGL animations, dynamic rotation, boot pausing and continued-activity-while-display-busy, the new [gsl3670] and [cst9220] touchscreens, the [qmi8658] IMU motion platform, the [mipi_spi] M5STACK ATOM3SR, YAML merge-drop warnings, and preserving light brightness on turn-off
    • @exciton - 9 PRs including the major [modbus] parser rewrite and client/server hub split, heap-free send path, register-range fixes with partial reads, and the finalized API surface
    • @tomaszduda23 - 8 PRs building out native nRF52 builds: native SDK support, upload flow, config-input rebuild triggers, non-nRF52840 board support, libc selection, and OTA error reporting
    • @kahrendt - 6 PRs including WiFi roaming-scan suppression, the [sendspin] playback integration, [i2s_audio] stream narrowing, and microMP3 bumps
    • @Ardumine - 5 PRs extending nRF52/Zephyr networking: BSD socket support, basic OpenThread, mDNS, [api] support, and enlarged Zephyr net buffers
    • @kbx81 - 4 PRs including ESP32 NVS encryption, OTA downgrade protection, the new [provisioning] component, and the ESP32 network/coexistence sdkconfig reconciler
    • @p1ngb4ck - 4 PRs including [usb_uart] per-device baud caps, FTDI RX stability fixes, and a vector pop_back helper
    • @luar123 - 3 PRs including the [zigbee] SDK 2.0.2 bump and endpoint merging on esp32
    • @frenck - 3 PRs deferring aioesphomeapi and requests imports to speed up config validation, and refining ESP32-S3 PSRAM pin warnings
    • @latonita - 2 PRs including the new [cst328] touchscreen and the [waveshare_io_ch32v003] I/O expander
    • @rwrozelle - 2 PRs including the [openthread] set_poll_period action for MTDs and a [socket] wake-request gate fix
    • @anunayk - 2 PRs including Zephyr GPIO interrupts and [ble_nus] atomic log framing
    • @remcom - 2 PRs including the [pcm5122] analog gain, channel mixing, and standby/powerdown work, plus [audio_file] MP3 detection fixes
    • @miniskipper - the new [st7123] touch controller component for the M5Stack Tab5
    • @Passific - the new [it8951] e-paper controller component
    • @ljungqvist - the new [ufm01] ultrasonic flow meter component
    • @bootc - RTC-backed preferences on ESP32, honoring the in_flash flag

    Also thank you to @bdraco, @haku, @Eelviny, @twisterss, @ddrown, @limengdu, @profplump, @arunderwood, @kyvaith, @jspiros, @guillempages, @zeroflow, @mikelawrence, @jclehner, @berikv, @Bl00d-B0b, and @crimike for their contributions, and to everyone who reported issues, tested pre-releases, and helped in the community.

    Breaking Changes

    Platform Changes

    • ESP32: ESP-IDF is now the default toolchain when no explicit toolchain: is set. Add toolchain: platformio to your esp32: block to keep building with PlatformIO (#16910)
    • Python: Minimum supported Python bumped to 3.12. Home Assistant add-on and Docker are unaffected; only direct pip installs on 3.11 need to upgrade (#17280)
    • nRF52: The native nRF Connect SDK toolchain is now the default when no explicit toolchain: is set. Add toolchain: platformio to your nrf52: block to keep building with PlatformIO (#17319)
    • nRF52: The Zephyr DCDC settings are no longer forced by ESPHome, which slightly changes generated code depending on board settings and allows building for boards without DCDC (#17373)

    Dashboard

    • Legacy Dashboard: The built-in Tornado web dashboard has been removed. Users installing via pip should install and run esphome-device-builder instead. Docker and Home Assistant add-on users are unaffected (#17124)

    Component Changes

    • Bluetooth Proxy: The newest advertisement subscriber now takes over the single subscriber slot from a stale one instead of being rejected. Fixes “connected but no advertisements” after a Home Assistant restart within the ~150 second keepalive window (#17423)
    • Hub75: The deprecated scan_wiring names FOUR_SCAN_16PX_HIGH, FOUR_SCAN_32PX_HIGH, and FOUR_SCAN_64PX_HIGH have been removed. Use SCAN_1_4_16PX_HIGH, SCAN_1_8_32PX_HIGH, and SCAN_1_8_64PX_HIGH instead (#17118)
    • Light: Setting brightness to 0 no longer silently clamps to 1.0. If a subsequent turn-on has no explicit brightness and the current brightness is 0, brightness is restored to 1.0 at turn-on time instead (#17103)
    • LibreTiny (LN882H): generic-ln882hki board removed; use generic-ln882h or generic-ln882h-tuya depending on factory firmware. Default LN882H log UART is now UART1. LN882H partition layouts changed (on-device OTA migration handled by LibreTiny). On the wb02a board, pin aliases shifted (D1 → GPIO6, D7 → GPIO5, D8 → GPIO9, D9 → GPIO24, new D10 → GPIO25) and default I2C pin order changed (#17288)
    • Modbus: The disable_crc option has been removed, along with send_wait_time and turnaround_time from server mode. Client-mode defaults were raised: send_wait_time 250ms → 2000ms, turnaround_time 100ms → 600ms (#11969)
    • Packages: The single-package !include form is removed. Wrap the include in a list: packages: [!include mypackage.yaml] (#17119)
    • Web Server: The deprecated object ID URL matching fallback has been removed. URLs are matched by entity name only (#17113)
    • Web Server: Version 1 is deprecated. A configuration warning is emitted when version: 1 is set; removal is targeted for 2027.1.0. Migrate to v2 (the default) or v3 (#17109)
    • Web Server: Cross-origin browser requests carrying an Origin header are now rejected unless the origin matches the request Host (same-origin) or is listed in the new allowed_origins: option. Requests with no Origin header (curl, native API, other non-browser clients) are always allowed, so this is defense-in-depth against the browser confused-deputy/CSRF case, not an authentication boundary. Private Network Access is now off by default (previously on) and, when enabled, reuses allowed_origins: to authorize preflights; enabling PNA without any allowed origins is rejected at config validation (#17530)
    • Zigbee: The Zigbee SDK bump to 2.0.2 changes the storage layout, so ESP32 devices must be re-joined and reconfigured after upgrading. The reporting: config values enabled and coordinator are no longer valid (#16869)
    • Zigbee: on ESP32 endpoint merging now combines endpoints of components by default and lets users manually combine them. Existing configurations require re-joining, re-interviewing, and re-configuring (#17402)
    • Display Components: The MIPI family of display drivers (mipi_spi, mipi_dsi, and mipi_rgb) now enforce configuration of mandatory components such as PSRAM and I/O expanders where required. This applies only to those display models that identify integrated boards with known hardware assignments (#17405)

    Undocumented API Changes

    Users with lambdas that call internal string-formatting helpers should note the following removals. Both methods were deprecated 6 months ago in favor of buffer-based variants that avoid std::string allocation.

    • Core: value_accuracy_to_string() has been removed. Use value_accuracy_to_buf(), which writes into a caller-provided buffer of VALUE_ACCURACY_MAX_LEN bytes (#17116).
    • Remote Base (Midea): MideaData::to_string() has been removed. Use to_str(buffer) instead, which formats into a caller-provided buffer (#17117).
    • Dashboard Import (ESP8266): package_import_url is now stored as a PROGMEM flash literal (ProgmemStr) rather than a plain const char * in DRAM. Existing consumers already read it as a flash pointer (mDNS via MDNS_STR_ARG, MQTT via ArduinoJson’s __FlashStringHelper overload), so this is a transparent change on ESP8266 and a no-op on other platforms. Lambdas that read package_import_url on ESP8266 should not dereference it as a plain const char * (#17127).

    Breaking Changes for Developers

    • Modbus API split: Modbus is split into ModbusClientHub and ModbusServerHub; ModbusDevice::set_parent() now takes ModbusClientHub *; Modbus::register_device() removed (#11969, #11987, #12376).
    • Modbus API naming: Core public API surface renamed and settled before release; further client-mode API cleanup is planned for the next release (#17378, #17434).
    • Modbus function code comparisons: Ordering operators (<, <=, >, >=) between ModbusFunctionCode and uint8_t removed. Cast explicitly or use helpers::is_function_code_*() (#11969).
    • Select state member removed: Use current_option() or active_index() instead (#17027).
    • Scheduler std::string overloads removed: App.scheduler.set_timeout(std::string, ...) and related std::string-name overloads are gone; pass const char * names (#17111).
    • get_object_id() and get_compilation_time() removed: Previously deprecated core helpers deleted (#17112).
    • GPIOPin::dump_summary() returning std::string removed: Overrides must return via the buffer form (#17115).
    • UART load_settings(bool) is now pure virtual: All UARTComponent subclasses must implement runtime settings updates (#16990).
    • API pre-1.14 object_id backward-compat removed: Old client fallback path deleted (#17108).
    • Preferences RTC helpers moved: Word-buffer + checksum format extracted to preferences_rtc.h and shared between ESP32/ESP8266 (#17073).
    • Network use_address logging changes: Runtime MAC-suffix expansion moved into the network component (#17432).
    • Configurable classes marked final: Roughly 20 batches of components add final to their runtime-instantiated concrete classes to allow devirtualization (#16952 - #16972, #17129, #17130, #17147).
    • rp2040 component renamed to rp2: Python imports esphome.components.rp2040 and the rp2040: YAML key continue to work as deprecated aliases through 2027.7.0 via the new alias infrastructure (#17145, #16826).
    • mipi_ display driver model config changes: Use of swap_xy=cv.UNDEFINED in a model definition is deprecated (#17405).

    For detailed migration guides and API documentation, see the ESPHome Developers Documentation.

    Original source
  • Jun 1, 2026
    • Date parsed from source:
      Jun 1, 2026
    • First seen by Releasebot:
      Jun 18, 2026
    ESPHome logo

    ESPHome

    ESPHome 2026.6.0 - June 2026

    ESPHome releases 2026.6.0 with the new ESPHome Device Builder as the default dashboard, faster LVGL validation, safer ESP8266 WiFi defaults, reclaimed RAM for disabled network interfaces, and a major audio stack refresh plus new hardware support and tooling updates.

    ESPHome 2026.6.0

    ESPHome 2026.6.0 retires the legacy in-tree dashboard, replacing it with the new ESPHome Device Builder, which reached 1.0.0 this release. The release also raises the default WiFi security mode on ESP8266 to WPA2 and finally makes WiFi and ethernet enable_on_boot: false reclaim the 15–30 KB and 3–8 KB of internal SRAM they were always supposed to, and delivers up to 4.5× faster LVGL config validation. A wide audio-stack modernization brings zero-copy ring buffers, any-bit-depth mixing, and a new router speaker, while a multi-PR effort prepares the codebase for ESP-IDF 6 and native RISC-V clang-tidy. It also adds a new motion IMU framework with BMI270 and LSM6DS drivers, two USB-serial drivers, a PCM5122 audio DAC, YAML frontmatter, and a top-level build_flags option that finally works on native IDF.

    Upgrade Checklist

    • If you use an ESP8266 on a legacy WPA-only (TKIP) router, pin min_auth_mode: WPA under wifi: or the device will stop associating
    • If you use the dsmr electricity_switch_position sensor, move it from sensor: to text_sensor:
    • If you use the nextion display with dump_device_info: true, remove that option (device info is always logged now)
    • If you still load components from a custom_components/ folder, migrate them to external_components:
    • If your time: component uses platform: homeassistant with an explicit timezone:, note that Home Assistant will no longer override your configured zone
    • If you use the dlms_meter component with the provider: option, expect a deprecation warning; the option is now ignored and will be removed in 2026.11.0
    • If you maintain external components calling mark_failed("...") or status_set_error("..."), switch to mark_failed(LOG_STR("...")) / status_set_error(LOG_STR("..."))
    • If you maintain external components using cv.only_with_esp_idf or CORE.using_esp_idf, replace them with cv.only_on_esp32 / CORE.is_esp32
    • If you have lambdas calling text_sensor->raw_state, switch to text_sensor->get_raw_state()
    • If you have lambdas referencing the mipi_dsi::MIPI_DSI C++ type, rename it to mipi_dsi::MipiDsi

    ESPHome Device Builder Replaces the Legacy Dashboard

    The new ESPHome Device Builder, shipped as an opt-in public beta in 2026.5.0, reached 1.0.0 this release and replaces the legacy in-tree dashboard, which is being retired. The Device Builder lives in two repos, device-builder (Python backend) and device-builder-frontend (web UI), and as of 2026.6.0 it is the dashboard bundled by default in the official Home Assistant ESPHome add-on.

    What it brings over the legacy dashboard:

    • Visual component and automation builder alongside a CodeMirror YAML editor, with a left-sidebar device navigator. The legacy dashboard was a plain text editor over a .yaml file.
    • 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, replacing the legacy one-operation-at-a-time model.
    • Remote builder: one Device Builder instance can offload OTA builds to another over a peer-paired link (mDNS discovery, out-of-band fingerprint confirmation, identity rotation, per-peer auto-route).
    • Labels, areas, an editable friendly name, device cloning, and multi-select bulk actions (update, delete, archive, and label across an arbitrary set of devices).
    • Out-of-sync detection: an encryption-state mismatch badge on every device, plus version and config-hash in-sync diagnostics in the device drawer.
    • 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).
    • Real settings UI with light / dark / system theme and localization underway across multiple languages.
    • First-run WiFi onboarding and an expanded install-method dialog: Web Serial flashing, server-side USB (relabeled for the Home Assistant host when running as the add-on), web.esphome.io download, and manual .bin download.

    The 2026.5.0 Use new Device Builder Preview opt-in toggle is gone; the Device Builder is simply the dashboard now.

    ESP8266 Default WiFi Security Raised to WPA2

    ESP8266 now defaults to min_auth_mode: WPA2, matching the ESP32 default that has shipped for years. ESPHome has been printing a deprecation warning on every ESP8266 config without an explicit min_auth_mode since 2026.1, calling out exactly this flip. With ~40% of ESPHome installs running on ESP8266 hardware, this is the broadest platform-wide change in the release (#16682 by @swoboda1337).

    Configurations connecting to modern WPA2/WPA3 access points need no change. Devices on legacy WPA-only (TKIP) routers must pin the old behavior explicitly:

    wifi:
      min_auth_mode: WPA
    

    WiFi and Ethernet Now Truly Free Their Memory When Disabled at Boot

    Setting enable_on_boot: false on wifi: and ethernet: previously skipped only the start() call, leaving the entire driver resident in DMA-capable internal SRAM. Two PRs by @kbx81 split the heavy allocation work into a lazy-init path that only runs when the interface is actually enabled, so a dormant interface now costs zero internal RAM.

    Reclaimed memory on ESP32:

    • WiFi: roughly 15–30 KB of DMA-capable internal SRAM freed when enable_on_boot: false (#16606). Field-tested on ESP32-S3 with W5500 SPI ethernet
      • I2S audio + bluetooth_proxy: free internal SRAM under peak load went from ~14 KB to ~32 KB, with min-free measurements reaching 78 KB in some configurations.
    • Ethernet: roughly 3–8 KB freed when enable_on_boot: false, matching parity with the WiFi lifecycle (#16607). Brings ethernet.enable / ethernet.disable actions and ethernet.connected / ethernet.enabled conditions, mirroring the WiFi automation surface.

    This is groundwork for running WiFi and ethernet side by side in a single configuration: each interface can now be brought up on demand without paying for the one that is not in use.

    The W5500 SPI driver also got a CPU win in #16596 by @kahrendt: large frame transfers now go through an interrupt-driven DMA path instead of busy-waiting the CPU. Measured streaming 48 kHz 24-bit FLAC over W5500, ethernet task CPU usage dropped from ~5% to ~3.8%, roughly a quarter lower.

    LVGL Configuration Validation Up to 4.5× Faster

    The new device builder UI revalidates configurations on every save, which made esphome config latency a direct UX cost. LVGL configs were the worst offender. A five-PR series by @bdraco attacks the hot path in LVGL schema construction:

    • Memoize and lazily build container_schema (#16567) - repeated validation median dropped from 210 ms to 17 ms on a representative LVGL config.
    • Build widget update action schemas lazily (#16569) - saves roughly 200 ms at lvgl import time.
    • Merge dict-extend chains (#16614) - 2.81× speed-up on read_config (0.246 s → 0.087 s on the lvtest host.yaml).
    • Memoize obj_schema by widget type (#16615) - 2.14× speed-up stacked on top.
    • Build automation_schema event validators lazily (#16633)
      • 4.5× reduction in per-call cost (281 ms → 63 ms across 29 widget types in a microbench).

    The user-visible effect: every save in the device builder is noticeably snappier, especially on configurations with large LVGL screens.

    Audio Stack Modernization

    @kahrendt led a wide-ranging audio rework this cycle, replacing the allocate-and-copy AudioSourceTransferBuffer pattern with a zero-copy RingBufferAudioSource across the critical audio paths.

    Zero-copy ring-buffer rollout eliminates one allocation and one extra copy per chunk of audio processing:

    • resampler (#16560)
    • AudioDecoder for the speaker media player (#16564)
    • micro_wake_word (#16595)
    • voice_assistant (#16597) - also drops two 1024-byte send buffers in favor of reading directly from the ring buffer

    Mixer supports any bit depth (#16524). The mixer speaker now accepts 8, 16, 24, or 32 bits per sample, lifting the last remaining 16-bit-only restriction from the audio stack. Backed by esp-audio-libs v3.1.0 / v3.2.0, with the upstream library using template specializations to keep the new code paths as fast as the old 16-bit-only version.

    Resampler bit-depth pass-through (#16892). Now that the mixer handles any bit depth, the resampler defaults to passing the input audio's bit depth through unchanged rather than auto-converting to match the output speaker. This avoids burning CPU cycles in the resampler when the downstream mixer would do a faster conversion anyway.

    New router speaker component (#16592) for switching between output speakers at runtime via the router.speaker.switch_output action. A typical use case is switching between SPDIF and analog I2S outputs live from a select entity, with audio staying in sync after the switch.

    micro_wake_word task stack can live in PSRAM (#16632), saving roughly 3 KB of internal SRAM on the ESP32-S3 with no measurable performance impact. Set task_stack_in_psram: true.

    PSRAM task-stack handling consolidated (#16628) so audio_file, audio_http, mixer, resampler, sendspin, and speaker.media_player all share the same validation pattern, with a bug fix for task_stack_in_psram: false on the speaker media player.

    ESP-IDF 6 and Native RISC-V clang-tidy

    A multi-PR effort by @swoboda1337 makes static analysis substantially better at catching real-firmware bugs:

    • script/clang-tidy now supports RISC-V targets natively (#16809)
      • clang's upstream RISC-V backend can now analyze ESP32-C3/C6/H2/P4 builds instead of falling back to the 32-bit x86 stand-in used for Xtensa.
    • Native ESP-IDF clang-tidy is now the comprehensive pass (#16823). The arduino toolchain has a blind spot where its headers pull std:: float math overloads into the global namespace, hiding performance-type-promotion-in-math-fn findings. The IDF toolchain catches them.
    • Float-to-double promotion fixed across the codebase (#16812) - display, daikin_arc, nau7802, sgp4x, thermopro_ble, tuya all switched from C <math.h> to the float-typed std:: overloads, keeping math in float on 32-bit MCUs instead of needlessly promoting to double.
    • clang-tidy passes on ESP-IDF 6 (#16850) - sdkconfig pinned to newlib, GCC-only flags stripped, ledc HAL guard added under static analysis. This is the foundation for upcoming ESP-IDF 6 support.
    • arduino-esp32 stub for managed IDF components (#16695)
      • on PlatformIO, components like FastLED that declare REQUIRES arduino-esp32 now resolve to a stub that re-exports the framework via CMake INTERFACE, instead of the IDF Component Manager trying to download a duplicate.
    • 100% patch coverage gating (#16827 by @jesserockz) - new PRs must cover every changed line. The project-level coverage stays informational so contributors are not penalized for pre-existing untested code.

    New Components and Hardware Support

    @clydebarrow landed a coordinated IMU stack: a generic motion hub component that provides acceleration, angular rate, and derived pitch/roll values, plus actions for automatic pitch/roll calibration (#16226). Two concrete drivers ride on top:

    • Bosch BMI270 (#16202) - used in M5Stack Tab5 and Espressif's EchoEar
    • STMicro LSM6DS3TR-C (#16232) - used in the Seeed ReTerminal D1001

    New sensors and DACs:

    • PCM5122 audio DAC by @remcom (#15709) - the Texas Instruments stereo I2S DAC popular in Raspberry Pi HATs, with mute, digital volume, and four configurable GPIO pins exposed through the standard pin schema.
    • XDB401 pressure sensor by @RT530 (#15108) - I2C pressure and temperature from the XIDIBEI XDB401.

    USB-serial drivers by @p1ngb4ck:

    • FTDI FT23XX family (#14587) - covers FT232R, FT2232C, FT2232H, FT4232H, FT232H, FT230X, and AM/BM variants; chip type auto-detected from the USB descriptor; multi-channel chips supported as separate channels.
    • Prolific PL2303 family (#16885) - covers the legacy PL2303H/HX through the modern G-series; baud rate encoding selected per chip type.

    New display: WAVESHARE-ESP32-S3-TOUCH-AMOLED-2.16 added to the mipi_spi driver (#16887 by @clydebarrow).

    DLMS Smart Meter Component Overhaul

    @Tomer27cz refactored the dlms_meter component to use the external dlms_parser library (#15458), substantially broadening smart-meter support:

    • Dynamic OBIS code schema - define custom sensors, text sensors, or binary sensors by their OBIS code (e.g. "1-0:99.99.9") instead of being limited to the hardcoded property list.
    • Binary sensor support is new.
    • Decryption key now optional - meters that broadcast plaintext are supported directly.
    • Flexible UART baud rate - the 2400-baud requirement is gone; configure the UART to whatever your meter speaks.
    • New configuration variables - auth_key, custom_patterns, skip_crc, and receive_timeout for meters with non-standard behavior.

    The legacy schema continues to work and the provider key is now ignored with a deprecation warning, slated for removal in 2026.11.0. Existing configurations should still validate without change.

    The companion DSMR component also got a fix and feature pass (#16561 by @PolarGoose), including support for EON Hungary meters, custom auth keys, and automatic hex-string detection in equipment ID fields. Note that electricity_switch_position moved from sensor to text_sensor, which may need a YAML update.

    Networking, Zephyr, and Platform Plumbing

    Zephyr IPv6 networking on nRF52 (#16336 by @Ardumine) is the first step toward OpenThread support on the nRF52840 via Zephyr. Adds a Zephyr-specific IPAddress class backed by struct in6_addr, networking Kconfig, and three new nRF52 network test fixtures. IPv6 defaults to enabled on nRF52.

    RP2350 / RP2040 variant config (#16602 by @jesserockz) brings the ESP32-style variant option to the rp2040 platform block, so the same platform can target the RP2040 or RP2350 and emits matching USE_RP2040_VARIANT_<VARIANT> build flags. The variant is auto-derived from the board's MCU; specifying both must match.

    esp32_hosted PSRAM transport buffers (#16627 by @swoboda1337) - new use_psram: true option moves the esp_hosted transport mempool out of DMA-capable internal RAM and into PSRAM, fixing boot-time sdio_mempool_create asserts on memory-tight ESP32-P4 + LVGL UI configurations.

    Configuration and Tooling Improvements

    YAML frontmatter (#16552 by @jesserockz) - a leading ----separated YAML document is now treated as opaque metadata, stripped before validation, and captured per-file on CORE.frontmatter. Future tooling (including the device builder) can consume arbitrary user metadata like author, version, labels:

    ---
    author: Jesse Hills
    version: 1.0.0
    labels: [office, climate]
    ---
    esphome:
      name: my-node
    

    esphome.build_flags for IDF + PlatformIO (#16629 by @swoboda1337) - a top-level esphome.build_flags list now applies compiler flags on both backends. The existing platformio_options.build_flags was PlatformIO-only; users on native IDF (ESP32-P4, ESP32-H2) needed this.

    esphome config --no-defaults (#16718 by @jesserockz) emits only the user-supplied configuration after substitutions and packages resolve, without the hundreds of injected schema defaults. Useful for diffing configs, sharing minimal reproductions in issues, and sanity-checking what was actually typed.

    Codeberg short-form Git URLs (#16501 by @Eelviny) - codeberg://owner/repo/path/file.yaml is now a supported source for dashboard_import and short-form packages, alongside github:// and gitlab://.

    github:// framework source (#16639 by @swoboda1337) - esp32.framework.source now accepts github://owner/repo@ref and https://github.com/owner/repo.git@ref, doing a git clone --depth=1 --recurse-submodules instead of downloading an archive. This is needed because GitHub archive URLs strip submodules, which ESP-IDF relies on heavily (mbedtls, openthread, tinyusb, …). Primary use case is testing against pre-release ESP-IDF before any tagged release ships a new chip.

    Sensitive value redaction (#16690 by @bdraco) is now driven by cv.sensitive() markers in the schema rather than a post-dump regex. WiFi SSIDs are now marked sensitive, joining passwords. External components using the new marker get redaction automatically; the regex fallback stays as a deprecation bridge through 2026.12.0.

    State reporting CLI control (#16746 by @clydebarrow) - the logs command gained a --states / --no-states flag and an environment variable for the default, restoring control over whether state changes appear in the log stream.

    Light Component Enhancements

    @jesserockz added two light improvements:

    • light.effect.next and light.effect.previous actions (#16491) cycle through a light's configured effects without having to hardcode effect names. An include_none: true option lets the cycle pass through the un-effected state. The target light must have at least one effect, checked at config validation time.
    • Lambda light effects receive the light as it (#16815) - matches the existing behavior of addressable lambda effects, so simple lambdas can reference the light directly without id(my_light).

    @clydebarrow also added a rounded property to LVGL meter arcs (#16669).

    Other Notable Features

    • Display metadata for LVGL validation (#16702) - display drivers now register byte_order, rotation, draw_rounding, has_writer, and has_hardware_rotation metadata, so LVGL can pick byte_order automatically, reject mixed byte orders, and merge draw_rounding requirements from the attached display.
    • Mitsubishi CN105 swing modes (#15653 by @crnjan) - vertical / horizontal / both swing for the Mitsubishi CN105 climate component, with last non-swing vane positions tracked and restored when swing is disabled.
    • SDL display screen selection (#16363 by @clydebarrow) - the host-platform SDL display can now be centered on a specific display when multiple are attached.
    • homeassistant.time honors explicit timezones (#16583 by @clydebarrow) - a timezone: configured locally is no longer silently overridden by Home Assistant's zone.
    • I2C host platform support (#14489 by @jesserockz) - basic I2C bus support when running ESPHome on a Linux host platform, useful for testing and host-side integrations.

    Notable Bug Fixes

    • Nextion device info storage rewrite (#16059 by @edwardtfn) - the connect_info parser was replaced with fixed-size field extraction, eliminating all heap allocations from the comok response. Device info is now always logged in dump_config(); the old dump_device_info YAML option is gone.

    Thank You, Contributors

    This release includes 178 pull requests from over 20 contributors. A huge thank you to everyone who made 2026.6.0 possible:

    • @swoboda1337 - 57 PRs including the ESP-IDF 6 / native RISC-V clang-tidy infrastructure, the ESP8266 WPA2 default flip, removal of long-deprecated core APIs, and extensive ESP32 platform plumbing
    • @kahrendt - 19 PRs including the audio stack modernization (RingBufferAudioSource rollout, any-bit-depth mixer, resampler pass-through, new router speaker) and the W5500 ethernet SPI offload
    • @clydebarrow - 16 PRs including the new motion IMU hub with BMI270 and LSM6DS drivers, LVGL display-metadata validation, and the Home Assistant timezone fix
    • @jesserockz - 9 PRs including YAML frontmatter support, light.effect.next / light.effect.previous actions, the RP2040/RP2350 variant option, esphome config --no-defaults, and 100% patch-coverage enforcement
    • @kbx81 - 4 PRs including the WiFi and ethernet lazy-init RAM reclamation work
    • @p1ngb4ck - 2 PRs adding the FTDI FT23XX and Prolific PL2303 USB-serial drivers
    • @PolarGoose - 2 PRs on DSMR including EON Hungary meter support and the dsmr_parser library update
    • @tomaszduda23 - 2 PRs on the nRF52 native build and network IPv6 enforcement
    • @rtyle - 2 PRs fixing QMP6988 sensor I2C error handling
    • @Tomer27cz - the DLMS meter component overhaul using the external dlms_parser library
    • @Ardumine - Zephyr IPv6 networking support for nRF52
    • @remcom - the new PCM5122 audio DAC component
    • @RT530 - the new XDB401 pressure sensor component
    • @crnjan - swing support for the Mitsubishi CN105 climate component
    • @edwardtfn - the Nextion connect_info heap-allocation rewrite

    Also thank you to @bdraco, @Rapsssito, @burundiocibu, @leodrivera, @SoCuul, @david-collett, @chemelli74, @Eelviny, @gnumpi, @GuzTech, @rwrozelle, @exciton, @i-am-no-magic, and @kpfleming for their contributions, and to everyone who reported issues, tested pre-releases, and helped in the community.

    Breaking Changes

    Platform Changes

    • WiFi (ESP8266): Default min_auth_mode flipped from WPA to WPA2, matching the ESP32 default. The 2026.1 deprecation warning has been calling this out at every config validation. Devices on legacy WPA-only (TKIP) routers must pin min_auth_mode: WPA explicitly under wifi: (#16682)

    Component Changes

    • DSMR: electricity_switch_position moved from sensor: to text_sensor: because Hungarian meters emit it as a string (e.g. ON) rather than a number. Update existing configs to declare the entity under text_sensor: (#16561)
    • DLMS Meter: Refactored to use the external dlms_parser library, adding dynamic OBIS-code schema, binary sensor support, optional decryption keys, and a configurable UART baud rate (the 2400-baud requirement is gone). The legacy hardcoded property schema continues to work; the provider: option is now ignored with a deprecation warning and will be removed in 2026.11.0 (#15458)
    • Nextion: The dump_device_info YAML option has been removed. Device info is now always stored and logged in dump_config(). Remove dump_device_info: true from any display: Nextion configs (#16059)
    • Home Assistant Time: When a timezone: is explicitly configured on a time: component with platform: homeassistant, the zone reported by Home Assistant no longer overrides it. Configurations that relied on HA silently overriding a stale local timezone should remove the local timezone: entry (#16583)

    Core Changes

    • custom_components/ folder removed: The deprecated custom_components/ directory auto-loader has been removed. Use external_components instead. The deprecation warning has been printing since 2025.6 (#16679)
    • Sensitive value redaction: cv.sensitive() now drives runtime redaction directly via a YAML representer instead of a post-dump regex. WiFi SSIDs are now marked sensitive and will be redacted alongside passwords in esphome config output. No YAML change needed; pass --show-secrets to bypass redaction as before (#16690)

    Undocumented API Changes

    Lambda users and external component authors who reference internal C++ types should note the following change. The type still behaves the same at runtime, but its name was updated to match the project's identifier-naming convention:

    • MIPI DSI display class renamed: mipi_dsi::MIPI_DSI is now mipi_dsi::MipiDsi. Any lambda or external component that referenced the old type name must use the new spelling. No behavior change (#16837)

    Breaking Changes for Developers

    • Component::mark_failed / status_set_error const char * overloads removed: use the const LogString * overloads via LOG_STR("...") (#16680)
    • cv.only_with_esp_idf and CORE.using_esp_idf removed: migrate to cv.only_on_esp32 and CORE.is_esp32; use CORE.using_toolchain_esp_idf for the rare actual toolchain check (#16681)
    • text_sensor::TextSensor::raw_state public member removed: use get_raw_state() instead (#16683)
    • nfc::format_uid(span) / nfc::format_bytes(span) heap helpers removed: switch to the stack-buffer variants nfc::format_uid_to(buf, span) / nfc::format_bytes_to(buf, span) with FORMAT_UID_BUFFER_SIZE / FORMAT_BYTES_BUFFER_SIZE (#16684)
    • seq<> and gens<> tuple-unpack templates removed from core/automation.h: use std::index_sequence / std::index_sequence_for (#16685)
    • audio::scale_audio_samples deprecated: switch to esp_audio_libs::gain::apply from the bundled esp-audio-libs (Q31 scale factor, byte buffers, explicit bytes_per_sample). Scheduled for removal in 2026.12.0 (#16831)
    • cv.sensitive() schema marker: external component schemas should now wrap sensitive fields with cv.sensitive(...) for redaction in esphome config output. The old regex-based fallback stays as a deprecation bridge through 2026.12.0 (#16690)

    For detailed migration guides and API documentation, see the ESPHome Developers Documentation.

    Full list of changes includes many new features, components, platforms, and breaking changes as summarized above.

    Original source
  • All of your release notes in one feed

    Join Releasebot and get updates from ESPHome and hundreds of other software products.

    Create account
  • May 1, 2026
    • Date parsed from source:
      May 1, 2026
    • First seen by Releasebot:
      Jun 3, 2026
    • Modified by Releasebot:
      Jun 8, 2026
    ESPHome logo

    ESPHome

    ESPHome 2026.5.0 - May 2026

    ESPHome 2026.5.0 releases a public beta of the new Device Builder, major runtime and watchdog efficiency gains, a native ESP-IDF toolchain, modernized audio and OTA upgrades, expanded Zigbee and nRF52 support, and broad reliability and performance improvements.

    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:
    • If you use modbus_controller in server mode, migrate to the new modbus_server component and rename server_registers to registers and server_courtesy_response to courtesy_response
    • If you rely on more than 5 concurrent API connections on esp32, bk72xx, rtl87xx, or ln882x, set api.max_connections explicitly (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_enabled on the speaker media player, drop it and use the pipeline format: setting instead (format: NONE includes all codecs, format: WAV matches the old none/false mode)
    • 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: esphome entries on different ports, consolidate to a single entry on one port
    • If you use throttle_average filters with a time_period longer than 24 hours, lower it (the new schema cap is 24 h)
    • If your lambdas call id(...).set_min_power(...), set_max_power(...), or set_zero_means_zero(...) on a FloatOutput, add min_power: 0% (or any of the scaling keys) to one of your output: entries so the runtime setters stay compiled in
    • If your external component implements ComponentIterator and doesn't handle media players, add bool 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 new bool return value (false means 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 calls set_update_interval(), pass an interval explicitly; the default constructor no longer polls at all
    • If your external component uses esphome::RingBuffer from esphome/core/ring_buffer.h, switch to esphome::ring_buffer::RingBuffer from esphome/components/ring_buffer/ring_buffer.h and add AUTO_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, or base64_encode, include esphome/core/alloc_helpers.h directly (the helpers.h re-export is temporary)
    • If you maintain an external climate platform, replace the deprecated set_supports_
    /get_supports_* accessors with add_feature_flags()/has_feature_flags() (note two_point_target_temperature maps to CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)
    • If you hold long-lived BLE connections via ble_client and notice WiFi throughput drops, this is the new esp32_ble_tracker coex behavior; open an issue with your config if it materially affects your workload
    • If your ektf2232 config still uses rts_pin, rename it to reset_pin (the friendly migration error has been removed)

    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 .yaml file 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:
    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 configured loop_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-second loop_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 inside arch_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_timeout knob from #15908), 100 ms on ESP8266, 2000 ms on LibreTiny BK72xx, and 300 ms on the remaining platforms. Real ESP32+BT proxy measurements show the wdt bucket 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:
    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:
    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 (refs), climate.control (ref), cover.control/cover.template.publish (ref), fan.turn_on (ref), or valve.control (ref) 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:
    @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-idf

    The same selection is available on the command line and overrides the YAML value for one-off builds:

    esphome --toolchain esp-idf compile my-device.yaml

    Precedence 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 /idf/ (or $ESPHOME_ESP_IDF_PREFIX when set). ESPHome downloads the matching ESP-IDF release plus its Python environment, then builds through idf.py directly.
    • Automatic conversion of PlatformIO library entries into ESP-IDF components (including the patches required), so projects that rely on lib_deps from 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:
    @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 a memmove operation on the transfer buffer that, for MP3, used more CPU than the decoding itself.
    • The unified microDecoder library (#15679, #16237) handles HTTP source reads, decoding, and threading internally. audio_file and the new audio_http media source (#15741) both build on it.
    • esp-audio-libs was 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:
    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:
    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:
    @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:
    @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:
    @clydebarrow continued landing LVGL refinements throughout the release:
    • Flexible grid layouts (#16041) accept new shorthands like 3x (3 rows, columns auto-derived) and x4 (4 columns, rows derived from widget count). Either grid_rows or grid_columns can now be a single integer that expands to that many equal FR(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, and on_release now receive an additional point parameter with object-relative coordinates.
    • on_update trigger and trigger: 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:
    @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 sensor entities through the analog Zigbee data model.
    • on_join trigger (#16060) fires when the device joins or rejoins the Zigbee network, with a bool indicating which case.
    • power_source option (#16062) lets devices advertise their power source (battery vs mains).

    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() and wakeable_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_server numeric comparison pairing (#14400) adds the on_numeric_comparison_request trigger 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 update progress messaging (#16321) makes it clear when Zephyr is fetching modules rather than hanging.
    • Native build preparation (#16193) and run_compile hook 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:
    • ESP32-P4 USB High Speed (#14584) by @p1ngb4ck: a new max_packet_size option on usb_host lets 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_timeout accepts 5-60 seconds for power-managed configurations.
    • esp32_ble PSRAM allocation (#15644) by @edwardtfn: a use_psram: true option 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_server split (#15509) by @exciton: a new dedicated modbus_server component, 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-mode modbus_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_mode for ESP8266 (#16055) lets users pin the radio to 11B, 11G, or 11N from YAML to work around routers that misbehave with ESP8266 802.11n associations.

    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=133 timeout after 19 s becomes status=0 success in 228 ms.
    • Production validation against a Yale BETA211123 lock with host BlueZ disabled: clean unlock via the patched proxy.

    Other Notable Features:
    • Voice assistant second audio channel (#16265) by @synesthesiam: a MULTI_CHANNEL_AUDIO feature flag and a second microphone source let voice-pipeline stages receive separately optimized audio.
    • config-hash CLI 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_digits filter (#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.
    • lock open states (#15120) by @egormanga: the lock entity gained OPENING and OPEN states.
    • 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-arduino dependency, 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:
    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.

    Highlights:
    • 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 collapsing namespace foo { namespace bar { ... } } into the modern namespace foo::bar { ... } and enabling the check to prevent regression.
    • bugprone-unchecked-optional-access fixes (#16102, #16103, #16107, #16121, #16124) eliminate a class of unchecked std::optional access bugs across sprinkler, pn532, feedback, time (CronTrigger), tormatic, and haier.
    • 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//hal.cpp and 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 flat packages->3->packages->net->wifi->ssid path.
    • Reproducible ESP-IDF builds (#16008, #16053) make ESPHome builds bit-for-bit reproducible.

    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, including but not limited to @swoboda1337, @kahrendt, @clydebarrow, @jesserockz, @tomaszduda23, @Mat931, @kbx81, @luar123, @crnjan, @diorcety, @rwrozelle, @Komzpa, @egormanga, @guillempages, @edwardtfn, @plazarre, @synesthesiam, and many others.

    Breaking Changes:
    Component Changes:
    • Modbus: server mode has been split out of modbus_controller into a new modbus_server component. Move server_registers to registers and server_courtesy_response to courtesy_response under a top-level modbus_server: block (#15509).
    • API: default max_connections lowered from 8 to 5 on esp32, bk72xx, rtl87xx, and ln882x. Set api.max_connections explicitly if you need more than 5 concurrent API connections (#15889).
    • OTA (esphome platform): multiple ota: - platform: esphome entries with different port: values are now rejected at config time. Use a single entry on one port (#15636).
    • Output: FloatOutput runtime power scaling (min_power, max_power, zero_means_zero, output.set_min_power/output.set_max_power actions) is now gated behind a build flag. You are only affected if you call id(...).set_min_power(...)/set_max_power(...)/set_zero_means_zero(...) from a lambda without ever using the YAML keys or actions. Add min_power: 0% (or any of the other keys) to a single output: entry to re-enable the runtime setters; the build will emit a clear static_assert error with the fix when this applies (#15998).
    • Sensor: the throttle_average filter now rejects time_period values longer than 24 hours (#16169).

    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: WAV as the preferred pipeline format, or use the speaker media player with format: NONE. If you only ever play WAV from arbitrary URLs via YAML actions, add audio: codecs: wav: to your configuration (#16244).
    • Media Player / Speaker / Speaker Source: codec_support_enabled on the speaker media player is now inert and deprecated. Codec inclusion is determined from the pipeline format: setting (use format: NONE to include all codecs, equivalent to the old all mode; format: WAV is the closest equivalent to the old none/false mode) (#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:
    • Core (main loop): every component's loop() now actually runs at the configured loop_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 on loop() being pulled forward by other scheduled work, components will now run less often; use HighFrequencyLoopRequester for fast wakes (#15792).
    • ESP32 BLE Tracker: ESP_COEX_PREFER_BT is now held for the full lifetime of any active BLE connection rather than reverting to balanced coex as soon as the handshake settles. This fixes status=0x85 / 133 GATT failures on bluetooth_proxy setups under heavy WiFi load (notably Yale/August locks). Configurations holding long-lived BLE connections (for example a ble_client persistent sensor) may see lower WiFi throughput while the connection is up (#16036).
    • One Wire: OneWireBus::skip() now performs a bus reset before issuing the SKIP ROM command, fixing a long-standing 1-Wire protocol violation that could cause unreliable readings (such as the DS18B20 power-on 25 °C / 85 °C values). External components or lambdas calling skip() must handle the new bool return value (false if reset/presence pulse failed) (#14669).

    Undocumented API Changes:
    Breaking Changes for Developers:
    • OneWireBus::skip() signature: now returns bool (true on successful presence pulse, false on reset/no-device); also performs a bus reset before the SKIP ROM command. Callers must handle the return value (#14669).
    • ComponentIterator::on_media_player is now pure virtual: subclasses that don't handle media players must add an override { return true; } stub guarded by #ifdef USE_MEDIA_PLAYER (#15618).
    • Heap-allocating helpers moved to alloc_helpers.h / alloc_helpers.cpp: functions like str_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_pretty, and similar have been moved. helpers.h re-exports alloc_helpers.h for backward compatibility until 2026.11.0; update your includes before then (#15623).
    • PollingComponent() default constructor: now initializes to SCHEDULER_DONT_RUN (UINT32_MAX) instead of 1. External components that bypass codegen, call PollingComponent() with no argument, and never call set_update_interval() will stop polling. Pass an interval to the constructor or call set_update_interval() explicitly (#15832).
    • API clients_ storage: APIServer::clients_ changed from std::vector to compile-time std::array; the set_max_connections() runtime setter was removed and the cap is now the MAX_API_CONNECTIONS compile-time define populated from YAML (#15889).
    • FloatOutput power scaling members gated: min_power_, max_power_, zero_means_zero_ and the corresponding setters are now gated behind USE_OUTPUT_FLOAT_POWER_SCALING. Lambdas that call these setters require the YAML to opt in (see the user breaking-changes section); a compile-time static_assert documents 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 ClimateTraits accessors removed: the 10 deprecated get/set_supports_* accessors (current_temperature, current_humidity, two_point_target_temperature, target_humidity, action) are gone after their 6-month window. Use add_feature_flags()/has_feature_flags() with the matching CLIMATE_* flag; note two_point_target_temperature maps to CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE (#16289).
    • EKTF2232 rts_pin migration shim removed: the schema-level rts_pin → reset_pin rename helper is gone; configs still using rts_pin get a generic schema rejection instead of the friendly renamed-to error (#16289).
    • Core ring buffer moved to ring_buffer helper component: include from esphome/components/ring_buffer/ring_buffer.h and use the esphome::ring_buffer::RingBuffer namespace instead of esphome::RingBuffer. The old esphome/core/ring_buffer.h location is deprecated with a 6-month removal window; add AUTO_LOAD = ["ring_buffer"] to your component's codegen (#16298).

    For detailed migration guides and API documentation, see the ESPHome Developers Documentation.

    Original source
  • Similar to ESPHome with recent updates:

This is the end. You've seen all the release notes in this feed!

Releasebot

Curated by the Releasebot team

Releasebot is an aggregator of official release notes from hundreds of software vendors and thousands of sources.

Our editorial process involves the manual review and audit of release notes procured with the help of automated systems.