Skip to main content

Devices are restricted to a single config entry and at most one subentry

· 12 min read

Summary

A device is now owned by a single config entry, and by a single (or no) config subentry. Devices are no longer merged across integrations: a physical device supported by several integrations is now represented by one device per config entry instead of a single shared device.

Devices which were previously tied to multiple config entries are split into one device per config entry when the device registry is loaded. The entity registry is updated so entities point to the correct device.

Most integrations don't interact directly with the device registry and don't need any changes. Integrations which interact with it directly need to handle the deprecations listed below.

This is implemented in core PR #175785, the rationale is described in architecture proposal home-assistant/architecture#1226. The changes land in Home Assistant Core 2026.8.

Background

Until now, a physical device supported by several integrations has been merged into a single, shared device. This was achieved by identifying devices by connections and identifiers which are globally unique, so that for example a device tracker and a native integration referring to the same MAC address end up on the same device.

This causes a few problems:

  • There's no single source of truth for device information such as name or model; conflicting values are discarded instead of preserved.
  • Users get a confusing experience where a device page contains a hodgepodge of entities from multiple integrations.
  • There are long-standing bugs where modifying the connections and identifiers of a device causes multiple devices to end up with the same connections, violating the original design of the device registry.

The new behavior is achieved by making identifiers and connections unique per config entry instead of globally unique.

Deprecations

Using the deprecated functionality below logs a warning at runtime. Unless noted otherwise, deprecated functionality remains supported until Home Assistant Core 2027.8.

DeviceEntry.config_entries

Deprecated, use DeviceEntry.config_entry_id instead. The property is kept as a compatibility shim which returns a set with the device's single config entry.

DeviceEntry.config_entries_subentries

Deprecated, use DeviceEntry.config_entry_id and DeviceEntry.config_subentry_id instead. The property is kept as a compatibility shim.

DeviceEntry.primary_config_entry

Deprecated, use DeviceEntry.config_entry_id instead. A device now belongs to a single config entry, which is its primary config entry.

Reading config entries of a composite device

DeviceEntry.config_entries, DeviceEntry.config_entries_subentries and DeviceEntry.primary_config_entry are only deprecated for ordinary devices, which belong to a single config entry. They are not deprecated when interacting with a synthesized composite device, the read-only device the backwards compatibility resolution returns for a pre-migration composite device id (see Backwards compatibility). Such a device spans several config entries, which config_entry_id and config_subentry_id can't represent, so these three properties, which report the union across the split devices, remain the way to read that information.

DeviceInfo["via_device"] and DeviceRegistry.async_get_or_create(via_device=...)

Deprecated, use via_device_id instead. Because identifiers are only unique per config entry, an identifier pair no longer unambiguously points at a single device, which is why via_device is deprecated.

Passing both via_device and via_device_id raises HomeAssistantError.

DeviceRegistry.async_update_device() config entry parameters

The add_config_entry_id, add_config_subentry_id, remove_config_entry_id and remove_config_subentry_id parameters are all deprecated. A device belongs to a single config entry and subentry, so adding and removing config entries is no longer meaningful; a device is instead moved or removed.

To move a device to another config entry or subentry, pass the new new_config_entry_id and new_config_subentry_id parameters:

device_registry.async_update_device(
device.id,
new_config_entry_id=config_entry.entry_id,
new_config_subentry_id=subentry.subentry_id,
)

Moving a device with the old parameters took the integration several async_update_device calls, adding the device to the new config entry and subentry and then removing it from the old ones, with a separate case for a device that only changed subentry within the same config entry. The single call shown above replaces all of that. In addition, the device registry now clears a CONFIG_ENTRY disable when a device is moved to an enabled config entry, so the integration no longer has to carry the disabled_by flag across the move by hand.

Relatedly, async_update_device now validates the disabled_by flag against the owning config entry's disabled state. Setting disabled_by=None for a device on a disabled config entry, or disabled_by=DeviceEntryDisabler.CONFIG_ENTRY for a device on an enabled config entry, is inconsistent; such a value is ignored and logged now, and will raise from Home Assistant Core 2027.8.

