Skip to content

J. Nick Koston

48 posts by J. Nick Koston

Default-Constructed PollingComponent No Longer Polls

The no-argument PollingComponent() constructor now initializes the update interval to SCHEDULER_DONT_RUN (UINT32_MAX) instead of 1. External components that subclass PollingComponent, instantiate it with no constructor argument, and never call set_update_interval() will stop polling. Pass an interval to the constructor or call set_update_interval() explicitly.

This is a breaking change for external components in ESPHome 2026.5.0 and later.

FloatOutput Power Scaling Fields Gated Behind USE_OUTPUT_FLOAT_POWER_SCALING

The min_power_ / max_power_ / zero_means_zero_ fields and their setters on FloatOutput are now gated behind a new USE_OUTPUT_FLOAT_POWER_SCALING build flag. The codegen turns the flag on automatically whenever a YAML config references the feature. Lambdas that call id(out).set_min_power(...) / set_max_power(...) / set_zero_means_zero(...) without any matching YAML key or action will now fail to compile with a clear static_assert pointing at the one-line YAML fix.

This is a breaking change for external components and YAML configs that drive power scaling exclusively from lambdas in ESPHome 2026.5.0 and later.

Main Loop Cadence Decoupled from Scheduler Wake Timing

Every component’s loop() now runs at the configured loop_interval_ (default ~62 Hz). Previously, when other scheduler activity was runningset_interval / set_timeout / PollingComponent updates with sub-loop_interval_ cadences — the component phase got pulled forward up to ~128 Hz; quiet configs with no such scheduler entries were unaffected and always ran at the documented rate. Background events (MQTT RX, USB RX, BLE events, ESPNOW, camera, micro_wake_word, speaker, USB host/CDC, lwIP socket) still wake their component within one tick via the existing wake_loop_* paths. App.set_loop_interval() — the documented knob for power savings — finally works.

This is a behavior change in ESPHome 2026.5.0 and later with no API break, but it affects every component whose loop() implicitly depended on running at ~2× the configured rate when scheduler activity was driving the pull-forward.

Modbus Server Split Out of modbus_controller

Modbus server mode has been split out of modbus_controller into a new dedicated modbus_server component. Configurations that used modbus_controller as a Modbus server (the role: server setting lives on the modbus: bus, not on modbus_controller) must move their server_registers: and server_courtesy_response: blocks under a new top-level modbus_server: entry.

This is a breaking change for YAML configurations using server mode in ESPHome 2026.5.0 and later.

BLE Event Handler Dispatch Devirtualized

Virtual handler interfaces (GAPEventHandler, GAPScanEventHandler, GATTcEventHandler, GATTsEventHandler, BLEStatusEventHandler) have been replaced with callback-based dispatch. External components that inherit from these interfaces must update their registration approach.

This is a developer breaking change for external components in ESPHome 2026.4.0 and later.

Climate and Fan Custom Mode Vectors Moved to Entity

Custom mode vectors (custom_fan_modes, custom_presets for climate; preset_modes for fan) are now stored on the entity base class instead of being rebuilt inside traits on every call. The old ClimateTraits and FanTraits setter methods are deprecated.

This is a breaking change for external components in ESPHome 2026.4.0 and later.

TemplatableFn: 4-Byte Templatable Storage for Trivially Copyable Types

The TEMPLATABLE_VALUE macro now uses TemplatableFn (4 bytes) instead of TemplatableValue (8 bytes) for trivially copyable types like float, uint32_t, bool, and enums. External components that call macro-generated setters with raw C++ constants instead of going through cg.templatable() will fail to compile.

This is a breaking change for external components in ESPHome 2026.4.0 and later.

Trigger Trampolines Eliminated with build_callback_automation

Common entity trigger classes have been replaced with lightweight forwarder structs that fit inline in the callback system. The new build_callback_automation() API eliminates per-trigger object allocations. Several entity callback signatures have also changed to pass state as an argument.

This is a breaking change for external components in ESPHome 2026.4.0 and later.

Application Name and Friendly Name Now Return StringRef

App.get_name() and App.get_friendly_name() now return const StringRef & instead of const std::string &. Most code compiles unchanged because StringRef provides .c_str(), .size(), .empty(), and implicit conversion to std::string. However, binding the result to const std::string & silently creates a heap-allocated temporary — update these references to const auto & to avoid the allocation.

This is a breaking change for external components in ESPHome 2026.3.0 and later.

call_loop(), mark_failed(), and call_dump_config() Are No Longer Virtual

Component::call_loop(), Component::mark_failed(), and Component::call_dump_config() are no longer virtual methods. External components that override any of these methods must remove them entirely and move logic to loop() or dump_config(). This saves 800+ bytes of flash globally from vtable elimination.

