ESPHome 2026.10.0: Native ESP8266 builds and compressed updates

NOTE
This is a beta release. Details on this page may change before the stable release is published.
Every month ESPHome ships a release like this one, so you can learn about new features, fixes, and other changes worth knowing about before you update.
Short on time? Here’s the quick version
Section titled “Short on time? Here’s the quick version”Here’s the plain-language rundown for anyone short on time or newer to ESPHome; the full technical post continues below for anyone who wants it.
Action needed if you use any of these:
- Lights with an
initial_state:now actually respect it at boot, so if you had a light configured to come up on but it was quietly staying off, double-check the behavior is what you want after updating. - The
codec_support_enabledoption onspeakermedia players is gone; remove it from your config and set the pipelineformat:toNONEto keep the old behavior, otherwise the config will no longer validate. - IR climate devices with
supports_heat: falseorsupports_cool: falseno longer offer the combined heat/cool auto mode by default - addsupports_heat_cool: trueto bring it back if you were relying on it.
New features: Updates install about a third smaller and proportionally faster on every platform ESPHome supports, and ESP8266 builds are considerably quicker end-to-end, including on recent Apple Silicon Macs that no longer need Rosetta.
Also in this release: 20 new components (including Wi-Fi motion sensing, a persistent counter, a new CO2 sensor, USB-UART bridging and TCP-UART), broader Zigbee cluster coverage, live light transition reporting, CSS color names in light actions, and a long list of smaller fixes and improvements.
If you’re running a typical config, there’s nothing else you need to look at before updating - aside from the items above, your setup should carry over cleanly.
If you’re running custom lambdas, external components, or a non-standard config, or you just want the full technical detail, keep reading below for the full release overview and feature writeups.
Components added in this release
Section titled “Components added in this release”Release overview
Section titled “Release overview”ESPHome 2026.10.0 lands two platform-wide defaults that touch most of the fleet: the native ESP8266
Arduino toolchain is now the default build path, retiring Rosetta on Apple Silicon and cutting cold
compile time nearly in half, and OTA uploads are compressed on ESP32, RP2040, LibreTiny and host
through a new deflate decoder, so updates ship about a third fewer bytes and finish proportionally
faster on slow or lossy links. The native API’s protobuf and controller-dispatch hot paths got
another round of per-byte and per-instruction optimizations, 20+ field-packing PRs and a sweep
moving codegen data from heap and RAM into flash tables trim RAM across every build, and the
register_apply_action and sub_* codegen helpers remove hundreds of boilerplate C++ classes and
Python lines across more than 150 components with no YAML changes. ESP-IDF builds now run cmake
and ninja directly instead of through idf.py, and a --skip-bootloader flag plus mbedTLS and
tool-startup trims save several more seconds per compile on Home Assistant Green class hardware. On
top of that, 20 new components land, including Wi-Fi CSI motion sensing, USB-UART bridges with mux
and tap seams, TCP-UART and UART-TCP, SPI Ethernet on KSZ8851SNL, a persistent counter sensor and
a Sensirion STCC4 CO2 sensor, alongside a device-side Home Assistant onboarding wizard, outgoing
API connections for NAT-ed devices, broader Zigbee cluster coverage and new light transition,
color, resume-effect and e-paper features. The bundled Device Builder can now flash every supported
chip family from the browser.
Upgrade checklist
Section titled “Upgrade checklist”- If you have ESP8266 devices, expect the first compile to be a clean build on the new native
arduinotoolchain; settoolchain: platformiounderesp8266:to keep the old path (and plan to migrate before 2027.4.0). - If you set
restore_mode:on alightwithinitial_state: {state: on/off}, verify the boot state is what you want - lights that previously booted off against yourinitial_statewill now follow it. - If you set
codec_support_enabled:on aspeakermedia player, remove it and switch the pipelineformat:toNONEfor the oldallbehaviour; configs that still carry the key now fail validation. - If you rely on a
speakerorspeaker_sourcemedia player muting itself at volume zero (or firingon_mute/on_unmutefrom a volume change), update your automations - volume and mute are now independent. - If you use
climate_irplatforms withsupports_heat: falseorsupports_cool: falseand want to keep theheat_coolauto mode, addsupports_heat_cool: true. - If you set
buffer_size:onremote_receiverfor ESP8266, RP2040, LibreTiny, ESP32-C2 or ESP32-C61, multiply the value by 4 to keep the same number of buffered pulses. - If you have
usb_uartentries that could match the same USB device (overlapping VID/PID), split them withmanufacturerandproductfilters - overlapping entries are now rejected at validation. - If you use
epaper_spiwith saturated pure green or magenta drawn by RGB value, review the output; monochrome conversion now uses perceived luminance. Also dropfull_update_everyabove 1 on panels without partial refresh and addpsram:forseeed-reterminal-sticky. - If you have a
sendspinswitch, addtype: enabledto each entry. - If you build for the
hostplatform, remove unsupportedplatformio_optionskeys and rewrite any separate-argumentbuild_flags(such as-include pre.h) as a single entry; Windows host builds are no longer supported. - If you use
homeassistant.actionwith more than 255 data/variable keys per action, split it - the config is now rejected at validation. - If you write lambdas that read
tagafter adelayinon_tag/on_tag_removed, note thattagis now passed by reference; existing read-only uses continue to work, but code that modified a local copy of the tag now modifies the shared reference.
ESP8266 native toolchain by default
Section titled “ESP8266 native toolchain by default”The native Arduino toolchain foundation that landed dormant in 2026.9.0 is now the default for every
ESP8266 build. @bdraco finished the stack with the compile driver and
dispatch layer (#18539), ESPHome-built xtensa gcc
10.3 archives for Linux x86_64/arm64, macOS x86_64/arm64 and Windows x86_64 served from
esphome-libs/xtensa-lx106-elf-toolchain
instead of the registry package (#19849), and
flipped the default to toolchain: arduino in
#19886. The CI matrix now compiles every ESP8266
component test the same way (#19714,
#19740).
- Faster builds end-to-end - on an M5 Max against the same config an
esphome compileruns in 9.2s cold instead of 17.8s, a warm-ccache rebuild in 2.8s instead of 7.6s, a no-op rebuild in 0.4s instead of 2.2s, and a one-line edit in 9.6s instead of 20.7s. - Apple Silicon without Rosetta - the registry package had no arm64 macOS build, so ESP8266 builds on recent Macs needed Rosetta 2, which Apple ends with macOS 28. The ESPHome-built archives are native on every supported host.
- ESP8266 progress in Device Builder - the native build runs through ninja, so its
[N/M]progress counters feed the device builder a compile percentage the way ESP-IDF builds already do. - Opt-out available, deprecated - a custom framework
source, a framework version other than 3.1.2, an unlisted board or an unsupported host still work withtoolchain: platformio. The PlatformIO path warns on validation and goes away in 2027.4.0.
Roughly 40% of ESPHome devices run on ESP8266, so the change touches a large slice of the fleet; the first build after upgrading is a clean build because the build directory’s toolchain changed.
Compressed OTA uploads on ESP32, RP2040, LibreTiny and host
Section titled “Compressed OTA uploads on ESP32, RP2040, LibreTiny and host”@bdraco extended compressed OTA, which the ESP8266 already had via its gzip-aware bootloader, to every other platform ESPHome supports (#19037). RP2040 takes the same gzip path through the Arduino Pico stub. ESP32, LibreTiny and host inflate a raw deflate stream through a 4 KB window as it arrives, so an image goes over the network about one third smaller than before.
- Automatic negotiation - a client bit and a server bit opt in; old clients and old firmware never see it, so pre-2026.10.0 devices update exactly as before.
- Covered by the digest - the client sends the inflated image’s MD5, so the integrity check covers the decoder.
- Big win on slow or lossy links - ESP32-C6 OpenThread, encrypted WiFi with 15% packet loss and any mesh-hop or VPN upload is transfer-bound; measured on an ESP32 at 15% simulated loss, upload time drops from 317/113s to 74/67s.
- Allocation-safe - the 4.9 KB decoder session is allocated through
RAMAllocator::make_uniqueso a heap-exhausted device falls back to an uncompressed upload instead of rebooting.
The flash cost is about 2 KB on ESP32 and under 100 bytes on ESP8266 and RP2040 for the negotiation glue.
API protobuf throughput and controller dispatch
Section titled “API protobuf throughput and controller dispatch”A sustained push on the native API’s hot paths continued through this release. @bdraco collapsed the three protobuf decode virtuals into one and removed the vtable from decode (#19016, #19029), made generated encode and size entry points type-erased (#19015), passed the encode cursor by value through the protobuf helpers so it stays in a register (#19014), counted packed varints a machine word at a time (#20122), decoded base64 in a single pass (#20123), and kept the write cursor in a register while emitting sub-message tags inline (#20124).
A separate change dispatches controller updates through generated direct calls instead of a runtime
registry: APIServer and WebServer drop the Controller base class and the state-publish path
goes from 25 instructions with 8 loads and 2 indirect calls to 10 instructions with 2 loads and
direct calls only, costing 256 B less flash and 24 B less RAM on a typical ESP32 build
(#19588). The web server also builds state
documents in a stack arena and sends events straight from the caller’s buffers so a state event
does no heap allocation at all (#19376,
#19383).
External components that subclass Controller or hand-write a non-const AudioFile * need small
updates; the migration notes are in each PR.
Memory optimizations across every build
Section titled “Memory optimizations across every build”@bdraco led a tree-wide sweep to shrink per-instance structures and move constant codegen data out of RAM.
- Field packing - roughly twenty PRs grouped single-byte flags next to other byte fields on speaker, mixer, i2s_audio, voice_assistant, microphone, sendspin, template, ledc, rtttl, ld2450, remote_receiver, esp32_ble, improv_ble, wifi and more (#20135 through #20152), reclaiming three bytes of padding at a time across many instances.
- Empty-base packing -
[[no_unique_address]]lets the emptySchedulermutex, empty script argument tuple, empty template unused members and the empty WiFipending_flags struct take no space on single-threaded platforms (#20139, #20140, #20141, #20142);Appdrops from 156 to 152 bytes on ESP8266. - Light state 12 bytes smaller - the effect index became a
uint16_tsharing a word with the flags, the gamma factor rode along in the flash lookup table, and the flash transition length only compiles in when a light setsflash_transition_length(#19711).LightStategoes from 180 to 168 bytes. - StaticVector and FreeRTOS handles embedded in place - the ring buffer, FreeRTOS queue, static task and ESP32 logger buffer now use the static structure as their own handle instead of allocating one next to it (#20129, #20133, #20134).
- Codegen data in flash - audio files, display init sequences, select options, font glyphs, RC
switch protocols, allowed origins, UDP addresses, BLE MAC filter lists, matrix keypad keys, Home
Assistant action fields and select.is option lists are now
constexprtables in flash instead of heap or RAM objects built insetup()(#20072, #20079, #20081, #20085, #20086, #20088, #20089, #20090, #20091, #20108, #20110, #20117, #20237); the Voice PE config saves 192 B of RAM and 272 B of flash from the audio files change alone. - Header inlining - trivial single-store setters moved into headers on template, bme280_base, bme680, bmp280_base, mqtt_subscribe, tsl2561, tsl2591, esp32_camera, web_server, waveshare_epaper, bang_bang, graphical_display_menu, adc, thermostat, openthread, st7789v, max44009, sds011, haier, lc709203f (#19278 through #19299) and setters that pass defaults are skipped at codegen on api, template, uart and version (#19227, #19232, #20066, #20067, #20069).
- ESP8266 log tags and flash strings - every
ESPHOME_LOG_TAGon ESP8266 now rides in flash (#20290, #19574, #19578) and the long-inertesp8266_store_log_strings_in_flashoption that forced them into RAM is deprecated and ignored (#19577). - ESP32 unused subsystems trimmed - a
flash_chipoption underesp32: framework: advanced:drops the unused flash-vendor driver tables, saving up to 5 KB of RAM and image on an ESP32-S3 with octal flash (#19217), andnvs_cache_in_psrammoves ESP-IDF’s internal NVS page cache into PSRAM for an extra ~10 KB of internal heap on boards with guaranteed PSRAM (#19378).
Codegen architecture: register_apply_action
Section titled “Codegen architecture: register_apply_action”@bdraco finished one of the largest coordinated refactors in recent
memory. register_apply_action and register_apply_condition turn an action or condition that
only forwards config values to its parent into a field table with no C++ class and no builder, with
constants and inline lambdas baked into one stateless function
(#19469,
#19655).
Over 80 components moved onto it in this release, covering actions and conditions on cover, fan,
climate, light, switch, valve, lock, number, text, select, event, media_player, speaker,
voice_assistant, mqtt, globals, update, ledc, esp32_ble_tracker, alarm_control_panel, rtttl,
sprinkler, servo, haier, midea, mitsubishi_cn105, nextion, dfplayer, hlk_fm22x, max7219digit,
fingerprint_grow, ezo_pmp, sim800l, senseair, scd30, scd4x, sen5x, nau7802, ds1307, pcf8563,
pcf85063, rx8130, bm8563, pzemac, pzemdc, bl0906, cm1106, hc8, htu21d, hdc302x, cs5460a, cc1101,
sx126x, sx127x, pulse_counter, pulse_meter, display_menu_base, hub75, snapshot, router, animation,
rotary_encoder, audio_dac, online_image, duty_time, pipsolar, aic3204, pmwcs3, mcp4461, ufire_ec,
ufire_ise, max6956, grove_tb6612fng, hbridge, lightwaverf, openthread, mixer, speaker_source,
emontx, zigbee and more. The parallel register_apply_condition sweep covered switch, binary
sensor, fan, light, media player, script, lock, time, mqtt, speaker, display, pn532, tinyusb,
voice_assistant, sun, alarm_control_panel and pid.
Each converted action is one pointer per action instead of one per field, with no per-component
action class. Alongside it a shared register_simple_action / register_parented_action /
register_bare_condition set covers the three remaining builder shapes
(#19321,
#19322). The removed action classes were only
instantiated by codegen and never reachable from user lambdas, so no YAML changes.
Sub-entity codegen helpers
Section titled “Sub-entity codegen helpers”Over 150 hub components wired up optional child entities with the same three-line pattern per key.
@bdraco added new_sub_sensor, new_sub_binary_sensor and
new_sub_text_sensor on top of a shared entity_helpers module, then expanded to seven entity
types with bound sub_* helpers (#19725,
#19754). Over 100 components moved to the new
helpers across this release’s merge window, covering ina2xx_base, pzemac/pzemdc, bl0906/bl0939/
bl0940/bl0942, hlw8012/hlw8032, cse7766, scd30, bme280_base, bme680, bmp280_base/bmp3xx_base/
bmp581_base/spa06_base/mlx90393, bmp085/dps310/lps22/mpl3115a2/ms5611, hdc1080/hdc2010/hdc2080/
hdc302x/hte501, aht10/am2315c/am2320/dht/dht12, atm90e26/atm90e32, ld2410/ld2412/ld2420/ld2450,
sgp30, sgp4x, pmsx003, sps30, hm3301, sun_gtil2, veml7700, tsl2591, tcs34725, hmc5883l, htu21d,
ccs811, inkbird_ibsth1_mini, mopeka_pro_check / mopeka_std_check, kuntze, ufm01, alpha3, max9611,
ufire_ec/ufire_ise, kmeteriso, mcp9600, airthings_wave_base/airthings_wave_plus, ruuvitag,
seeed_mr24hpc1/seeed_mr60bha2/seeed_mr60fda2, mhz19, growatt_solar, havells_solar, sdm_meter,
wireguard, pm2005, ee895, mmc5603/mmc5983, gdk101, smt100, pmwcs3, emc2101, ens160_base, zyaura,
cs5460a, es8388, sht3xd, shtcx, combination, libretiny, sound_level, bthome_mithermometer,
atc_mithermometer, pvvx_mithermometer and a dozen Xiaomi BLE sensors.
Each converted hub drops from three lines per key to a single sub_sensor(), sub_binary_sensor()
or sub_text_sensor() call, so a 600-line hub config in Python becomes a few dozen declarative
calls while the generated main.cpp stays identical.
ESP-IDF build speed
Section titled “ESP-IDF build speed”@bdraco replaced the idf.py wrapper with direct cmake and ninja
calls, matching the native ESP8266 and host paths
(#19871). The firmware is byte-identical across
nine scenarios on ESP-IDF 5.5.5 and 6.1.0 and a CI check runs the real idf.py on a freshly built
tree so an IDF bump cannot drift unnoticed. The win is Python startup and the removed second
ninja all: about 5 seconds saved per build on a Home Assistant Green.
Several supporting pieces compound the saving.
--skip-bootloaderflag for OTA-only builds disables the bootloader sub-build when the update will never deliver one, saving 7.5 s per compile on a warm ccache on an M5 Max and proportionally more on Green-class hardware (#19925).- Build-and-size in one ninja run instead of two
(#19997) and the
json2size data emitted so the link edge is not blocked (#18848). - ESP-IDF tool paths resolved once per build and the IDF version read in-process instead of
starting
idf_tools(#19996, #19998). - mbedTLS trimmed to the client-only defaults needed by ESPHome’s HTTPS and MQTT paths, and disabled entirely when no component needs crypto (#18877, #19088).
- nRF52 shared ccache between devices and native-toolchain cache settings (#20027, #20028).
Native API outgoing connections
Section titled “Native API outgoing connections”@bdraco added outgoing_connection under api: so the device opens
the native API connection itself instead of waiting to be reached
(#18919). With no host: the device remembers the
last dial-back client and reconnects when that client stops reaching it; with host: set the
device always dials out, which covers a peer behind NAT or on a routed segment it cannot reach
inbound.
- Noise only - the option requires
encryption:because the connection can leave the LAN and the shared key is the only trustworthy way to verify the peer. - Protocol unchanged - the device stays the Noise responder; it only sends its server hello first so the peer can pick the key by name and MAC.
- Deep-sleep aware - deep-sleep builds skip the connect delay and dial as soon as the network is up.
The cost on an ESP32 is 32 B of RAM with outgoing_connection plus a host, 48 B when learning
the target. aioesphomeapi 46.4.0 and Home Assistant core already know how to answer.
Setup wizard for Home Assistant onboarding
Section titled “Setup wizard for Home Assistant onboarding”@jesserockz added a device-side setup wizard that Home Assistant
can show during onboarding (#19966). A wizard:
block under api: describes a series of pages with device entities to highlight (an enable
switch, for example) and inputs that pick Home Assistant entities the device needs. Inputs can be
linked to a homeassistant-platform entity so the device subscribes to it automatically, or stand
alone as a plain entity id a lambda can use.
The wizard is fixed at build time and compressed with zstd on the Python side, so the device ships one blob out of flash and the format can grow without new proto fields. On an ESP32-IDF build a single-switch wizard adds about 0.4 KB of flash; a config with no wizard adds nothing.
NOTE
The wizard will not be shown until Home Assistant 2026.11. Older versions ignore it and add the device as usual.
Light transition state publishing and color names
Section titled “Light transition state publishing and color names”@nohat added an opt-in transition_state_publish_interval on every
light so Home Assistant and MQTT subscribers see the live value during a fade or flash instead of
just the start and end (#11968). Values below 150
ms are rejected; 200 to 250 ms is a good value. The build pays nothing unless at least one light
sets the option, and each light grows by 8 bytes of RAM when the option is enabled. This also lays
the groundwork for hold-to-dim.
Two other light-side quality-of-life improvements landed from
@clydebarrow: CSS color names and 0xRRGGBB values anywhere a
light state is specified, including strobe and addressable_color_wipe
(#19712,
#19874); and resume_effect: from
@TheRealChriAnd that resumes the running effect after a plain
turn-off / turn-on (#17791). restore_mode is
replaced by a more expressive restore_state that can leave, invert or force a specific value per
property, with a warning when a legacy restore_mode would silently discard an explicit
initial_state (#18997).
Host platform builds natively
Section titled “Host platform builds natively”@jesserockz moved the host platform off PlatformIO onto ninja and
the machine’s own compiler, matching how ESP32 and ESP8266 already build
(#19214). Compilers are picked from PATH
(g++/gcc first, then clang, honouring CC/CXX), ccache is used when present, and libraries
go through the shared converter into archives. Build-flag routing follows SCons semantics, and
cg.set_cpp_standard replaces the old -std=gnu++20 build flag so C sources never see a C++
standard. Measured on an M5 Max against the same host config and the same 128 objects, a cold
build takes 2.4 s instead of 3.9 s, a no-op rebuild 0.3 s instead of 1.2 to 1.4 s, and a clean
build with a warm ccache 0.5 s instead of 4.0 s.
USB bridging, mux and serial proxy taps
Section titled “USB bridging, mux and serial proxy taps”A connected effort landed around sharing physical ports between a USB host, an on-device component
and remote API consumers. Starting from @kbx81’s new
bridge component with its first cdc_acm_uart platform
(#11689), the ESP32-S2/S3/P4 can present as a
USB-to-UART bridge with dynamic line coding, DTR/RTS output, and clean pause()/resume() for
sharing the UART with on-device code. The new uart_mux component
(#19066) routes a UART to either the bridge or a
local consumer like zwave_proxy, driven by tinyusb on_mount / on_unmount automations added
in #19067.
Alongside it, serial_proxy grew a protocol-agnostic tap seam and port mode so Home Assistant can
flash through a port that normally carries a protocol
(#18955) and a USB_SERIAL port type plus a
SerialProxyIdentity message so clients can target a specific USB device even when the topology
changes (#19308, by
@puddly). usb_uart gained descriptor filters for iManufacturer and
iProduct for devices that share VID/PID (#19095)
and a claim_comm_interface: false option that frees a USB host hardware channel per device on
channel-poor hosts like the ESP32-S3
(#18969).
Networking, ethernet and connectivity
Section titled “Networking, ethernet and connectivity”- TCP-UART bridges - @Bascht74 added two complementary
components: tcp_uart presents a remote TCP socket as a UART, so Modbus
and similar can talk to a network-reachable serial device without an on-device hardware UART
(#19884); uart_tcp
copies a hardware UART to a TCP socket in either direction
(#19887). A server role with IPv4 allow-lists,
a shared
TcpListener, and a diagnosticdisconnectscounter follow in #20026, #20037 and #20096. - KSZ8851SNL SPI Ethernet - @jesserockz added Microchip’s
KSZ8851SNL to the
ethernetcomponent’s SPI chip list, so boards built around it can now use their Ethernet port (#19883). - Forced WiFi roam check - a new
wifi.roamaction andforce_roam()API call trigger an immediate roaming scan from automations (#17349, by @clydebarrow). - ESP32 UART clock source - @ikruglov added a
clock_sourceoption that keeps a UART stable through dynamic frequency scaling: a 9600 bps Modbus RTU bus that was flooded by timeouts under DFS runs cleanly withclock_source: XTAL, and idle power drops noticeably because DFS becomes usable (#16486).
Signed OTA mDNS and plaintext migration lane
Section titled “Signed OTA mDNS and plaintext migration lane”@kbx81 made devices built with signed_ota_verification advertise an
ota_signed=1 TXT record on _esphomelib._tcp, so a discovery client like Device Builder knows up
front it needs a trusted key before compiling anything
(#19407). To bridge the long tail of devices that
cannot encrypt yet, @bdraco added allow_plaintext_upload: true under
ota: encryption: so one migration install can still land after the plaintext fallback is removed
in 2027.3.0; the uploader logs that the install went plaintext and tells the user to remove the
option once the device is seen encrypting (#19396).
ESPHome Device Builder
Section titled “ESPHome Device Builder”The bundled ESPHome Device Builder can now flash every chip family that ESPHome supports from the browser. Before this release, browser flashing worked only for ESP32 and ESP8266. This section covers the main changes merged since the 2026.9.0 release notes.
- nRF52 - @tomaszduda23 added flashing through the Adafruit bootloader’s Nordic Legacy DFU (Device Firmware Update) protocol (frontend#1784), MCUboot updates over Bluetooth and serial (frontend#1891), and logs over USB serial and Bluetooth (frontend#1788). The Bluetooth logs reconnect when the link drops (frontend#1868).
- Raspberry Pi Pico - RP2040 and RP2350 boards flash over WebUSB with PICOBOOT, the Pico’s USB boot protocol (frontend#1785, frontend#1927). Logs work over Web Serial (frontend#1786). Firefox has no WebUSB, so it downloads the UF2 firmware file and shows how to copy it to the board.
- LibreTiny - Realtek RTL8720C and RTL8710B, Beken BK72xx and Lightning LN882H boards flash over Web Serial through a USB serial adapter (frontend#1796, frontend#1977, frontend#1929, frontend#1972). Logs work over Web Serial too (frontend#1804, frontend#1934, frontend#1973).
- Plain HTTP dashboards - browsers block USB access when the dashboard is served over plain HTTP. In that case the dashboard sends the firmware to ESPHome Web, which flashes it. This already worked for ESP; it now works for every family above (frontend#1869, frontend#1923, frontend#1926, frontend#1935, frontend#1978).
- ESPHome Web - web.esphome.io supports the same families. nRF52 gets logs and MCUboot updates (frontend#1799, frontend#1913). Pico boards flash over PICOBOOT (frontend#1814). BK72xx and LN882H are new (frontend#1933, frontend#1973). RTL8720C and RTL8710B share one RTL87xx entry that reads the chip from the firmware file (frontend#1986).
Other Device Builder changes in this release:
- Signed OTA devices - a device that advertises
ota_signed=1rejects any OTA update that is not signed by a key it trusts. After adoption, the user’s own build is unsigned. When the config has no signing key, @kbx81 made the dashboard flash the first build over USB (#2895, frontend#1991). - Faster OTA builds - OTA updates compile with the new
--skip-bootloaderflag. On ESP32, the build skips the bootloader and factory image, which an OTA update never uses. This saves tens of seconds per device on a Home Assistant Green. Other platforms run a full build as before (#2874). - Optional MCP endpoint - MCP (Model Context Protocol) is a standard way for an AI assistant to
call tools. The new
/api/mcpendpoint lets an MCP client, such as Home Assistant’s MCP integration, read and edit configs, add components and automations, and validate, compile and install (#2733, #2736, #2789). It is optional. The Device Builder never calls an AI model itself. The endpoint does nothing until you connect an MCP client, and everything else works the same without one. - Offline duration - @siegeld added how long a device has been
offline. The status pill shows
Offline 2h(#2842, frontend#1862). - Network column - @davidcoulson added a Network column and filter. It shows whether a device uses WiFi or Ethernet (#2692, frontend#1737).
- Analyze memory - Expert Mode has a new action that runs
esphome analyze-memory. It shows the flash and RAM use of each component (#2835, frontend#1816). - Flash chip setting - the ESP32 framework form shows the new
flash_chipoption (#2870). - Version history - the Device Builder now only commits to a git repository rooted at the
ESPHome config directory. If your whole Home Assistant
/configis a git repository, ESPHome edits are no longer committed to it (#2882).
New components
Section titled “New components”The release adds a strong batch of new components and platforms:
- tfluna by @candrews adds the Benewake TF-Luna I2C LiDAR with distance, signal strength and temperature sensors (#9199).
- bridge +
cdc_acm_uartby @kbx81 - the USB-UART bridge domain described above (#11689). - rx8025t by @remcom adds the Epson RX8025T I2C real-time clock (#13274).
- systa_bus by @Mat931 reads Paradigma SystaSolar controllers over UART (#15473).
- tas2780 by @remcom adds the TI TAS2780 mono Class-D amplifier with gain, power mode and channel options (#15708).
- xiaomi_body_scale by @dckiller51 covers the Xiaomi Body Composition Scale S400 and Smart Scale S200 over MiBeacon v5 (#16476).
- pzem6l24 by @nuttytree reads the Peacefair PZEM-6L24 three-phase energy monitor over Modbus RTU (#17849).
- icnt86 by @danepowell supports the ICNT86 I2C touch driver used on Waveshare touch e-paper displays (#18331).
- uart_mux by @kbx81 shares a UART
between a bridge and local consumers like
zwave_proxy(#19066). - ir_rf_proxy + ir_rf_base by
@bdraco splits the near-duplicate
infraredandradio_frequencycomponents onto a shared base and adds a transmit-complete reply so paced clients cannot stall the main loop (#19086). - stcc4 by @j9brown implements the Sensirion STCC4 CO2 sensor with ambient pressure compensation (#19434).
- pn71xx by @kbx81 merges the PN7150 and PN7160 drivers, fixes NCI transport and card-emulation bugs, and shares actions across both chips (#19589).
- tas58xx by @remcom adds the TI TAS5805M stereo Class-D amplifier with mixer modes, fault monitoring and runtime calibration; a follow-up by @mrtoy-me adds the TAS5825M model (#19595, #19971).
- counter by @clydebarrow adds a 64-bit persistent counter sensor that accumulates from another sensor, binary pulses or actions (#19746).
- espectre by @francescopace brings Wi-Fi Channel State Information motion sensing to ESPHome: an ESP32 and an existing WiFi AP act as a motion detector without additional sensing hardware (#19801).
- exponential_moving_average by @clydebarrow adds a filter-shaped sensor that preserves the average across reboots (#19882).
- tcp_uart and uart_tcp by @Bascht74 - see Networking above.
unicodeby @ssieb movesextract_unicode_codepointout offontinto its own shared component (#19969).- xiaomi_mccgq02hl by @ahpohl adds the Xiaomi Mijia Door/Window Sensor 2 with light and battery readings (#20104).
Other notable features
Section titled “Other notable features”- Zigbee for ESP32 grows substantially - @luar123 added
measurement-cluster support (
cluster: device_class) that exposes sensors over typed Zigbee clusters instead of generic analog/binary input (#17388), switch support (#17579) and number support (#20243). - Motion gestures and orientation - @clydebarrow added
orientation sensors, face-up/face-down/free-fall/moving binary sensors and a shake event platform
on the
motionIMU hub (#16787). - Homeassistant text, select and button platforms by @jesserockz mirror live HA entities onto the device with no heap allocation after setup (#19964).
- Resampler microphone platform by @remcom converts a 48 kHz mic to the 16 kHz voice_assistant and micro_wake_word need, laying the groundwork for full duplex I2S (#19953).
- Audio-HTTP private CA by @TheSwert lets TTS and announcement
playback trust a private CA, inheriting from
http_requestwhen the per-source key is omitted (#19392). - Snapshot animated GIFs - @clydebarrow extended the
snapshotdisplay platform to record short animated clips (#19421), and the e-paper driver gained afull_update_nextaction, four-level grayscale on the Seeed reTerminal Sticky, a fix for partial-refresh fading on SSD1677 panels (#19213, by @FuNK3Y), SSD1681 support (#18073, by @snoack) and anis_updatingcondition (#20261). - I/O expander selection by I2C address - @clydebarrow lets canned display configs on boards with multiple identical expanders (M5Stack Tab5) pick the right hub by address instead of relying on single-instance auto-pick (#18939).
- Template time by @ilikecake adds a lambda-driven time platform for simulation and testing (#14838).
!includewith acondition:by @j9brown makes conditional file inclusion a first-class YAML construct instead of a two-step substitution trick (#19455).esphome:suspend_loop:andloop_interval:by @luar123 let low-power configs sleep longer with no watchdog risk and no responsiveness loss when a component wakes the loop (#18136).- mk2pvrouter by @FredM67 gained tag-based sensor defaults, a binary_sensor platform and a text_sensor platform (#18870, #19719, #19720).
- Hoermann HCP by @RaHehl adds the motor’s serial number and
firmware version as diagnostic text sensors, a
door_statetext sensor that distinguishes vent, half-open and stopped states (#19543, #19747), and switches to one-shot commands with the lamp tracked as an absolute state (#19765). - LVGL animations and MIPI AMOLED light - @clydebarrow added
animatable scale properties,
ease_in/ease_outcurves, and adebug_outlineoption (#19420), plus a MIPI SPI light platform for AMOLED brightness control (#20212). - Sendspin by @kahrendt gained manufacturer/model/firmware identity options (#18792), a switch platform to enable and disable the client (#19361) and mixer ducking that now goes up to 255 dB (#19347).
- nRF52 2001-baud touch by @bdraco reboots an nRF52 straight into the application without going through DFU, so a host can “restart device” over USB (#19727).
- ESP32
remote_receiverbuffer - @bdraco resized the RMT ring from a flat 10 KB to four slots ofreceive_symbols, saving about 15 KB of free heap per two receivers with no decode regression (#19100); #19101 also fixed a decade-old bug wherebuffer_sizewas documented in bytes but allocated as entries on non-RMT platforms. - i2s_audio volume ramping by @kahrendt smooths software volume
changes to avoid clicks (#19302), and
speaker/speaker_sourcenow treat a volume of zero as true silence regardless ofvolume_minwhile keeping mute independent of volume (#18412, #19305, #19307). - Zephyr mcuboot status LED by @tomaszduda23 exposes the bootloader-driven update LED (#16322).
- AEG Hob2Hood IR protocol by @Mat931 adds the fan and light
commands used by AEG induction hobs on
remote_base(#4687). - PID climate deadband tuning by @ngolf exposes deadband multipliers and thresholds as runtime actions (#14730).
- Mipi SPI SPD2010 by @grischard adds the Waveshare
ESP32-S3-Touch-LCD-1.46 to
mipi_spi(#19056). - BTHome
0x45and0x2E- @mtmatteo79 decoded the standard BTHome v2 temperature (0.1 C sint16) and humidity (1 % uint8) objects, so Shelly BLU H&T sensors now report their values instead of silently publishing nothing (#19956). - DFPlayer by @myztillx added a
dfplayer.set_current_track_repeataction so the repeat flag can be toggled mid-playback (#9186).
Codebase correctness and test coverage
Section titled “Codebase correctness and test coverage”A long sweep by @bdraco and others gave over 60 classes a
user-provided default constructor so they no longer carry 1264 bytes of value-initialised zero
fill at every boot: App itself (#19109) and
StaticVector (#19110), then
binary_sensor::BinarySensor, text_sensor::TextSensor, TemplateButton, TemplateSensor,
TemplateTextSensor, TemplateEvent, TemplateBinarySensor, TemplateSelect,
SettleFilter, DelayAction, StatusBinarySensor, VersionTextSensor, UptimeSecondsSensor,
InternalTemperatureSensor, IDFUARTComponent, GPIOBinarySensor, SafeModeComponent,
IntervalSyncer, IPAddressEthernetInfo, RestartButton, HttpRequestIDF,
PerformForcedCalibrationAction, FactoryResetButton (ld2450), BaudRateSelect,
DistanceResolutionSelect, LightOutControlSelect, BluetoothSwitch, EngineeringModeSwitch,
MultiTargetSwitch, LightThresholdNumber, PresenceTimeoutNumber, Automation,
ESP8266PWM, BluetoothConnection, BluedroidGattClient, GPIOSwitch, RestartSwitch,
DelayedOnFilter, DelayedOffFilter, PCF8574GPIOPin and PCA9554GPIOPin
(#19111 through
#19210). On an ESP32-S3 Apollo config this alone
removes one 1264 B memset and shaves 20 B of flash per class.
Many previously untested branches on binary_sensor, sensor, climate, fan, light, select, event, cc1101, udp, espnow, sx126x, sx127x gained component tests, and GCC format warnings on climate, display, sensor, uart, light, mk2pvrouter, speaker, midea, modbus, dlms_meter, api, json, micro_wake_word, ufm01 and xdb401 were fixed so a tightened format-sensitive build is clean (#19936 through #19989 and many others).
Thank you, contributors
Section titled “Thank you, contributors”This release includes 786 pull requests from over 60 contributors. A huge thank you to everyone who made 2026.10.0 possible:
- @bdraco - 564 PRs including the native ESP8266 toolchain by default,
compressed OTA on every platform, native API outgoing connections,
register_apply_action, direct ESP-IDF cmake/ninja builds and tree-wide protobuf and memory optimizations - @clydebarrow - 22 PRs including the LVGL scheduling and animation overhaul, motion gesture detection, animated GIF snapshots, I/O expander pin providers and a sweep of light enhancements (color names, strobe fix, persistent EMA and counter sensors)
- @Bascht74 - 19 PRs building the new
tcp_uartanduart_tcpcomponents on top of a reusable TCP client link, listener, IPv4 allow list and reconnect helpers - @jesserockz - 17 PRs including the Home Assistant device wizard,
homeassistanttext/select/button platforms, KSZ8851SNL SPI ethernet and the native host build via ninja - @kbx81 - 16 PRs introducing the new
bridgeanduart_muxcomponents, serial_proxy and tinyusb extensions, and PN71xx/PN532 NFC transport and tag-handling fixes - @kahrendt - 13 PRs updating esp-audio-libs to 4.x, refining speaker DAC/volume handling and extending sendspin with mDNS runtime enable/disable and a switch platform
- @luar123 - 11 PRs adding Zigbee switch and number support,
measurement-cluster
device_classexposure, and newsuspend_loop/loop_intervalcore options - @swoboda1337 - 11 PRs bumping ESP32 Arduino/platform and wireguard libraries, refining climate_ir encoding and tightening esp32_hosted compatibility
- @leodrivera - 8 PRs reworking climate_ir supported-mode handling and shrinking state across fujitsu_general, gree and mitsubishi
- @tomaszduda23 - 7 PRs adding initial nRF Connect SDK 3.4.0 / Zephyr 4.x build support, BLE NUS MTU and nRF52 deep-sleep power management
- @remcom - 5 PRs including the new RX8025T RTC, TAS2780 and TAS58xx audio amplifiers, and the resampler microphone platform
- @j9brown - 5 PRs including the new STCC4 CO2 sensor and conditional
!includesupport in substitutions - @FredM67 - 4 PRs adding mk2pvrouter binary_sensor and text_sensor platforms with tag-based defaults
- @puddly - 3 PRs adding USB host descriptor filters and the serial_proxy USB_SERIAL port type
- @davidcoulson - 3 PRs fixing IPv6 SLAAC behavior on wifi and ethernet, and adding a bluetooth_proxy advertisement filter hook
- @RaHehl - 3 PRs extending hoermann_hcp with serial number, firmware version and door-state text sensors
- @vonpanda - 3 PRs fixing the platformio ccache path for non-root containers and tidying CLI address-cache handling
- @Mat931 - 2 PRs adding the new systa_bus component and the AEG Hob2Hood infrared protocol
- @rwrozelle - 2 PRs rewriting openthread’s esp.cpp against the new
esp functions and fixing
set_sockaddr()IPv4 validation - @jeroen85 - 2 PRs tightening web_server_idf EventSource timeout and HTTP header limit handling
- @guillempages - 2 PRs cleaning up http_request and runtime_image log output
- @exciton - 2 PRs fixing deferred modbus server replies and silencing unused-function warnings in user builds
- @candrews - the new TF-Luna LiDAR distance sensor component
- @francescopace - the new
espectreWi-Fi CSI motion detection component - @nuttytree - the new PZEM-6L24 three-phase energy monitor sensor
Also thank you to @asergunov, @myztillx, @nohat, @ngolf, @ilikecake, @dckiller51, @ikruglov, @TheRealChriAnd, @snoack, @danepowell, @carriewattsmake, @iago-veiga, @matt123p, @grischard, @rexmoriarty, @FuNK3Y, @pleasantone, @TheSwert, @Xiretza, @oxynatOr, @snabb, @dalklein, @tronikos, @n-IA-hane, @ryan-ronnander, @crnjan, @elpollodiablo, @catthetech, @techdregs, @patagonaa, @michaelheichler, @randybb, @cpruijsen, @mtmatteo79, @heythisisnate, @ssieb, @mrtoy-me, @ahpohl, @abderbejaoui, @makstech for their contributions, and to everyone who reported issues, tested pre-releases, and helped in the community.
Breaking changes
Section titled “Breaking changes”Platform changes
Section titled “Platform changes”- Host: The
hostplatform now builds natively with ninja instead of PlatformIO. Keys underesphome.platformio_optionsother thanbuild_unflags,lib_depsandlib_ignoreare ignored with a warning, andplatformio_options.build_flagsstill works but is deprecated in favour ofesphome.build_flags. Flags that take a separate argument (such as-include pre.h) must be written as a singlebuild_flagsentry;--toolchain platformiois rejected on host. Windows is refused because the host sources use POSIX interfaces. #19214 - ESP8266: The native
arduinotoolchain is now the default. To keep the previous behaviour, settoolchain: platformiounderesp8266:or pass--toolchain platformioon the command line. A custom frameworksource, a framework version other than3.1.2, or an unlisted board still requires the PlatformIO toolchain, which now emits a deprecation warning and will be removed in 2027.4.0. #19886
Component changes
Section titled “Component changes”- Light: Added a new
restore_state:option that replacesrestore_mode:. The old key still works and remains valid, but is now deprecated. A light configured withinitial_state: {state: on}and no explicitrestore_mode:now actually boots on, instead of being silently forced off by the previousALWAYS_OFFdefault. Monochrome colour mixing foron/offstate now prefersON/OFFtokens. #18997 - Speaker: Removed the deprecated
codec_support_enabledoption from thespeakermedia player. Configs that still set the key now fail validation; use the pipelineformat:setting, withformat: NONEreplacing the oldallmode. #19074 - Speaker: In the
speakermedia player, volume and mute are now independent. Setting the volume to zero is silent but no longer reports the player as muted,on_mute/on_unmuteno longer fire from volume changes, and changing the volume while muted keeps the mute on. #19307 - Speaker Source: The same volume/mute independence now applies to the
speaker_sourcemedia player: a saved volume of zero no longer forces mute on at boot, andon_mute/on_unmuteno longer fire from volume changes. #18412 - Serial Proxy: Added a tap interface and port mode (
RAW/PROTOCOL), controlled only by the live subscriber via a new API request. Every port write, configure, modem-pin, flush and mode operation now requires being the current subscriber. #18955 - USB Host / USB UART: Added
manufacturerandproductdescriptor filters for theusb_uartplatform. Validation is stricter: twousb_uartentries can no longer match the same USB device, so configurations with overlapping VID/PID entries now fail at validation time. #19095 - Remote Receiver: On ESP32 with the current RMT driver, the ring buffer is now sized from
receive_symbolsby default (four slots, around 3136 bytes at the defaults) instead of a fixed10kb. Explicitbuffer_size:values still override it. #19100 - Remote Receiver: On ESP8266, RP2040, LibreTiny, ESP32-C2 and ESP32-C61,
buffer_size:is now interpreted as bytes rather than entries, matching the validator. The default moves from1000bto4000bso the number of buffered pulses stays the same; an explicit value must be multiplied by 4 to keep the same pulse count. #19101 - E-Paper SPI: Monochrome colour conversion now uses Rec.601 luma, so some saturated colours render differently
(pure green
(0,255,0)was black and is now white; magenta(255,0,255)was white and is now black). Grays, black, white, red, blue, yellow and cyan are unchanged.full_update_everyabove 1 is rejected on panels without partial refresh,seeed-reterminal-stickynow requirespsram:, SSD1677 panels with partial refresh allocate a second frame buffer, andseeed-reterminal-sticky-gray4requires a width that is a multiple of 8 whendimensions:is overridden. #19213 - Climate IR:
CLIMATE_MODE_HEAT_COOLis no longer advertised when eithersupports_heat:orsupports_cool:is false. A newsupports_heat_cool:key overrides the default when a device does not follow the rule. Platforms built onclimate_ir(exceptmitsubishi, which is unchanged, andgree, which changes only forsupports_cool: false) no longer offer theheat_coolmode in those configurations. #19483 - Hoermann HCP: Commands are now sent once per status answer, following the Hoermann bus accessory. Open, close, set position, vent and half-open requests issued while the door is already moving now only stop the door, so a burst of commands can no longer reverse or restart it at speed. The lamp is tracked as an absolute state. #19765
- Sendspin: The
sendspinswitch platform now requires atype:option. The only supported value today isenabled. #20189
Undocumented API changes
Section titled “Undocumented API changes”These are C++ API changes that are not covered by the formal breaking change policy because the methods are internal or undocumented. Lambda users and external component authors may still want to be aware of them.
Removed deprecated members
Section titled “Removed deprecated members”-
Sensor: The public
raw_statemember is gone. Useget_raw_state()instead. #19077// Beforefloat v = id(my_sensor).raw_state;// Afterfloat v = id(my_sensor).get_raw_state(); -
Modbus Controller: The forwarding shims in the
modbus_controllernamespace forvalue_type_is_float(),modbus_register_read_function(),modbus_register_write_function(),c_to_hex(), the*_from_hex_str()family, the vector overload ofget_data(),mask_and_shift_by_rightbit(),number_to_payload(),payload_to_number()andfloat_to_payload()are removed. The replacements live inmodbus::helpers. The span overloads andcoil_from_vector()are kept until 2027.2.0. #19076// Before, in a lambdamodbus_controller::c_to_hex('A');// Aftermodbus::helpers::c_to_hex('A');
Remote base protocol methods are no longer virtual
Section titled “Remote base protocol methods are no longer virtual”- Remote Base:
encode,decodeanddumponRemoteProtocol<T>are now plain members. External protocol classes must dropoverrideon these methods, and listeners / dumpers that previously registered themselves fromsetup()must register fromto_code()using the newremote_base.add_listener(),remote_base.add_dumper()andremote_base.attach_receiver()helpers. A protocol used only from C++ or a lambda (for exampleid(tx).transmit<remote_base::NECProtocol>(data)) needsremote_base.request_protocol("<name>")to keep its source in the build. #19084
NFC: tags passed by reference, records in a fixed-size list
Section titled “NFC: tags passed by reference, records in a fixed-size list”The nfc stack now avoids per-tag heap churn. Most lambdas keep compiling, but the shape of tag and
get_records() changed.
- NFC:
on_tagandon_tag_removedtriggers now receiveconst nfc::NfcTag &instead of a copy;nfc::get_ha_tag_ndef()/nfc::has_ha_tag_ndef()now takeconst NfcTag &. Range-for loops,tag.get_uid(),tag.get_tag_type(),tag.has_ndef_message()andtag.get_ndef_message()compile unchanged. Code that modified its local copy of the tag will now modify the shared reference. #19902 - NFC:
NdefMessage::get_records()now returnsconst nfc::NdefRecords &(aStaticVectorofunique_ptr) instead ofconst std::vector<std::shared_ptr<NdefRecord>> &. Iteration withconst auto &record : ...andrecord->...still works. Copying an element out (auto r = records[0];) or copy-assigning oneNdefMessageto another no longer compiles. #19912
PN71xx (PN7150 / PN7160) shared base
Section titled “PN71xx (PN7150 / PN7160) shared base”- PN71xx:
pn7150andpn7160now share apn71xxbase.set_tag_emulation_message()andset_tag_write_message()now take(const std::string &, bool)instead ofoptional<>values; lambdas passing a plain string are unaffected. The action and condition classes (pn7150::EmulationOnActionand friends) are removed in favour ofregister_apply_action/register_apply_condition.TestModemoved from thepn7150andpn7160namespaces topn71xx, so lambdas callingset_test_mode()must now usepn71xx::TestMode. #19589
Climate IR mode storage
Section titled “Climate IR mode storage”-
Climate IR: The protected
supports_cool_,supports_heat_,supports_heat_cool_,supports_dry_andsupports_fan_only_fields are replaced by oneclimate::ClimateModeMask modes_. External components that derived fromClimateIRand read or wrote the booleans must switch to the mask. #19580// Beforeif (this->supports_heat_) { ... }this->supports_dry_ = true;// Afterif (this->modes_.count(climate::CLIMATE_MODE_HEAT)) { ... }this->set_mode_supported_(climate::CLIMATE_MODE_DRY, true);
Audio files are flash constants
Section titled “Audio files are flash constants”- Audio File: Codegen now emits each
audio_fileentry as aconstexpr audio::AudioFilein flash and the id resolves toconst audio::AudioFile *.SpeakerMediaPlayer::play_file,AudioPipeline::start_file,AudioReader::startandNamedAudioFile::filenow takeconst audio::AudioFile *. Passingid(my_file)still works; only C++ that stored the id in a non-constaudio::AudioFile *needs aconstadded. #20072
Home Assistant action internals
Section titled “Home Assistant action internals”-
API:
HomeAssistantServiceCallAction, the internal class codegen builds forhomeassistant.action,homeassistant.eventandhomeassistant.tag_scanned, no longer hasset_service,init_data,init_data_template,init_variables,add_data,add_data_templateoradd_variable, and its constructor changed;TemplatableStringValueandTemplatableKeyValuePairare removed. External components should use the documentedCustomAPIDevicemethods. A map with more than 255 entries is now rejected at config validation. #20237// Beforeauto *a = new HomeAssistantServiceCallAction<>(global_api_server, false);a->set_service("light.turn_on");a->init_data(1);a->add_data("entity_id", "light.kitchen");a->play();// Afterthis->call_homeassistant_service("light.turn_on", {{"entity_id", "light.kitchen"}});
Hoermann HCP lamp control
Section titled “Hoermann HCP lamp control”- Hoermann HCP:
toggle_light()andcancel_light_toggle()are removed; lambdas that drove the lamp through them should useid(hcp).set_light(!id(hcp).is_light_heading_on()). #19765
Breaking changes for developers
Section titled “Breaking changes for developers”- Light:
LightStateand the restore pipeline were rewritten so a light’s state is now assembled by generated lambdas rather than a fixedrestore_modeswitch; subclasses that reached into those internals need updating. #18997 - Light:
LightStateshrunk by 12 bytes per light; internal field layout changed. #19711 - Core:
Action::stop_complexandAction::is_runningare no longer virtual; overrides must be removed. #19662 - Core: Controller updates are now dispatched through generated direct calls rather than virtual overrides. #19588
- Core:
selectoptions are now stored in a sharedConstVectorkept in flash; direct access to the old option vector no longer compiles. #20117 - Core: Static FreeRTOS task TCBs now live in internal RAM; code that relied on the previous placement must adjust. #19426
- HTTP Request:
set_url()andset_method()now takeconst char *instead ofstd::string. #19215 - Modbus Controller: Removed deprecated helper shims from the
modbus_controllernamespace; use the equivalents undermodbus::helpers. #19076 - Modbus: Removed the deprecated
send()entry point and thedisable_crcvalidation stub. #19079, #19078 - Sensor: Removed the deprecated public
raw_statemember; useget_raw_state(). #19077 - Template: Removed the deprecated
bypass_before_arming()helper. #19075 - Nextion: Removed the deprecated
get_wave_chan_id(). #19080 - Remote Base:
encode,decodeanddumponRemoteProtocol<T>are no longer virtual; protocol classes must dropoverride. Listener and dumper registration moves from C++setup()to codegen viaremote_base.add_listener()/remote_base.add_dumper(); a protocol used only from C++ or a lambda must callremote_base.request_protocol(). #19084 - Network: Removed the deprecated
IPAddress::str()from the host build. #19528 - PN532 / NFC: Removed runtime heap allocation from the frame and tag paths;
NfcTagis passed to triggers by reference and the internal listener list is now in static storage. #19744, #19902, #19912 - nRF52: Builds now fetch only the nRF Connect SDK projects they need; external components relying on the full checkout must declare the modules they require. #19735
For detailed migration guides and API documentation, see the ESPHome Developers Documentation.
Full list of changes
Section titled “Full list of changes”For the complete list of every merged pull request in this release, see the full 2026.10.0 changelog.