Core integrations have been updated as examples: openai_conversation in PR #176662, scrape in PR #176663, waqi in PR #176664 and wolflink in PR #176665.

To remove a device, call DeviceRegistry.async_remove_device():

device_registry.async_remove_device(device.id)

Core integrations have been updated to remove devices this way in PRs #176669, #176671, #176672 and #176673.

DeviceRegistry.async_get_device()

Deprecated. Identifiers and connections are only unique per config entry, so a lookup by identifiers or connections can by design match more than one device, and what async_get_device returns is therefore ambiguous.

When the owning config entry is known, look the device up scoped to that config entry with the new methods DeviceRegistry.async_get_device_by_identifier() or DeviceRegistry.async_get_device_by_connection(). Each takes a single identifier or connection tuple plus the config entry id, so the lookup can no longer be ambiguous:

# Before
device = device_registry.async_get_device(identifiers={(DOMAIN, serial_number)})
# After
device = device_registry.async_get_device_by_identifier(
(DOMAIN, serial_number), entry.entry_id
)

Inside an entity, prefer self.device_entry over a registry lookup. If you genuinely need every device matching a key, possibly across config entries, use DeviceRegistry.async_get_devices(), which returns a list.

Core integrations are migrated to the new methods, heos in core PR #176932 is an example.

During the deprecation period, async_get_device resolves an ambiguous lookup as described in Backwards compatibility below. Note that this backwards-compatible resolution only happens through the DeviceRegistry lookup methods such as async_get() and async_get_device(); interacting with the devices container directly, for example DeviceRegistry.devices.get(device_id), does not synthesize a composite device.

Adding a helper config entry to another integration's device

Helper integrations must not add their config entry to the source entity's device or to a user-selected device, they should link their entities to the device instead. This is a direct consequence of the change described here: a device now belongs to a single config entry, so a helper config entry can no longer be added to a device owned by another integration.

This was announced last year in Updated guidelines for helper integrations linking to other integration's device, and stops working in Home Assistant Core 2026.8.

Both helpers now always return None.

They returned a DeviceInfo carrying another device's identifiers and connections, which implicitly added the caller's config entry to that device. A device with a single config entry can't represent that, it would silently fork a duplicate device instead.

Link the helper entity to the device by setting self.device_entry in the entity's constructor instead:

self.device_entry = async_entity_id_to_device(hass, source_entity_id)

The helpers are removed in Home Assistant Core 2027.8.

helpers.helper_integration.async_handle_source_entity_changes(add_helper_config_entry_to_device=...)

The parameter no longer has any effect and should be removed from the call.

When the source entity moves to another device, async_handle_source_entity_changes now only updates the helper entity to link to the new device, it no longer removes the helper config entry from the old device and adds it to the new one.

Passing the parameter is accepted until Home Assistant Core 2027.8, and logs a warning.

Cleaning up helper devices

The helper used to clean up a helper integration's devices from a config entry migration step has been renamed from async_remove_helper_config_entry_from_source_device to homeassistant.helpers.helper_integration.async_remove_helper_devices in core PR #176714. The old name is kept as a deprecated alias which keeps working until Home Assistant Core 2027.8. The new signature is:

def async_remove_helper_devices(
hass: HomeAssistant,
*,
helper_config_entry_id: str,
source_device_id: str | None,
remove_all_devices: bool = False,
keep_device_ids: Collection[str] = (),
) -> None:

Both are now no-ops. Call async_remove_helper_devices with remove_all_devices=True from the helper's async_setup_entry instead:

async_remove_helper_devices(
hass,
helper_config_entry_id=entry.entry_id,
source_device_id=entry.options.get(CONF_DEVICE_ID),
remove_all_devices=True,
)

The template helper has been migrated as an example in core PR #176900. The functions are removed in Home Assistant Core 2027.8.