This is a breaking change for external components in ESPHome 2026.3.0 and later.

get_loop_priority() Now Conditionally Compiled with USE_LOOP_PRIORITY

The get_loop_priority() virtual method on Component is now only available when USE_LOOP_PRIORITY is defined. This define is only set on RP2040, the only platform where loop priority has an effect. External components that override get_loop_priority() must guard the override with #ifdef USE_LOOP_PRIORITY.

This is a breaking change for external components in ESPHome 2026.3.0 and later.

http_request API Modernization: Vector-Based Headers

The http_request component’s C++ API has been updated to use std::vector instead of std::map, std::list, and std::set for header handling. The get_response_headers() method (returning the full headers map) has been removed — use get_response_header(name) instead. Deprecated overloads using the old container types are provided until 2027.1.0.

This is a breaking change for external components in ESPHome 2026.3.0 and later.

Icon and Device Class Getter Migration

The get_icon_ref(), get_icon(), get_device_class_ref(), and get_device_class() methods are deprecated on all platforms and replaced by new buffer-based APIs get_icon_to() and get_device_class_to(). On ESP8266, the old methods produce a static_assert error because the underlying strings have been moved to PROGMEM and cannot be accessed through normal C string pointers. On other platforms, the old methods continue to work but emit deprecation warnings and will be removed in 2026.9.0.

This is a breaking change for external components in ESPHome 2026.3.0 and later.

register_action Now Requires Explicit synchronous= Parameter

All register_action() calls now require an explicit synchronous=True or synchronous=False parameter. This enables the StringRef optimization for synchronous actions (zero-copy string argument passing) while ensuring asynchronous actions safely use owning std::string to prevent dangling references. Existing external components will continue to work but will see a warning at config time until updated.

This is a breaking change for external components in ESPHome 2026.3.0 and later.

RP2040 Framework Update: Pico-SDK 2.0 and GCC 14

The RP2040 platform has been updated to arduino-pico 5.5.0 (from 3.9.4), pico-sdk 2.1.0 (from 1.5.1), and GCC 14 (from GCC 12). Several hardware register names, API functions, and macros have changed. External components targeting RP2040 may need updates.

This is a breaking change for external components in ESPHome 2026.3.0 and later.

Socket Abstraction Layer Devirtualized

The socket::Socket and socket::ListenSocket types have been changed from virtual base classes to concrete type aliases. Listen sockets now use the ListenSocket type instead of Socket. These changes save 1,020–3,228 bytes of flash across platforms by eliminating virtual dispatch overhead.

This is a breaking change for external components in ESPHome 2026.3.0 and later.

UART flush() Now Returns FlushResult

The UARTComponent::flush() method return type has changed from void to FlushResult. External components that subclass UARTComponent and override flush() must update their override to return a FlushResult value.

This is a breaking change for external components in ESPHome 2026.3.0 and later.

ESP32: Unused Built-in IDF Components Excluded by Default

ESP32 builds now exclude unused built-in IDF components by default to reduce compile time. This applies to both ESP-IDF and Arduino framework builds, since both use the ESP-IDF build system underneath. External components that use excluded IDF APIs (e.g., esp_vfs_fat.h, esp_http_client.h, esp_eth.h) must explicitly re-enable them via include_builtin_idf_component().

This is a breaking change for external components in ESPHome 2026.2.0 and later.

Scheduler String-Based API Deprecation

The scheduler’s std::string-based APIs (set_timeout, set_interval, set_retry, defer, and their cancel counterparts) are now deprecated. Use const char* string literals or uint32_t numeric IDs instead.

This deprecation affects ESPHome 2026.1.0 and later, with removal planned for 2026.7.0.

Light State Callbacks: Listener Pattern Migration

The light component’s callback system has been refactored to use a listener interface pattern with lazy allocation. This replaces the previous CallbackManager<void()> approach with explicit listener interfaces that only allocate memory when callbacks are actually registered.

This is a breaking change for external components that register light state callbacks in ESPHome 2025.12.0 and later.

Network get_use_address() Optimization

The network::get_use_address() function has been optimized to return const char* instead of const std::string& to reduce memory overhead. This eliminates unnecessary string object storage when accessing the device’s network address.

This is a breaking change for external components that call network::get_use_address() in ESPHome 2025.11.0 and later.

Action Framework Performance Optimization

The core automation framework (actions, triggers, and conditions) has been optimized to use const references instead of pass-by-value for parameter passing. This reduces memory allocations by 83% in the automation execution path.

This is a breaking change for external components with custom actions or conditions in ESPHome 2025.11.0 and later.