Devices can only have a single config subentry

A device can no longer be tied to more than one config subentry. This is a breaking change without a backwards compatibility shim; integrations which attach several subentries to the same device must create one device per subentry.

Example: telegram_bot

The telegram_bot integration has been adjusted for this in core PR #176606, which can be used as an example.

It previously had a single bot device shared by every chat, with each chat's subentry attached to that same device. It now creates an individual device per chat, linked to the bot device as a via device. A config entry migration moves each chat's notify entity onto its own device and strips the chat subentries from the bot device, leaving the bot device with no subentry.

Note that the PR was written before via_device_id was added, new code should use via_device_id instead of via_device.

When child devices are introduced, integrations which model this with a via device should migrate to child devices instead.

Linking an entity to a split device

A pre-migration composite device id no longer refers to a real device. Attempting to link an entity to such an id, by passing it to EntityRegistry.async_get_or_create(device_id=...) or EntityRegistry.async_update_entity(device_id=...), is ignored with a logged warning rather than applied. A new entity is then created with no device, and an existing entity keeps its current device. Passing a genuinely non-existent device id still raises ValueError as before.

Entities whose stored device is a composite device with no split owned by the entity's config entry are detached from the device when the registry is loaded; the owning integration is expected to re-link them.

Link entities to one of the split devices instead, looking it up with async_get_device_by_identifier or async_get_device_by_connection.

Device registry events

Splitting a pre-migration composite device happens when the registry is loaded from storage, before any listeners run, so it emits no EVENT_DEVICE_REGISTRY_UPDATED events; devices are already split at startup.

Two things change for integrations which subscribe to EVENT_DEVICE_REGISTRY_UPDATED, or use async_track_device_registry_updated_event, and inspect the payload:

  • The changes dict of an update event reports a device move with the keys config_entry_id and config_subentry_id, replacing the previous config_entries and config_entries_subentries.
  • Updating or removing a pre-migration composite device id forwards the operation to each split device, so one event is fired per split device rather than a single event for the composite id.

A device now belongs to a single config entry, so it can no longer lose one config entry while staying around for another. Integrations which previously watched update events for a change to the config_entries or config_entries_subentries keys, typically to detect their config entry being removed from a device shared with another integration, probably only need to handle remove events now: a device losing its config entry means the device is removed.

Backwards compatibility

Splitting devices changes assumptions which custom integrations may rely on, and device ids which are stored in automations and scripts no longer exist as devices. To soften that, the device registry makes a best-effort attempt to keep unmodified custom integrations working, by resolving a pre-migration composite device id to the devices it was split into.

This is best-effort, not a guarantee. The shims can't cover every way a custom integration interacts with the device registry, and an operation which is ambiguous across the split devices can't be applied at all. An AI-assisted analysis of 462 custom integrations interacting directly with the device registry suggests at least 90% are expected to work unaffected, which also means some will not. Please migrate your integration to the new API rather than relying on these shims; they are removed in Home Assistant Core 2027.8.

During the deprecation period:

  • DeviceRegistry.async_get() synthesizes a read-only restored composite device when passed the id of a pre-migration composite device. Its identifiers, connections and config entries are the union of the split devices'. The synthesis only happens in async_get(); interacting with the devices container directly, for example DeviceRegistry.devices.get(device_id), does not synthesize a composite and returns None for a pre-migration composite device id.
  • DeviceRegistry.async_get_device() resolves a lookup by identifiers or connections matching several config entries to a single device when possible, preferring the device whose config entry domain matches the looked-up identifier. If the remaining matches are the splits of one pre-migration composite device, a read-only composite spanning them is returned. For independent devices sharing an identifier or connection, a device owned by the calling integration is preferred, falling back to the first match.
  • DeviceRegistry.async_update_device() and DeviceRegistry.async_remove_device() forward the call to each of the split devices. Arguments which rewrite a device's identity or move it are ambiguous across the split devices; they are ignored and reported to the offending integration.
  • Entity registry get_entries_for_device_id() and async_entries_for_device() expand a pre-migration composite device id to the entities of the devices it was split into.
  • Actions targeting a pre-migration composite device id trickle down to the split devices.
  • User customizations (area, floor, labels, name) are kept when a device is split.

A new method DeviceRegistry.async_get_devices_for_composite_device_id() returns the devices a pre-migration composite device was split into. DeviceRegistry.async_is_composite_device_id() returns whether a device id is a pre-migration composite device id, that is, an id which was split into one device per config entry and no longer refers to a registered device.

Introducing the Open Home Foundation AI Policy

· 2 min read

AI-assisted development has become part of daily reality for many contributors, and for us as maintainers too. That can be a good thing: AI tools help people write code, improve their English, and find their way in an unfamiliar codebase. It also has a downside: unreviewed AI output submitted as a contribution costs maintainers real time, and maintainer time is the scarcest resource an open source project has.

Today, we are publishing the Open Home Foundation AI Policy. It applies to all Open Home Foundation projects, including every repository in the home-assistant and home-assistant-libs GitHub organizations.

The policy boils down to a few points:

  • AI tools are welcome as an aid. You remain responsible for everything you submit.
  • Autonomous agents are not allowed to contribute. Pull requests and issues that were created autonomously will be closed.
  • You must understand and be able to explain every change you submit, in your own words. This includes answering questions from maintainers yourself, not having an AI do it for you.
  • Using AI to improve the grammar or clarity of text you have written yourself is fine. For non-native English speakers this is genuinely useful, and we appreciate the effort.

We are rolling the policy out to every repository in both organizations. Each repository gets an AI_POLICY.md file in its root, and existing contributing guidelines and AGENTS.md files gain a reference to it, so both humans and their AI tools know what we expect.

Read the full policy in the developer documentation. If a contribution does not follow it, it will be closed. If you believe that happened to yours in error, reach out to a maintainer and we will sort it out.

Modernizing Modbus in Home Assistant

· 4 min read
Update — July 16, 2026

We are re-evaluating the Home Assistant side of the approach described in this post. The foundation is unchanged: everything will still be built around the modbus-connection PyPI package, and device libraries built on it remain the right investment. What we are rethinking is how connections surface inside Home Assistant itself, where we want to focus on being able to produce a better user experience. If you are working on a device integration, hold off on wiring it up to the modbus_connection integration described below — we will share the updated approach here soon.

Modbus is everywhere in the modern home: solar inverters, energy meters, heat pumps, and all kinds of industrial equipment that has found its way indoors. Home Assistant has long supported these devices through the YAML-based modbus integration, where users hand-write register maps in their configuration. That integration is not going anywhere, and existing setups keep working. But hand-writing register maps puts the burden of understanding a device's protocol on every user, and it does not fit the config-flow, UI-first direction the rest of Home Assistant has taken.

So we are adding a new way to use Modbus: an integration-based approach, where a device integration owns the device-specific knowledge and the user simply picks their device in the UI, the same as any other integration.

Sharing a connection

A Modbus connection is a single, exclusive resource: only one party can talk on the bus at a time. A serial (RS-485) bus, or a TCP-to-serial gateway, can carry many devices at once, sometimes from different manufacturers. If two integrations each open their own connection to the same bus, they fight over it, and historically Home Assistant did not support sharing a bus between integrations at all.

The new modbus_connection integration solves this by making a connection something device integrations route through rather than own. The user sets up a connection once in the UI, and modbus_connection keeps it open and manages its lifecycle, including reconnecting after a drop. Device integrations then borrow what they need from that shared connection instead of managing their own. We have revamped the Modbus developer documentation to cover how that works, with example code.

A standalone library

The connection abstraction underneath modbus_connection lives in modbus-connection, a new library we designed for this purpose and published on PyPI. It is not bound to Home Assistant and can be used standalone in any Python project. It presents a common, backend-neutral interface, so device library authors write against one API regardless of the underlying Modbus implementation, and it ships a device-modelling framework and a pytest plugin to make building and testing a device library straightforward.

This keeps concerns where they belong. A device library is a normal PyPI package that knows how to talk to a specific device, and a consuming integration in Home Assistant wires that library up to a shared connection and exposes entities. Both can be developed and tested independently.

For more background, see our research.

Let's get building

With these new building blocks in place, it is now possible to turn a collection of YAML configuration for Modbus into manufacturer-specific integrations that people can set up via the UI. If you're (interested in) working on this, stop by the #modbus channel on the Home Assistant Discord and we'll be happy to help.

If you're using an AI agent, you can give it the following prompt:

I want to create a new integration for Home Assistant using the new Modbus Connection integration as documented here: https://developers.home-assistant.io/docs/modbus/introduction

The YAML we want to turn into a device library can be found here: TODO INSERT LOCATION OF MODBUS YAML!

The deliverables of this task are going to be 3 folders:

Media sources can now be searched

· One min read

Media sources can now implement search. By adding an async_search_media method to your MediaSource, users can search through your media directly from the media browser.

from homeassistant.components.media_player import SearchMedia, SearchMediaQuery

async def async_search_media(
self, item: MediaSourceItem, query: SearchMediaQuery
) -> SearchMedia:
"""Search media."""
results = [...] # list of BrowseMediaSource items
return SearchMedia(result=results)

To tell the media browser which items can be searched, set the can_search flag to True on the BrowseMediaSource items you return while browsing (typically directories). Other integrations can trigger a search through the new media_source.async_search_media helper.

For more info, see the updated documentation.

Deprecation of the home_assistant_start flag of async_initialize_triggers

· One min read

The dedicated home_assistant_start flag of async_initialize_triggers is deprecated and will be removed in Home Assistant Core 2027.8. During the deprecation period the parameter no longer has any effect.

The flag existed because the homeassistant start trigger was a pseudo trigger: instead of acting like a real trigger, it relied on the caller of the trigger API passing home_assistant_start=True so that async_initialize_triggers would fire the trigger during startup.

The start trigger has been rewritten to work as a real trigger, so the flag is no longer needed. Callers of async_initialize_triggers should simply stop passing home_assistant_start.

To make the new implementation possible, the rewrite adds HomeAssistant.async_add_startup_job, which registers a job to be called after all listeners to EVENT_HOMEASSISTANT_START have executed, but before EVENT_HOMEASSISTANT_STARTED is fired. This mirrors the approach already used for the homeassistant shutdown trigger, and avoids adding yet another core state and event to the already complex relationship between core states and events.

For more details, see core PR 175160.

Introducing new unit enumerators

· One min read

As of Home Assistant Core 2026.7, the following unit constants are deprecated and replaced by a corresponding enum:

  • UnitOfDensity enumerator replaces mass over volume CONCENTRATION_*** constants ("g/m³", "mg/m³", "μg/m³", "μg/ft³")
  • UnitOfRatio enumerator replaces unit-less ratio CONCENTRATION_*** constants ("ppm", "ppb")

CONCENTRATION_PARTS_PER_CUBIC_METER was only used by a single integration and is deprecated without a replacement unit.

Please note that the use of PERCENTAGE constant is also deprecated when used as a unit of measurement, even if the constant itself is not deprecated.

Frontend component updates in 2026.7

· 3 min read

Component updates

Component sizes use Web Awesome names

ha-button, ha-button-toggle-group, and ha-slider now use the short Web Awesome size names.

For ha-button, use:

<ha-button size="s">Save</ha-button>

Supported values are xs, s, m, l, and xl.

For ha-button-toggle-group, use s or m:

<ha-button-toggle-group size="s" .buttons=${buttons}></ha-button-toggle-group>

ha-slider uses s or m.

If your custom card or editor still uses small, medium, or large on these components, migrate them to short values like s, m, or l.

Virtualized lists

We added two list components for large data sets:

  • ha-list-virtualized
  • ha-list-selectable-virtualized

Use these when a picker or dialog can render enough rows to affect scrolling or initial render time. The virtualized list renders only the visible rows while keeping the roving-tabindex keyboard navigation from ha-list-base.

Rows expose accessibility metadata with aria-setsize and aria-posinset, so assistive technologies still get the full list position even though only part of the list is in the DOM.

For selectable lists, render ha-list-item-option rows.

Context and editor infrastructure

Dirty state tracking

Dialogs and editors now have shared dirty-state infrastructure:

  • DirtyStateProviderMixin
  • dirtyStateContext
  • isDirtyState
  • isEffectiveDirtyState

Use DirtyStateProviderMixin for new dialogs or editors that need to block scrim close, enable Save only after edits, or coordinate dirty state with child components.

class MyDialog extends DirtyStateProviderMixin<MyState>()(LitElement) {
public openDialog() {
this._initDirtyTracking({ type: "shallow" }, this._state);
}

private _stateChanged(state: MyState) {
this._updateDirtyState(state);
}
}

isDirtyState is the raw comparison and is usually right for enabling Save. isEffectiveDirtyState can ignore equivalent config output, for example when an editor normalizes an explicit default back to the same effective config.

Pages and editors can now publish related context for nearby pickers:

  • relatedContext
  • fireRelatedContext
  • fireEntityRelatedContext

When a card editor, badge editor, automation trace page, or similar surface knows the current entity, device, or area, it can provide that context. Entity pickers and add-element searches can then prioritize related entities, devices, and areas.

fireEntityRelatedContext(this, "light.kitchen");

Clear the context with undefined when the editor no longer has a related item.

Narrow viewport context

narrowViewportContext exposes whether the main Home Assistant viewport is in the narrow layout.

Components that only need narrow-layout state can consume this context instead of receiving narrow through several layers of properties.

@consume({ context: narrowViewportContext, subscribe: true })
private _narrow!: boolean;

Lovelace updates

Strategy regeneration control

Lovelace strategies can now avoid unnecessary regeneration.

Strategies may declare registryDependencies to use the default reference-change check for only the registries they depend on:

static registryDependencies = ["entities", "areas"] as const;

For custom logic, implement shouldRegenerate():

static shouldRegenerate(config, oldHomeAssistant, newHomeAssistant) {
return oldHomeAssistant.entities !== newHomeAssistant.entities;
}

If neither is provided, strategies keep the previous default behavior and regenerate on changes to entities, devices, areas, or floors.

Changes to device tracker entity models

· 3 min read

Summary

There have been multiple recent changes to the device tracker entity model:

  • The battery_level property has been deprecated
  • The location_name property of TrackerEntity has been deprecated
  • A new entity base class BaseScannerEntity has been introduced
  • Users can associate scanners with other zones than the home zone
  • TrackerEntity has a new property in_zones
  • BaseScannerEntity and ScannerEntity have a new state attribute in_zones
  • A new capability attribute tracking_type has been introduced
  • Zones are now calculated by size, then distance to center when calculating the state of TrackerEntity

Details

Deprecation of battery_level

The battery_level property has been deprecated in all device tracker base classes, and will stop working in Home Assistant Core 2027.7. Integrations should communicate battery level via a battery sensor instead.

More details can be found in architecture proposal #627

Deprecation of location_name

The location_name property of TrackerEntity has been deprecated, and will stop working in Home Assistant Core 2027.7.

Integrations with device trackers which do not know or do not want to report the exact coordinates and today use location_name to report the name of a zone should instead report a list of zone entity IDs through the in_zones property. Device trackers which use location_name to give extra context can instead do that via a separate sensor or an extra state attribute.

More details can be found in architecture proposal #1387

Introduction of the BaseScannerEntity base class

The BaseScannerEntity class should be used by integrations which have scanners which do not track connection to a WLAN or other local network, for example scanners which track connection to a BLE beacon.

Users can associate BaseScannerEntity and ScannerEntity with any zone

BaseScannerEntity and ScannerEntity store the associated zone as an entity registry option. The base class will set the state of the entity to the name of the associated zone when connected, and the in_zones state attribute to all zones which contain the associated zone.

More details can be found in architecture proposal #1389

Introduction of the in_zones state attribute

A new state attribute in_zones is present in the state of device tracker entities. The state attribute is automatically calculated by BaseScannerEntity and ScannerEntity. TrackerEntity will derive the in_zones state attribute from the in_zones property if not None, if it is None it will be calculated from the reported location.

The in_zones state attribute is a list of zone entity IDs sorted by size, with the smallest zone first, then by distance to center.

Introduction of the tracking_type capability attribute

A new capability attribute tracking_type is present in the state of device tracker entities. The state attribute is set to connection by BaseScannerEntity and ScannerEntity and to location by TrackerEntity. Integrations should not override this behavior.

Custom card suggestions in the card picker

· One min read

As of Home Assistant 2026.6, custom cards can show up as suggestions in the card picker. When a user selects an entity, custom cards that opt in are listed under a Community section, below the built-in suggestions.

To opt in, add a getEntitySuggestion function to your window.customCards entry. It receives the hass object and the selected entity id, and returns a suggestion (or null if the entity is not supported):

window.customCards.push({
type: "my-card",
name: "My Card",
getEntitySuggestion: (hass, entityId) => {
if (entityId.split(".")[0] !== "light") {
return null;
}
return {
config: { type: "custom:my-card", entity: entityId },
};
},
});

You can also return an array of suggestions to offer several variants, each with its own label.

Only suggest your card when it makes sense for the entity. Check the domain, device class, or supported features with the hass object, and return null otherwise. Suggesting your card for every entity makes the picker noisy.

See the custom card documentation for the full reference.

Frontend component updates in 2026.6

· 2 min read

Component updates

ha-radio updates

ha-radio was removed from our codebase, we use the webawesome based ha-radio-group with ha-radio-option now. No need for a ha-formfield around a ha-radio anymore and you can use the new CSS properties to customize the radio group and options.

New component specific tokens:

--ha-radio-group-required-marker
--ha-radio-group-required-marker-offset

--ha-radio-option-active-color
--ha-radio-option-heigh
--ha-radio-option-toggle-size
--ha-radio-option-border-width
--ha-radio-option-border-color
--ha-radio-option-border-color-hover
--ha-radio-option-background-color
--ha-radio-option-background-color-hover
--ha-radio-option-checked-background-color
--ha-radio-option-checked-icon-color
--ha-radio-option-checked-icon-scale
--ha-radio-option-control-margin

ha-drawer updates

ha-drawer was updated to use the webawesome drawer component. The API is mostly the same it just uses now --ha-sidebar-width instead of --mdc-drawer-width

top bar

  • ha-top-app-bar was removed entirely.
  • ha-top-app-bar-fixed was migrated from MWC to plain Lit.
  • ha-two-pane-top-app-bar-fixed was rewritten to extend the new implementation instead of Material base code.
  • ha-header-bar was rewritten from a Material top-app-bar styled wrapper to a native Lit component.

The --ha-top-app-bar-width token replaces --mdc-top-app-bar-width.

New decorators

@consumeLocalize

Following up on the context entry decorators introduced last release, we added a shortcut for the most common single-field read off internationalizationContext: the localize function.

Before:

@state()
@consume({ context: internationalizationContext, subscribe: true })
@transform<HomeAssistantInternationalization, LocalizeFunc>({
transformer: ({ localize }) => localize,
})
private _localize!: LocalizeFunc;

After:

@state()
@consumeLocalize()
private _localize!: LocalizeFunc;

Use @consumeLocalize() whenever a component only needs the localize function. For other single-field reads off internationalizationContext (e.g. locale, language), keep using @consume + @transform.