Battery and problem sensors land on Maintenance purely by device class,
so a car's target charge level showed up with no way out short of hiding
the entity everywhere.
Store a hidden_entities list in maintenance frontend system data and let
the resolver take it, so the Home dashboard summary badge, the overview
check and the area views drop the same entities as the page. The pencil
enters an inline edit mode: the page regenerates with every tile wrapped
in a new entity-visibility card that dims hidden tiles with a dotted
border and toggles them on tap, saving at once.
Saves go through a queue shared by all panel instances, and a panel
loads its config only after that queue drains, so leaving and
reopening the page while a save is in flight cannot reload a snapshot
that lacks it. A failed save shows a toast and reloads, unless a newer
save is already queued on top of it.
Every tap regenerates the view, which replaces the focused tile. The
wrapper card remembers that its button fired while it had focus and the
recreated wrapper for the same entity focuses its button, so keyboard
users stay where they were.
The security page, the maintenance page, the Home summary badges and
the Home overview each rebuilt the same filter functions and ran
findEntities over every state. Add findFilteredEntities, which takes
the states and registries as one object, and a findSecurityEntities
and findMaintenanceEntities wrapper so every consumer resolves a
page's entity set the same way. No behaviour change.
* Label marketplace open issue count as issues and pull requests
The GitHub open issues count includes pull requests. Rename the
translation keys and update the English strings so the label matches
the number shown.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Update marketplace tests for the renamed open issues keys
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
The entity filter matched the full name and the friendly name, so typing a
device name returned entities whose entity column showed no match. It now
matches the entity ID and the name the entity column shows. When the device
column is hidden, it still matches the full name.
Fixes#55451
Co-authored-by: Claude Opus 5.5 <[email protected]>
The "lovelace" panel is cached by the panel resolver, so it should stay
subscribed to config updates while the user is on another page. Since the
urlPath getter returns "lovelace" instead of null, the null check in
disconnectedCallback always unsubscribed it, and nothing resubscribed on
reconnect. Changes saved from another page, like adding a device section
from the device page, only showed after a browser refresh.
Also reset the refetch flag once used, so returning to the dashboard
without a change doesn't refetch the config again, and let a pending
refetch take precedence over the locale rebuild, which would otherwise
rebuild from the stale config.
Fixes#55440
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Migrate the new automation dialog and zones list from ha-list
Moves the new automation and script dialog and the zones list from the
mwc-based ha-list and ha-list-item to ha-list-base and
ha-list-item-button, handling clicks instead of request-selected.
- Render the get blueprints link as a list item button with an href
inside the blueprints list
- Keep the zone edit buttons in the row's end slot, as their handlers
already stop the click from reaching the row
- Drop the request-selected handling left in the more info dialog and
the webhook trigger, where nothing fires it anymore
- Remove the now unused request-selected helper
- Point the automation e2e helper at the new list items
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ln7qMagr2YDdssKkaBiqf5
* Pass the blueprint path and zone id as data attributes
Both are plain strings, so data attributes avoid untyped ad hoc
properties on the list items and their any casts in the handlers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ln7qMagr2YDdssKkaBiqf5
* Drop the unused entityId property from the zone edit button
Nothing reads it: the edit handler only checks noEdit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ln7qMagr2YDdssKkaBiqf5
* Drop the redundant display block on the zones list
ha-list-base is already a block element.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Hide the home zone edit button on narrow screens
The old list item hid its meta slot for the home zone on narrow
screens, where clicking the row already edits it. The new list item
has no such switch, so leave the button out instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Type the list item click handlers with their current target
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude <[email protected]>
* Allow fans with up to 11 speeds to show speed buttons
Keep showing buttons by default up to 6 speeds, and add helpers to
resolve the speed style (buttons or slider) for a fan.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Add a switch between buttons and slider for fan speed in more-info
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Add a style option to the fan speed card feature
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Add fans with more speeds to the gallery
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Use contexts in the fan speed card feature editor
Read the fan state and localize from contexts instead of the full hass
object, so the editor only updates when this fan or the language changes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Address review feedback on fan speed style
- Only show the "too many speeds" info in the feature editor when the
fan has too many speeds for buttons, not for an off/on fan.
- Reuse the numeric input style labels and align the more-info switch
labels with covers.
- Show fractional steps in round-trip test names.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Keep buttons for off/on fans whatever style is requested
Fans that do not offer a style choice now always resolve to their
default style, so an off/on fan configured with the slider style no
longer gets stuck on a slider with a disabled selector.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Keep dense fan speed buttons from overflowing on short screens
Give dense vertical speed buttons a minimum height based on the number
of segments, so they no longer overflow into the mode switch when the
viewport is short.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Remove the fan speed style switch from more-info
Keep this change to the card feature, as asked in review. The more-info
dialog goes back to its previous behavior: buttons up to 6 positions,
a slider above.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Show a row of segments for the fan speed buttons style
The buttons picture in the fan speed feature editor showed the numeric
input stepper. Use a new picture of a segmented row instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Fall back to a slider when fan speed buttons do not fit
Measure the fan speed feature width like the favorite features and show
the slider when the buttons would be too narrow for their labels. Off
and 6 speeds still fit as buttons in a half-width card, while a fan with
10 speeds no longer cuts its last label. The editor now explains this
below the style option.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Clarify fan speed feature examples and helper logic
- Explain the button minimum width next to the constants instead of in
the test.
- Read the disabled state from the schema for the editor helper text.
- Describe the default slider and the width fallback in the gallery
headings.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Use a 24px minimum for fan speed buttons
Buttons in card features have no gap between them, so each one must be
at least 24px wide to meet the WCAG 2.2 AA target size. This also fits
two-digit speeds, so a single minimum replaces the previous pair.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Refine tmp fan entity and config in gallery
* Keep the fan speed button minimum width in the card feature
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Give picker section filter chips a descriptive aria-label
Screen readers announced the section chips in quick search and the
target picker only by their visible text (e.g. "Navigate"), which gives
no hint that the button filters the results. Label them as
"Filter: <section>" instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018Kv7eFvrHAAoLcdEeqDAUg
* Fix fallback for section label localization in ha-picker-combo-box
---------
Co-authored-by: Claude <[email protected]>
Inline setup steps in the Copilot setup workflow
Copilot runs copilot-setup-steps.yml in a context that can't resolve
`$/` self-repository actions, so Copilot code review fails with
"Unable to resolve self-reference '$/'". Inline the setup action's
steps so the workflow only uses pinned remote actions; the actions
lockfile entries stay the same.
Claude-Session: https://claude.ai/code/session_018Kv7eHPGHei6YP9qBPGw1D
Co-authored-by: Claude <[email protected]>
* Keep relative README addresses for the Marketplace rewrite
Images and links in a README with a plain relative path, such as `images/logo.png`, were broken on the Marketplace repository page. xss empties every `src` and `href` that does not start with a scheme, `/`, `./`, `../` or `#`, so the path was gone before `rewriteHtmlUrls` could point it at the repository.
`renderMarkdown` takes `allowRelativeUrls`, which `ha-markdown-element` sets when it rewrites the addresses afterwards. Such an address of a link, image or source is kept as a path below the current one, so it can never carry a scheme, and xss checks it like any other address.
* Trim a relative README address before it gets the ./ prefix
xss trims the spaces written around an attribute value, but not encoded ones. A relative path such as ` images/logo.png` therefore got the space inside the path after the ./ prefix, and the image stayed broken. The value is now decoded and trimmed first, as the browser trims the address.
* Decode a relative README address once and keep data links first
The relative address was decoded before `safeAttrValue` decoded it again, so `&#105;mages` was checked and shown as `images` while the browser reads `images`. Like `srcset`, it is now decoded once with `&` for query strings, dropped when entities are left over, and written back escaped, so the browser reads the path that was checked.
Links to data URLs allowed with `allowDataUrl` are handled before relative addresses again, so they are no longer turned into paths.
* Escape a kept relative README address only once
`safeAttrValue` returns the value escaped already, so escaping its result again turned a quote in a path into `&quot;`, and the browser requested another file. It is now only the check, and the decoded path is escaped once, like `srcset`.
The comments of the change are cut down to the one above the new function, as the code around has few.
* Drop relative README addresses with a scheme and reuse URL_ATTRIBUTES
A relative path never has a scheme, so an address xss empties that still has one after decoding (HTTPS://..., j:alert(1) from javascript:) or is empty is dropped instead of being kept below ./, where it would request a path in the repository that does not exist. The worker imports URL_ATTRIBUTES from rewrite-html-urls, so the attributes it keeps and the ones the rewrite touches cannot drift apart.
* Add option to combine energy sources of the same type
Add a "Combine sources" toggle to the energy period selector menu and a
combine_sources option to the energy cards that list sources separately.
The Summary view always combines sources.
* Make combine_sources a per-card option with each card's current default
Cards render from their own combine_sources option instead of a flag on the
energy data. The power sources graph and the energy and power Sankeys default
to combined and gain a separate mode. The energy dashboard toggle now sets the
option on its cards through the strategies.
Keep stacked series and Sankey columns in the same order when switching.
* Keep usage graph series stable across modes instead of in ha-chart-base
Combined mode puts the sum on the first source's series and keeps the other
sources and the fallback series as empty series, so the series ids stay the
same and ECharts keeps the stack order. Revert the ha-chart-base guard.
* Only offer Combine sources on the energy panel
* Keep the legend entry of a source with only comparison data
* Keep the combine_sources setting on the energy collection
The collection holds the setting, unset by default and saved per collection
only when chosen, and re-publishes the data with it. Cards fall back to it
before their own default. A date picker shows the toggle only with
show_combine_sources, which the Electricity, Gas and Water views enable along
with an initial off value. The setting only applies while a source type is
configured twice.
* Only combine or split source types with two or more sources
A type with a single source keeps each card's current look in both modes.
The Electricity, Gas and Water views only show the toggle when their own
source types have duplicates.
* Keep the solar graph's forecasts from older requests or missing data
* Lock workflow actions with an actions.lock file
* Run the actions lockfile check on every pull request
* Regenerate the actions lockfile after the rebase
* Pin Renovate's gh-actions-lock to the version CI verifies with
* Add virtual parents for offline, parentless, and green power devices
* Tighter pull for virtual groups
* Merge offline with no parent
* Add hidden nodes to better space disconnected clusters
* Ignore neighbor table contributions from offline devices
* Make offline device edges 1.5x weaker
* Differentiate router color from end device color
* Switch `no parent` to `unknown parent`
* Adjust sizing
* Shrink the root symbol on mobile
* Fix tests
* Resolve external statistics in the History panel
The History panel plots external statistics (`<domain>:<object_id>`), for example when the statistics graph card links to it, but treated them as entities everywhere else.
- The sources picker looks up an external statistic in the statistics metadata and shows its name instead of "Entity not found".
- Statistics without an entity take name and unit from their metadata, so the legend and tooltip no longer show the prettified ID without a unit.
- External statistic IDs are no longer sent to `history/stream`. Core rejects the whole subscription for an ID that is not an entity ID, which dropped the state history of every other picked entity.
* Use statistic metadata only for external statistics
The statistics of a removed entity got the unit from their metadata, but no device class. Its recent state history has the same unit and a device class, so both ended up in separate charts and the overlap was no longer trimmed. Other statistics keep a blank unit again and merge into the history line of their entity. The metadata is only fetched for external statistics.
* Reset the zoom when only external statistics remain
Removing the last entity from a zoomed view returned before the zoom was reset. The chart was rebuilt at full range, but the zoom slider kept the previous range.
* Reset the chart zoom when only external statistics are shown
Charts of external statistics stay mounted when the date range changes, because there is no state history to load. Resetting only the zoom range left the chart zoomed while the reset button was disabled. The zoom is now reset on the charts as well, and the normal path is unchanged again.
* Keep the statistics when the metadata lookup fails
A failed metadata lookup rejected the whole request, so the fetched statistics were dropped and the panel could stay on the loading spinner. External statistics are now shown under their ID instead.
* End the loading when the statistics request fails
With only external statistics selected there is no state history, so a failed statistics request left the panel on the loading spinner. A failed request now sets an empty result, guarded by the fetch ID so that an older request cannot overwrite newer data.
* Show external statistics like the statistic picker and inline the stream filter
The sources picker row of an external statistic gets the chart line icon and the source integration as context, as in ha-statistic-picker, instead of the default entity icon. The filter for the history stream has a single caller, so it is inlined in the History panel and its export and test are dropped. The comment on the blank unit of other statistics moves to the isExternalStatistic check it describes.
* Clear the previous statistics before fetching new ones in the History panel
Switching from one external statistic to another kept the chart of the previous one on screen and in the download until the new request was done, as no state history shows the loading then. The statistics are now cleared before the request, so the panel shows its loading state instead.
* Wrap supporting text of entity settings rows
Long explanations such as the visibility description were truncated
with an ellipsis, especially in languages with longer translations.
Fixes#55584
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018Kv7eGRG4gEJgUauHeEZcB
* Update supporting-text styles for row and list items
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Keep chart tooltips inside the window
ECharts only fits tooltips into the chart, so a tooltip taller than the
chart opened above it and ran off the top of the window. Charts without
their own tooltip position now move it into the window, below the
cursor first.
The energy devices detail tooltip also lists the 10 largest devices,
largest first, and folds the rest into one "N other devices" row.
* Try both sides of the cursor and keep confined tooltips in the chart
When the window fit has to move a tooltip, try the other side of the
cursor horizontally too before clamping, so it does not cover the
hovered point. A confined tooltip now only moves within the visible part
of the chart and keeps its chart-confined spot otherwise, so it is not
clipped by containers that hide overflow.
* Keep confined tooltips on screen and skip hidden rows when folding
When the visible part of a confined chart is too small for the tooltip,
move the tooltip only as far as the window needs instead of leaving it
off screen.
Devices whose value the tooltip shows as 0 are no longer folded into the
"other devices" row, so that row never appears without the larger
devices it refers to.
* Add a level filter to the logs page
* Pass the logs level filter through My links
* Share the supported log levels and the log entry filtering
* Expand one logs filter at a time and count from the other filters
* Drop detected location details after choosing another place in onboarding
* Reset the elevation when searching an address in onboarding
* Keep the chosen country when the browser time zone is unknown in onboarding
* Add optional count badges to the integration filter
* Show log level counts in the logs integration filter
* Label log count badges and hide them in the raw view
* Show log count badges on the integration filter's supporting line
* Migrate the remaining static lists from ha-list
Moves the auth provider picker, the config flow menu step, the
onboarding address search results, the input select options, the
system log card, the Wi-Fi access points, the network storage mounts
and the adjust sum statistics list from the mwc-based ha-list and
ha-list-item to ha-list-base, ha-list-selectable, ha-list-item-button,
ha-list-item-option and ha-list-item-base.
- Make the onboarding address results and the Wi-Fi access points
single-select lists, as they were activatable lists before
- Copy the picked access point into a new Wi-Fi configuration instead
of using the access point object itself, which the form then changed
- Let the config flow menu descriptions wrap, as multiline-secondary did
- Keep the list padding, icon colors and mount icon column of the
previous lists
- Remove unused element imports in the touched files
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ln7qMagr2YDdssKkaBiqf5
* Add aria-label attributes to ha-list-base components for improved accessibility
* Refactor onboarding location component to use ha-list and ha-list-item for improved structure and interactivity
* Add reload label to network mounts and update translations for storage
* Refactor storage panel to use ha-list-item-base and ha-list-item-button for improved UI consistency
---------
Co-authored-by: Claude <[email protected]>
Give the to-do panel list pane an accessible name
The migrated ha-list-base pane had no label, so screen readers announced an unnamed list. Reuse the existing "To-do lists" panel title, matching how the calendar panel labels its pane list.
The data URL exception for links wrote the href back unescaped, so a quote
in the value could add attributes. It also added `download` to the cached
whitelist, which then allowed it in all later markdown.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Link the integration page to the integration's settings panel
The Connectivity settings panels, like Modbus and Infrared, are not
reachable from the integration page unless the integration has a config
entry. A user that opens the integration page looks for these settings.
The integration page now shows a button that opens the panel of the
integration when the integration has no entries. With entries, each
entry row already links to the panel. The button uses the panel name
from the Connectivity page. LoRaWAN, Modbus, Radio frequency, and USB
(Serial) are added to the map of integrations with a panel.
An integration with a panel and no config flow is set up in its panel.
For such an integration, the button is the primary action, and the page
does not show the YAML texts, the empty entries section, or the
"Add entry" button.
The demo Infrared and Radio frequency manifests now match core, so
these pages show it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EzDS5Gc7EVw1NsLpEB7yza
* Always show the settings panel button
An entry row links to the panel only while the entry is loaded. When
all entries are disabled, not loaded, or still setting up, the page had
no link to the panel. The integration page now always shows the
settings panel button.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EzDS5Gc7EVw1NsLpEB7yza
---------
Co-authored-by: Claude <[email protected]>
* Use the dark brand icon in state badges when dark mode is on
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Only follow dark mode from the UI context in state badges
Subscribing to the whole UI context made every state badge run an update
whenever an unrelated value such as the panel URL or the docked sidebar
changed. Consume only `themes.darkMode` through `@transform`, like
ha-media-browser-thumbnail does, so badges update only when dark mode
itself changes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
The AI settings show how to connect to the MCP server and the URL of
each of its APIs. The MCP client and MCP server integration pages now
have an "AI settings" button that opens the MCP card on that page. The
button is the primary action when the page has no "Add entry" button.
The AI settings page scrolls to the section named in the URL hash, like
the backup settings page, and clears the hash after. In the demo, the
hash is the route of the page, so the button links to the page without
a section and the page keeps a hash that names no section.
Claude-Session: https://claude.ai/code/session_01EzDS5Gc7EVw1NsLpEB7yza
Co-authored-by: Claude <[email protected]>
* Seek on slider change in the media control card
A touch drag ends without a click, so the card never seeked after a
drag. Use the slider's change event, which fires when a drag or tap
ends and on arrow keys.
* Remove media control card rendering test
The frontend testing guidance asks for tests of code that computes
something, not rendering tests.
* Align installed apps cards with the search field
The installed apps grid got an 8px margin from the shared apps style
on top of its own 16px padding. The cards sat 24px from the screen
edge while the search field sits at 16px. Reset the margin so the
cards line up with the search field and are wider on mobile.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016677RTobVvbXRTMLYj3N4m
* Limit apps card margin change to mobile screens
Remove the side margin of the installed apps grid only below 600px.
Wider screens keep the 24px edge spacing, which matches the
integrations page.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016677RTobVvbXRTMLYj3N4m
* Match installed apps grid spacing to the integrations dashboard
Use the same container spacing as the integrations dashboard: 16px
from the side edges and 24px below the toolbar on all screen sizes.
The margin from the shared apps style is reset, so it no longer adds
to the padding.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016677RTobVvbXRTMLYj3N4m
---------
Co-authored-by: Claude <[email protected]>
* Announce the number of search results in pickers
Screen readers stay on the search field while the list filters, so typing in a picker or the quick search said nothing. A hidden status message now gives the number of matching items, or the not found label when nothing matches.
* Test the picker search results announcement
The status message is never seen, so a regression would go unnoticed. The test checks the count, the not found label, the cleared search, and that section titles are not counted.
* Count only matches and name the search in the picker announcement
The custom value row and the additional items, like the add new area row, were counted as results, so a search that matched nothing said there were results. They are no longer counted. A live region is only read out when its text changes, so a new search with the same count, or a second search that found nothing, said nothing. The message now names the search.
* Add the picker announcement messages to the gallery demo
The picker gallery's localizer only knew the older keys and returned the key for anything else, so searching a demo announced "ui.components.combo-box.result_count" instead of the count and the search. It now has both new messages, copied from en.json, and formats them with their values, so the demos announce "1 result for ‘desk’", "2 results for ‘kitchen’" and "No results for ‘zzz’".
* Move the search results message into a helper and test it there
The frontend testing guidance asks for no rendering tests, and the picker test mounted the component and checked its status text. The counting and the message now live in searchResultsStatus, a plain function the picker calls, with a unit test in its place: rows that are not results (section titles, placeholders, rows like "Add custom item") are not counted, a not found label that takes the search is used, and the message names the search so a new search is read out. The announcements are unchanged.
* Wait for typing to pause before announcing, and don't count Ask Assist
The picker announcement changed on every key press, so a screen reader started a new announcement for each character, over its own echo of the typed characters. The message now updates 500 ms after the rows stop changing, using the existing debounce helper, and is no longer recounted on every render.
The quick bar adds an "Ask Assist" row to every search through getItems, so it was counted: searching for something that matched nothing announced "1 result". The picker takes a new isSearchResult callback for rows like this, and the quick bar uses it to leave the assist row out. It isn't skipped in _isOption, because that would also stop the row being selectable. The gallery page documents both.
* Clear the picker announcement as soon as the rows change
With the pause, the last message stayed in the status region for half a second after the rows had changed, so it could describe the previous search. Going back to a search that had already been announced also wrote the same text again, so nothing was read out. The message is now cleared as soon as the rows change, at once when the search is emptied, and when the picker is removed. Clearing a status region isn't read out, and the next message is a change, so it is.
Since #54801 the resolver creates a stacking context (isolation: isolate) so
its fixed ::before background sits behind the panel. The resolver is rendered
conditionally, once the startup data is available, and isolating it can
intermittently leave the Android WebView accessibility tree (Chromium
109-124) with an empty panel subtree while the page paints normally. UI
automation then never sees the dashboard; the companion app nightly E2E has
hit this on the legacy build (API 32/33/35) since the change landed.
Move the isolation to home-assistant-main's host, which exists before the
resolver is rendered. The pseudo-element and the Lovelace view background
both stay at z-index -1 in that context, in the same order as before, so the
themed background behaves exactly as #54801 intended. Measured with Maestro
on a Chrome 109 emulator: 0 stale trees in 62 cold loads, against 11 in 100
with the isolation on the resolver.
Each legacy entry bundle carries its own copy of intl-polyfill.ts, so the
module-level guard only stopped re-runs within one bundle. With the bundles
loading in parallel, the second bundle could probe Intl.PluralRules after the
first had installed the polyfill but before its locale data chunk arrived. The
FormatJS should-polyfill probe calls select(), which throws on a data-less
polyfill, rejecting the top-level await so the entry never evaluated. On the
companion app this surfaces as ExternalBusTimeout on old WebViews.
Share one promise per page on globalThis so every bundle waits for the same
run, and import the PluralRules polyfill and its English data from one module
so Intl.PluralRules is never installed without data.
* Migrate the config device, area and integration lists from ha-list
Moves the device child, via and linked devices cards, the device
entities card, the area page, the Matter fabrics and ping dialogs, the
Thread border routers, the Z-Wave remove node dialog and the Z-Wave
config entry picker from the mwc-based ha-list and ha-list-item to
ha-list-base, ha-list-nav, ha-list-item-button and ha-list-item-base.
- Render links as ha-list-item-button with an href instead of an anchor
around a list item, so the rows take part in keyboard navigation
- Render automations and scenes that are not editable as
ha-list-item-base with the existing tooltip, as on the device page
- Wrap the area scripts and the Thread border routers in a list, as
the other sections already were
- Only render the remove fabric button where mwc showed it, and give it
a label
- Keep the list padding and row alignment of the previous lists
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ln7qMagr2YDdssKkaBiqf5
* Add aria labels and adjust padding for ha-list components in device and integration dialogs
* Refactor aria labels for related lists in ha-config-area-page and update translations
---------
Co-authored-by: Claude <[email protected]>
* Migrate the remaining selection lists from ha-list to ha-list-selectable
Moves the media management dialog, the calendar panel pane, the expose
entities dialog, the user visibility condition and the select view dialog
from the mwc-based ha-list, ha-check-list-item and ha-radio-list-item to
ha-list-selectable and ha-list-item-option. The read-only ZHA endpoint
list now uses ha-list-base, as its rows already were ha-list-item-base.
- Use ha-list-selectable-virtualized for the expose entities dialog,
replacing the custom click and keyboard handling around lit-virtualizer
- Render the calendar pane as its own list instead of reusing the
dropdown items, sharing the selection logic with the dropdown
- Make the select view dialog a single-select list and keep the icon
column aligned for views without an icon
- Drop the media management workaround for spurious mwc select events
- Render the create calendar row as ha-list-item-button
- Remove unused element imports in the touched files
After this, ha-check-list-item is only used by the todo list card and
ha-radio-list-item is unused.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ln7qMagr2YDdssKkaBiqf5
* Refactor ha-list components for improved focusability handling and update list items logic
* Enhance focus handling in ha-list-base and update tests for disabled items
* use helper
---------
Co-authored-by: Claude <[email protected]>
* Show blueprint usage count as text on narrow screens
* Show the blueprint usage count before the path on narrow screens
* Name the blueprint usage column and skip it for broken blueprints
Problem binary sensors were added to the maintenance view using the
battery tile builder, which names every tile after its device. A device
with several problem sensors (overheating, overvoltage, ...) then showed
identical tiles. Problem tiles now use a device plus entity name; the
entity part is dropped automatically when the sensor has no name of its
own, so single-sensor devices still read as just the device name.
Battery tiles keep the device-only name.
Fixeshome-assistant/frontend#55413
* Remember logs page search, integrations and raw view
* Keep stored logs choices from links and loading apps
* Keep logs choices when closing dialogs and while apps load
* Restore stored logs choices when linked to the page again
* Don't hide stop action on playing media players
* Show stop as a secondary button when play/pause exists
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Add media player entity for "Playing The Music" and update media control card
* Implement logic to remove secondary stop control when play or pause is available in media player
* Test removing the secondary stop control
Runs real computeMediaControls output through removeSecondaryStopControl,
like its callers do, to protect when stop is removed and when it has to
stay.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Wendelin <[email protected]>
* Fit gauge card to the card height in sections view
* Address review: flex container for gauge, min_rows 2, drop DOM tests
- Wrap ha-gauge in a container that takes the space left after the name
(flex: 1, min-height: 0, container-type: size) and size the gauge with
min(250px, 100cqw, 100cqh * 100 / 55) instead of the calc
- Use typeof rows === "number" like the other cards
- Set min_rows: 2 in getGridOptions, one row leaves almost no room
- Remove the DOM tests
---------
Co-authored-by: pradeeppeddineni <[email protected]>
With physics off, graph labels fade in after the physics toggle or a
chart rebuild. A roam updates label layout instantly, and ECharts then
stops every label animation, which froze the fade at its current
opacity. Finish the fade before such an instant update.
* Add ZHADeviceType enum for ZHA device types
* Translate ZHA device types
* Support Green Power devices in ZHA panels
* Reword joining text to reflect device-driven joining, ZHA doesn't search
* Use translations for `Source ID`
* Update src/panels/config/integrations/integration-panels/zha/functions.ts
Co-authored-by: Petar Petrov <[email protected]>
---------
Co-authored-by: Simon Lamon <[email protected]>
Co-authored-by: Petar Petrov <[email protected]>
* Offer to create an energy sensor for power-only energy devices
An individual device needs an energy sensor, so a device with only a
power sensor could not be saved. Saving now asks to create an Integral
helper from the power sensor, or to reuse one that already integrates
it, and uses it as the device's energy sensor.
* Offer energy sensors from power for solar and home sources
Move the Integral helper flow out of the device dialog into a shared
confirmIntegrals helper. The confirmation now creates the helpers itself
and shows its progress. Solar production and the home meter can be saved
with only a power sensor, like individual devices.
* Offer gas and water sensors from flow rates
Gas and water sources and individual water devices can now be saved with
only a flow rate sensor. Saving offers to create an Integral helper with the
gas or water device class, or to reuse one that already integrates the rate.
* Offer energy sensors from power for grid and battery sources
A grid connection or battery with two power sensors can now create Integral
helpers for its missing energy sensors on save, or reuse existing ones. A
battery needs both, while a grid connection can skip them and save as before.
* Keep a device's children out of its upstream choices without an energy sensor
Clearing the energy sensor while editing a device dropped its children from the
check, so a child could be picked as the upstream device. Saving then reuses
the original Integral helper, which made the hierarchy circular.
* Save the submitted device when its dialog closes during helper creation
Snapshot the draft before creating the Integral helper, and only update the
dialog while the same dialog session is open. Always pass the energy device
class, as the helper only infers it from a power device class.
* Save the submitted water device when its dialog closes during helper creation
Mirror the electricity device dialog: snapshot the draft before creating the
Integral helper, and only update the dialog while the same session is open.
* Price gas in the unit of a reused helper
A reused Integral helper can display another unit than a new one would, so
take the price unit from that helper while only a flow rate is set.
* Keep grid and battery helpers apart and wait for all of them
Neither direction of a source may reuse the statistic picked for the other.
When one of several helpers fails, wait for the others to finish, so a retry
reuses them instead of creating duplicates.
* Lock the device form while it saves
The helper is created after the confirmation closes, so edits made meanwhile
would be replaced by the submitted device.
* Lock the water device form while it saves
Mirror the electricity device dialog, so edits can't be replaced by the
submitted device while its helper is created.
* Save the submitted solar and home sources
Like the device dialogs: snapshot the source before asking for the helper,
lock the inputs while saving, and only touch the dialog while the same
session is open.
* Save the submitted grid and battery sources
Like the other energy dialogs: snapshot the source and power settings before
asking for helpers, lock the energy inputs while saving, and only touch the
dialog while the same session is open.
* Save the submitted gas and water sources
Like the other energy dialogs: snapshot the source and cost choice before
asking for the helper, lock the inputs while saving, and only touch the dialog
while the same session is open.
* Keep a device's name and upstream device when its energy sensor changes
A device can now be named and given an upstream device before it has an energy
sensor, so picking or clearing the sensor must not drop them. The upstream
device is only dropped when the new sensor rules it out.
* Keep a water device's name and upstream device when its sensor changes
Mirror the electricity device dialog: picking or clearing the water sensor
keeps the name, and only drops the upstream device when the sensor rules it out.
* Lock the power settings while a grid or battery saves
Changing a power sensor during the save could pair the new sensor with helpers
for the old one after a failed save. Only the new energy sensors are written
back to the form, so other edits are kept.
* Keep solar and home edits when saving fails
Only write the new energy sensor back to the form, so changes made while the
source was saving survive a failed save.
* Keep gas and water edits when saving fails
Only write the new sensor back to the form, so changes made while the source
was saving survive a failed save.
* Lock the grid pricing controls while saving
Pricing edits made while the grid saved were lost, as the save uses the
submitted source.
* Leave gas and water prices in the form when saving fails
The no-cost reset only applies to the saved source, so a failed save no longer
clears the price text while keeping the hidden price.
* Reset the device dialog's saving state per session
A save that finishes after the dialog closed or reopened no longer touches the
new session's saving state.
* Reset the solar and home saving state per session
A save that finishes after the dialog closed or reopened no longer touches the
new session's saving state.
* Ask grid connections to create or cancel, without skipping
The grid confirmation now offers Cancel and Create like the other sources, so
it always has a way back to the editor. Grid and battery also reset their saving
state per session.
* Reset the gas and water saving state per session
A save that finishes after the dialog closed or reopened no longer touches the
new session's saving state.
* Lock the gas and water pricing controls while saving
Pricing edits made while the source saved were lost, as the save uses the
submitted source.
* Lock the solar forecast controls while saving
Forecast changes made while the source saved were lost, as the save uses the
submitted source.
* Lock the battery state of charge and capacity while saving
Like the other inputs, so edits made while the battery saves aren't lost.
* Drop grid export compensation without an export meter
Compensation set while an export was pending could stay hidden in the source
after the power sensors changed, and come back once an export meter is added.
* Explain why a flow rate can't provide the gas or water sensor
When the flow rate can't become a meter, its picker now says so instead of
leaving Save disabled without a reason. The flow rate help text says
"Optionally" again, since it stays optional next to a meter.
* Find existing helpers in the live states
The home dialog passed the hass from when it opened, so a retry after a
failure didn't see a helper created by the first try and made a second one.
The helper now takes the live states and entities from each dialog. The
counting note also fits solar production now.
* Pass the live states to the grid and battery helpers
* Update Save when the power sensor's unit changes
Show entity pictures in the target picker rows
Target picker rows for entities with an entity picture now show it, e.g.
the photo of a person, like the automation targets do since the entity
pictures fix.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Remove unused ha-list imports and migrate gallery off ha-list
Prepares the removal of the mwc-based ha-list components:
- Drop unused ha-list / ha-list-item element imports
- Drop leftover @material/mwc-list imports, typing the click handlers
in ha-domain-integrations as MouseEvent
- Replace the ha-list wrapper in the date and time gallery pages with
a plain div, as it only wrapped layout divs
- Update the ha-control-select-menu gallery page to pass options via
the options property, as the component no longer renders slotted
ha-list-item children, and keep the selected value in state
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ln7qMagr2YDdssKkaBiqf5
* Add HASSDomCurrentTargetEvent type to select menu and integration handlers
---------
Co-authored-by: Claude <[email protected]>
* Offer energy sensors from power for grid and battery sources
A grid connection or battery with two power sensors can now create Integral
helpers for its missing energy sensors on save, or reuse existing ones. A
battery needs both, while a grid connection can skip them and save as before.
* Keep grid and battery helpers apart and wait for all of them
Neither direction of a source may reuse the statistic picked for the other.
When one of several helpers fails, wait for the others to finish, so a retry
reuses them instead of creating duplicates.
* Save the submitted grid and battery sources
Like the other energy dialogs: snapshot the source and power settings before
asking for helpers, lock the energy inputs while saving, and only touch the
dialog while the same session is open.
* Lock the power settings while a grid or battery saves
Changing a power sensor during the save could pair the new sensor with helpers
for the old one after a failed save. Only the new energy sensors are written
back to the form, so other edits are kept.
* Lock the grid pricing controls while saving
Pricing edits made while the grid saved were lost, as the save uses the
submitted source.
* Ask grid connections to create or cancel, without skipping
The grid confirmation now offers Cancel and Create like the other sources, so
it always has a way back to the editor. Grid and battery also reset their saving
state per session.
* Lock the battery state of charge and capacity while saving
Like the other inputs, so edits made while the battery saves aren't lost.
* Drop grid export compensation without an export meter
Compensation set while an export was pending could stay hidden in the source
after the power sensors changed, and come back once an export meter is added.
* Offer to create an energy sensor for power-only energy devices
An individual device needs an energy sensor, so a device with only a
power sensor could not be saved. Saving now asks to create an Integral
helper from the power sensor, or to reuse one that already integrates
it, and uses it as the device's energy sensor.
* Offer energy sensors from power for solar and home sources
Move the Integral helper flow out of the device dialog into a shared
confirmIntegrals helper. The confirmation now creates the helpers itself
and shows its progress. Solar production and the home meter can be saved
with only a power sensor, like individual devices.
* Keep a device's children out of its upstream choices without an energy sensor
Clearing the energy sensor while editing a device dropped its children from the
check, so a child could be picked as the upstream device. Saving then reuses
the original Integral helper, which made the hierarchy circular.
* Save the submitted device when its dialog closes during helper creation
Snapshot the draft before creating the Integral helper, and only update the
dialog while the same dialog session is open. Always pass the energy device
class, as the helper only infers it from a power device class.
* Lock the device form while it saves
The helper is created after the confirmation closes, so edits made meanwhile
would be replaced by the submitted device.
* Save the submitted solar and home sources
Like the device dialogs: snapshot the source before asking for the helper,
lock the inputs while saving, and only touch the dialog while the same
session is open.
* Keep a device's name and upstream device when its energy sensor changes
A device can now be named and given an upstream device before it has an energy
sensor, so picking or clearing the sensor must not drop them. The upstream
device is only dropped when the new sensor rules it out.
* Keep solar and home edits when saving fails
Only write the new energy sensor back to the form, so changes made while the
source was saving survive a failed save.
* Reset the device dialog's saving state per session
A save that finishes after the dialog closed or reopened no longer touches the
new session's saving state.
* Reset the solar and home saving state per session
A save that finishes after the dialog closed or reopened no longer touches the
new session's saving state.
* Lock the solar forecast controls while saving
Forecast changes made while the source saved were lost, as the save uses the
submitted source.
* Find existing helpers in the live states
The home dialog passed the hass from when it opened, so a retry after a
failure didn't see a helper created by the first try and made a second one.
The helper now takes the live states and entities from each dialog. The
counting note also fits solar production now.
* Update Save when the power sensor's unit changes
* Drop hass from the device pickers
getDevices takes an options object and fetchDeviceCompositeSplits takes connection and callWS.
* Refresh the device picker value when the brands token arrives
Key the memoized value renderer on the brands access token so the selected device's integration icon gets its URL.
* Fetch config entries once in the device selector
Restores the one-time fetch with an empty-list fallback so a failed request no longer hides the selector.
* Drop hass from the entity selector
fetchEntitySourcesWithCache takes callWS and states and caches per connection in module scope.
* Refetch entity selector data on reconnect
Fetch config entries once per connection again and keep overlapping cache callers from overwriting newer results.
* Refetch entity selector data when the connection comes back
Reset entity sources and config entries when the websocket reconnects, and drop an unreachable cache guard.
* Drop hass from the media browser and its dialogs
The media browse helpers now take callWS, and the browser, its manual entry form and the browse and manage dialogs read contexts instead.
* Refresh manual media entry labels when the language changes
Key the ha-form label and helper callbacks on localize so they update without a hass change.
* Drop hass from the category picker
Consume the connection, API and internationalization contexts in ha-category-picker and drop SubscribeMixin.
* Keep categories when the scope returns to the subscribed one
Clear the list only when the new scope differs from the active subscription's scope.
The retention and search tests filled the viewer with 5000 lines,
which takes over a second per test in jsdom and pushed the search
tests past the 5 s timeout on loaded CI runners. Run them against a
50-line limit instead and drop the raised timeouts.
* Drop hass from the area controls picker
getAreaControlEntities takes states and registries as separate values.
* Keep states out of the area controls editor memo key
Restores the editor's previous caching so state changes do not rescan the area's entities.
* Drop hass from the history charts
The history line and timeline charts and state-history-charts consume contexts, and generateStateHistoryChartLineData takes localize and states.
* Rebuild line chart options only on chart updates
Entity state changes are compared when the chart updates, as before, instead of triggering their own update.
* Add convert to manual section action
* Keep the section when its strategy fails to generate during conversion
* Drop strategy disabled state when converting a section
* Skip the conversion save when the section changed meanwhile
* Always drop disabled when converting a section
* Add Oxford comma to convert to manual text
* Offer to create an energy sensor for power-only energy devices
An individual device needs an energy sensor, so a device with only a
power sensor could not be saved. Saving now asks to create an Integral
helper from the power sensor, or to reuse one that already integrates
it, and uses it as the device's energy sensor.
* Keep a device's children out of its upstream choices without an energy sensor
Clearing the energy sensor while editing a device dropped its children from the
check, so a child could be picked as the upstream device. Saving then reuses
the original Integral helper, which made the hierarchy circular.
* Save the submitted device when its dialog closes during helper creation
Snapshot the draft before creating the Integral helper, and only update the
dialog while the same dialog session is open. Always pass the energy device
class, as the helper only infers it from a power device class.
* Lock the device form while it saves
The helper is created after the confirmation closes, so edits made meanwhile
would be replaced by the submitted device.
* Keep a device's name and upstream device when its energy sensor changes
A device can now be named and given an upstream device before it has an energy
sensor, so picking or clearing the sensor must not drop them. The upstream
device is only dropped when the new sensor rules it out.
* Reset the device dialog's saving state per session
A save that finishes after the dialog closed or reopened no longer touches the
new session's saving state.
* Drop hass from the navigation picker
computeNavigationPathInfo and subscribeNavigationPathInfo take an options object of separate values.
* Rerender the navigation picker when related items change
The related section is read in render, and hass updates no longer rerender the picker.
* Drop hass from the display state cards
The remaining display state cards read formatters, localize and the API from contexts, and the lock and update helpers take the separate values they read.
* Call the entity registry directly in callProtectedLockService
Avoid a hass-shaped literal for getExtendedEntityRegistryEntry.
* Select and drag picture elements in the card editor preview
* Recreate the element editor on preview selection and moves
* Select a preview element activated by a click alone
Turn the category group menu into a context element
The menu stored hass on its dropdown for its handlers; it now consumes localize and the API context.
* Show custom trigger IDs in the "Triggered by" condition
Since 2026.10 the trigger list in the "Triggered by" condition only shows the
generated description, and a trigger's ID only appears when it is a duplicate.
Automations that relied on descriptive custom IDs can no longer tell similar
triggers apart.
Show the ID in the same end slot whenever it was set by the user, with a
neutral badge. Generated IDs stay hidden and duplicates keep the warning badge.
Fixes#54721
* Process UX feedback
* Fix accessibility issue
---------
Co-authored-by: Jan-Philipp Benecke <[email protected]>
* Add bulk expose to voice assistants on the entities table
* Add exposed entities parameter to voice assistant ID retrieval
* Add toast notifications for exposing and unexposing entities to voice assistants
* Fetch current exposure from core before determining changed entities for assistants
* Adapt to core change
* Remove dropdown fix
* Update toast message count to reflect core changed entities
* Update stub
* Return a fresh copy of stub
NavigationPathInfoController resolves localize, panels, areas, devices,
config and connection from context and only takes a path, so the
shortcut card, badge, editors and home shortcut dialogs no longer pass
hass to it. It skips the host update when the resolved label and icon
did not change. getShortcutCardDefaults and the panel title helpers take
localize instead of hass. The badge and the editors keep hass for their
actions and forms.
* Edit categories from their data table group headers
* Use an edit and delete menu on category group headers
* Drop a deleted category from the filter and pass callWS
* Name the category in its group header menu label
* Key category group header menus by category ID
* Keep group header menus at normal font weight
* Use the rows' icon size for group header actions, keep marketplace at 20px
* Fit chart legends to two rows
Use the legend fit controller in ha-chart-base instead of hiding
everything after a fixed count, and resize the chart when the legend
changes height within a fixed height.
* Refit the chart legend when its items change
* Drop hass from the button, gauge, light, plant, picture and markdown cards
Each card consumes the entity state and registry entry it shows plus the
formatters, localize, config and themes it reads. Actions are dispatched
as hass-action events. The picture card resolves media source images
once per image id, and the markdown card resubscribes its template when
the connection or user changes. The plant status card selects its sensor
states from the configured entity directly, so the selection does not
depend on transform ordering.
* Let the picture card retry a media source that failed to resolve
A rejected resolve left the in-progress marker set, so the same image
was never requested again.
* Subscribe the markdown template once per connection and user
The first context values arrive before connectedCallback() subscribes, so
the first update replaced a subscription that was already live. The card
now tracks the connection and user it subscribed with and only
resubscribes when one of them differs.
* Fit the distribution card legend to two rows
Hide only the legend items that do not fit in two rows, instead of
everything after a fixed count. Each item is fitted at its widest since
the last resize, so changing values cannot make the More button flicker.
* Create the legend observers when the list renders
* Drop the legend mutation observer
* Rename "Use device name" to "Main entity of the device"
* Explain main entities in the switch description
* Rename device name variables to main entity
* Drop hass from the clock, glance and other simple cards
The clock cards consume locale and the server time zone from context,
and the date format helper takes those values instead of hass. The
glance card consumes its entity states and registry entries through the
entity helpers, so it only rerenders when a shown entity changes. The
iframe, empty state, starting and toggle group cards consume localize,
config, states and api, and dispatch actions as hass-action events.
* Act from the element that fired hass-action and keep toggle group order
The shared handler ran handleAction on the app root, so fire-dom-event
dispatched ll-custom above the dashboard listeners and broke the Home
panel's Add device button. The toggle group now walks the configured
entity order, because the states record keeps its identity when only the
order changed.
* Drop hass from the todo list and shopping list cards
The cards consume their entity state, states, entities, formatters,
localize, config, themes, connection and api from context. The todo
helpers take the connection, callService, callWS or states they use, so
the todo panel and item dialog pass those instead of hass.
* Refresh the todo card at midnight or at the next item deadline
Today labels and the overdue styling depend on the time. They used to
refresh with unrelated state updates, which no longer rerender the card.
The timer now fires at midnight or at the earliest upcoming deadline,
whichever is first, and is rearmed when the items change.
* Drop hass from the area controls card feature
The feature consumes the registries, localize and api it reads and
narrows statesContext to the entities its buttons show, so unrelated
state changes do not rerender it. The area control entities and group
toggle helpers take narrowed hass slices. The hui-card-feature wrappers
keep hass to feed custom features.
* Keep the default area controls stable and key the lookup on states
A fresh default array on every access missed both memoized lookups, which
now run per state notification as well as per render. States join the
cache key because the filter reads device classes from them, so an entity
can only match once its state exists.
* Drop hass from the alarm panel, media control and weather forecast cards
The cards consume their entity state, formatters, localize, config,
themes, api and connection from context. callAlarmAction takes
callService and getSecondaryWeatherAttribute takes the formatter and
localize values it uses. The media control card extracts artwork colors
in update(), after @transform has refreshed the entity state for a new
config, and the alarm panel reports the keypad size before its first
update since masonry sizes cards before attaching them.
* Apply the media control theme without waiting for the entity
The theme was skipped when the player was missing on the config or
themes change, and nothing reapplied it once the entity appeared.
* Drop hass from the map card and the trend graph card feature
The map card narrows statesContext to the entities it tracks, including
the entities its visibility conditions read, so unrelated state changes
do not rerender it. The visibility condition checks take a narrowed hass
type that includes the registries the location condition resolves zones
through, the target selector helpers take what they read, and
extractConditionEntityIds visits every condition instead of returning at
the first logical group. The history stream helper takes a connection
and a states slice, which the map card and trend graph feature read live
so a resubscribe after a reconnect sees current states.
* Track core entity_id conditions and single-child groups on the map
The extractor skipped the core entity_id field that the checks prefer,
and threw on a logical group that holds one condition instead of a list.
It now also handles legacy conditions without a condition key.
Adds ConnectionSubscriptionController as a context based replacement for
SubscribeMixin. It keeps one websocket subscription alive while the host
is connected and re-evaluates an optional enabled callback after each
update. The repairs and discovered devices cards subscribe through it.
The updates card counts installable updates with a states transform that
waits for the formatters context and evaluates its visibility in
update(), after the transform ran. Its test provides contexts instead of
a hass object.
* Add an actions slot to data table group headers
* Dismiss new repositories from the Marketplace group header
* Style group header actions like the rows' icon buttons
* Keep group header actions in view and forward ungrouped slots
* Keep the error alert open when dismissing new repositories fails
* Let pages change the colour of group header actions
* Size group header actions like other header icon buttons
* Keep the pinned group header a table row
* Forward only supplied group actions and index the pinned header
getVacuumSegments, getAppKey, listSerialPorts and fetchSupervisorMounts take callWS, so their components read translations, config, registries and the API from contexts.
listAssistPipelines, listAgents, getConfigEntry and getSubEntries take callWS, so both pickers and their selectors read translations, the API, the config and the entity registry from contexts.
Both pickers read entity state, formatters, translations and registries from contexts, so the attribute and UI state content selectors stop forwarding hass.
The TTS and STT helpers take callWS instead of hass, so the TTS, STT and TTS voice pickers and their selectors read translations, registries, states and the API from contexts.
createFloorRegistryEntry and updateAreaRegistryEntry take callWS instead of hass, so ha-floor-picker and ha-floors-picker read registries, states, translations and the API from contexts.
The zwave_js/set_config_parameter and set_raw_config_parameter commands
return the zwave-js-server CommandStatus string ("accepted" or
"queued"), not the numeric zwave-js SetValueStatus. #54287 retyped the
status as a number and mapped it through computeSetConfigParamStatus,
which turned every successful update into "error".
Type the status as the string union the backend sends and use it
directly again. The raw variant only returns status, so give it its own
result type without value_id.
Co-authored-by: Claude Opus 5.5 <[email protected]>
When the users finish loading, the list picks up the pre-selected items and
fires a selected event for them. The handler appended that user again on
every open of the editor, so a single user condition grew one duplicate id
per edit. Ignore selection events that do not change the configured users.
Fixes#54894
Keep column settings for tables without fixed last columns
The data table settings dialog dropped the stored column order when the
table had no lastFixed columns. With no column order, the dialog showed
every column as visible. Reopening the dialog on the statistics table
then showed hidden columns as enabled again.
Claude-Session: https://claude.ai/code/session_014uzH9wKHRBfTGqXypaYEgV
Co-authored-by: Claude <[email protected]>
The add device button on the Matter dashboard linked to
/config/matter/add, which navigated to the devices dashboard and opened
the Matter add device dialog without awaiting the navigation. The
navigation closes open dialogs before it completes, so the dialog was
closed again right away and the user ended up on the devices dashboard.
Open the dialog directly from the Matter dashboard, like the Z-Wave
dashboard does, and make the /config/matter/add route (used by the
add_matter_device my link) await the navigation before showing the
dialog from the app root, as its own element is detached by then.
Co-authored-by: Claude Opus 5.5 <[email protected]>
The vector map's globe projection (new in 2026.10) cannot solve a camera
for a zero-area bound, so fitting a single-entity map warned "Map cannot
fit within canvas" and left the view on the default location. Center a
single point directly with easeTo/flyTo rather than routing it through
fitBounds, carrying the fit's padding as a transient offset and honouring
its fly request.
* Replace map overview more info button with a header icon
* Reuse the show more information label
* Show a settings button for admins in the map overview
* Hide the map overview settings button in the demo
* Show the next demo card on the home page demo again
The home overview strategy now generates the favorites section itself
instead of returning a common-controls section strategy, so the demo
strategy no longer found the section to add its card to. Find the
favorites section by its heading instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Keep at least six favorites next to the demo card
The section strategy used to get a limit of two less, so seven tiles
became six. Only drop as many tiles as needed to keep six.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Keep the current user when the demo user subscription refreshes
The auth/current_user mock answered with the user from startup, so the
user subscription reset a changed user, like the non-admin scenario of
the e2e tests, to the initial admin user.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
The demo did not answer person/update_own_profile, so saving the profile
dialog showed an error. Also answer auth/current_user, so the user
subscription picks up the new name.
Co-authored-by: Claude Opus 5.5 <[email protected]>
The "Rebooting system" toast was shown without an id and with no
timeout, so nothing could ever close it and it stayed on screen after
the host came back. Give it an id, dismiss it when the connection is
re-established, and also when the action fails with an error dialog.
Fixes#54630
Claude-Session: https://claude.ai/code/session_01SkS1AB8w2GPUuRBaDDmsEu
Co-authored-by: Claude <[email protected]>
The landing page bundle includes the Intl polyfill. When a browser
needs a polyfill, it loads locale data from /static/locale-data/.
The landing page build never copied that directory, so these requests
returned 404. The language picker then showed raw language codes.
Fixeshome-assistant/landingpage#216
* Cache map tiles per asset class and stop refetching what still works
The service worker kept glyphs, sprites and tiles in one 500-entry LRU, so
panning around evicted the fonts that every label on every map needs. Glyphs
and sprites move to a cache of their own, on the 30 day age core already sends
them with, and the tile ceiling goes up.
The TileJSON was deliberately left on the network because it is the switching
point for the tile source. Every map waits on it before asking for a tile
though, and at a five minute max-age that is a round trip before the first one.
Served stale-while-revalidate instead: out of the cache immediately, refreshed
behind it, so a moved endpoint still arrives.
The raster fallback redrew on every token rotation, refetching every tile on
screen twice an hour whether or not anything had failed. Later tiles pick up a
new token by themselves, so only a layer that actually had one refused needs
asking again - and since a refused tile is the only sign that path gets that
its token is stale, it now asks for a new one instead of waiting for the
refresh interval to come round.
Style requests that are in flight are shared, so a dashboard of maps, or a
theme toggle switching all of them at once, fetches each style once. Only while
in flight: a settled request is dropped, or a style that failed once would
never be fetched again.
Co-Authored-By: Claude Opus 5 <[email protected]>
* Hand the raster fallback the current map tiles token
A rotation is only announced once, so a vector layer that later falls back
handed the raster layer the token it was created with, and the refresh that
follows a refused tile fetches the already-current token and announces
nothing. The vector layer now tracks rotations itself.
Also covers in-flight style sharing, and passes a map style rather than
`false` in the raster recovery tests.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5 <[email protected]>
* Leave passive zones out of the zones map fit
Opening the zones page fitted the map around every zone, so a few remote
passive zones pushed the view out to the whole globe and adding a zone took
a lot of zooming. Passive zones are still drawn and editable, but no longer
count toward the fit unless they are the only zones there are. Editable
locations gained a fit flag for this.
Fixes#54595
* Hide a zone's radius handle while the circle is under its icon
Zoomed out on the zones page, every zone's radius handle collapsed onto the
zone icon and covered it. The handle now hides while the radius on screen is
shorter than the icon's half width, so it returns as soon as the circle's
edge shows past the icon, and it stays while the radius is being dragged.
* Show a zone's name in a tooltip when hovering its marker on the zones map
The marker only had a native title, which appears late and in the
browser's own style. Every named editable location now gets an ha-tooltip
anchored to its element, so hovering a zone icon on the zones page names
it promptly; the native title is dropped so the two do not stack.
* Start a new zone at the center of the zones map
Create zone placed the new zone at a random spot up to 500 meters from
home, so it landed somewhere unexpected and often off-screen. When the zones
page has its map open, a new zone now starts at the center of the current
view, so panning to the right place first works as expected. A location
handed in, as from a person's Create zone action, still wins; without a map
the random offset around home remains.
* Auto-hide compact map attribution and restyle it for dark mode
MapLibre decides compact mode from the map canvas width alone (640px), so
shrinking the window flipped the attribution from compact back to inline
when the sidebar became a drawer and the map got wider. The engine now adds
the control itself and forces compact on a narrow viewport (870px, the app's
own narrow layout), swapping it when the breakpoint changes.
MapLibre only collapsed the opened compact attribution on a drag; it now
also collapses by itself after five seconds, unless the user opened it from
the info button.
MapLibre's stylesheet hardcodes a white attribution with black text, which
Home Assistant never overrode, so it stood out on a dark map. The dark mode
rules now match the zoom control styling, and the inline text color is set
explicitly instead of inherited from a page that may be in the other mode.
* Add show_zone_radius option to the map card
In panel layout the map hides zone radii except on the Zones tab, for the
selected zone, and while editing. A user asked to always see them, so the
new option draws the radius of every visible zone on all tabs. Other layouts
already show radii and are unaffected.
* Regroup map clusters up front for every fit
Clicking a bubble already regrouped its members at the zoom the fit would
land on, so they parted as the animation started. Any other way of zooming
in, such as picking a person in the map overview, waited for the move to end
before the bubble split. The fit itself now does the regrouping, for all
markers, so every animated fit shows the bubbles as they will end up.
* Return the map to its previous view from the overview's back button
Leaving a selected person or zone cleared the selection but left the map
zoomed in on it. The card now remembers where the map was when the overview
drilled in: the exact camera, or the previous entity's fit when one selection
followed another, and the back button eases back there. A camera that was
still auto-fitting resumes auto-fit instead of freezing. Clicking the map to
deselect stays put, since that is the user looking around.
Engines gain getView, and setView can animate.
* Float the selected map marker above a dot at its location
Selecting a person or device marked it with a ring only, and inside a zone
it stayed hidden in the zone's bubble. The selected marker is now excluded
from clustering, drawn a quarter larger, and hangs above a small dot at the
exact coordinate with a tail pointing down to it. The accuracy circle around
the dot carries the entity color, so the ring is dropped; an entity that
reports no accuracy gets a nominal 5 meter circle so the color still shows.
A fix of 10 meters or better draws that circle as a soft disc without an
outline, since a hard edge would claim more precision than is known; map
circles gained an outline option for this. The engine's marker anchor puts
the dot on the coordinate; both engines support it. Bubble avatars keep
their size.
* Open more info from a button in the map overview detail
The selected person, device or zone's name was the only way to the more-info
dialog, which made the title read as a link. The name is plain text now and
a filled More info button sits below the activity, where the user expects an
action. Focus still moves to the name when the detail view opens.
* Always show the selected marker's accuracy circle
The circle only appeared for a GPS fix looser than 100 meters, so most
phones never showed one. The selected marker now floats above a dot at its
coordinate, so even a small circle around that dot reads clearly; it is
never hidden for the selected entity. Focusing still fits the circle into
view only above 100 meters, since a tighter circle fits at the focus zoom.
* Describe every toggle in the map card editor
Only Show all and Show zone radius explained themselves; Scale ruler, Auto
fit, Fit zones and Cluster markers now have helper text too.
* Float every map marker above its location
Only the selected marker hung above a dot with a tail; the rest sat
centered on their coordinate, so a marker's frame always hid the spot it
marked. Every entity marker is now a pin whose tail points just above its
coordinate. Selecting one scales its frame up and shows the dot at the
exact location. Cluster bubble avatars keep their plain frames.
* Keep the map overview selection in browser history
The card remembered a single camera to restore from the overview's back
button, so after selecting one entity and then another the original view
was lost, and back from the second left the map zoomed on the first while
the overview showed the list. The selected entity now lives in the URL as
an entity_id query parameter; selecting pushes a history entry, and so
does deselecting, whether from the overview's back arrow or a click on
the map, so the browser's back and forward step through every selection.
The overview's arrow itself always returns to the list. Selecting focuses
the entity; deselecting leaves the map where it is. Only selectable
domains are accepted from the URL; anything else is dropped from it. The
saved camera, the animated setView and the autoFit view flag had no
other users.
* Pan to a selected map entity instead of zooming in on it
Focusing a person jumped to zoom 19 and a zone fitted its circle, which
often zoomed much further in than the user had been. Selecting now keeps
the current zoom and pans the entity into the free part of the view. It
only zooms in when another marker sits within 80 screen pixels, and then
just far enough to clear it, capped at zoom 17. Markers at the very same
position cannot be separated by zooming and are left alone. A zone is
fitted as its whole circle with a little room around it under that same
zoom cap, so a zone that fits the view keeps the zoom and a bigger one
zooms out just enough. A marker already on screen eases over; one off
screen flies there in an arc. Fits gain a fly option for this, and every
other fit now eases straight to its bounds on both engines, where
MapLibre used to fly. Map-engine gains pixelDistance, the Web Mercator
screen distance at a zoom level, with a test for its scale.
* Float every cluster bubble above its location
Only a bubble attached to a zone had a tail and hung above the zone's
circle; a bubble of nearby markers sat centered on the cluster, hiding the
spot it stood for. Every bubble now has the tail and floats above its
location with the same lift as a single marker's pin, so the two read as
one style. A zone's bubble keeps floating above the zone circle.
* Treat an entity name equal to its device name as no own name
* Use the same device name check when computing entity names
* Only check the user-set name against the device name
* Add tests for the device name check
* Clarify why only the user-set name is matched loosely
Exclude our own packages from the 3-day minimumReleaseAge rule so
fixes we publish ourselves can be picked up immediately. The
no-downgrade trust policy still rejects unsigned releases.
When the MCP server is not set up, the card has no URLs to show.
The description told the user to add one of the URLs below. Show
only the documentation link until the server is enabled.
Claude-Session: https://claude.ai/code/session_01MeyiXPWWRNRpD2XHVrtPSr
Co-authored-by: Claude <[email protected]>
The MapLibre engine pads a fit by a fraction of its span. A zone whose
circle reaches a pole, or markers far apart, pushed the padded
latitude past 90, and MapLibre throws on such bounds, so the fit never
happened and the console filled with errors. The padded latitude is
now clamped to the poles.
Fixes#54498
* Show four avatars in a cluster bubble before counting
A bubble capped at three avatars added a +1 chip for the fourth
member, taking a fourth slot to hide one avatar. Four members now show
as four avatars; from five on, three avatars and a count fill the same
four slots.
* Open the new zone dialog on every visit to /config/zone/new
The route was only handled in firstUpdated. The config panel is kept
alive between visits and the router reuses the zone page when it is
already the current one, so "Create zone from current location" did
nothing once the zone list had been opened earlier in the session. The
route is now handled on every route change, like /edit/ already was.
The dialog also opened before the more-info dialog it was launched
from had finished leaving the history, and that departure closed the
new dialog again. The page now awaits the replace navigation, which
waits for closing dialogs, before opening it.
* Hide the map overview while the dashboard is being edited
In panel view edit mode the map card kept rendering its overview. On
phones that is a bottom sheet fixed over the bottom of the screen,
which covered the card's edit bar and the view's add button, so the
map dashboard could not be edited in portrait. The overview is for
using the map, not configuring it, so it is left out while editing.
* Show only the overview tabs that have something to list
People and Zones were always offered, with an empty message when the
map card's entities held none of them; only Devices was hidden when
empty. All three now appear only with entries, the first remaining tab
takes over when the selected one empties, and the overview tells the
card so its marker filter follows. The empty-tab messages are gone
with the empty tabs.
* List the person of the current user first, as "Me"
The overview's People tab sorted everyone alike. The person linked to
the user looking at the map now comes first and is named "Me" in the
list and the detail header, through a localized string. Their initials
still come from their real name.
* Draw the overview activity as a timeline
The activity list reused logbook rows: the person's name on every row,
the state pushed to the right, dividers between rows. It is now a
timeline of its own. Each row names where the person went, with the
clock time and how long ago underneath, hung off a dot in the zone's
color with a rail to the next row. A zone's rows name who came or
left and where they are. Five rows show at first, with a "Show more"
button for the rest. The overview no longer needs the broad hass
object, which only the logbook row took.
* Fit the map to its zones when they are all it shows
Zones only joined the auto-fit with the fit_zones option. The panel
map does not set it, so on the Zones tab, with no people or devices
left, the fit found nothing and jumped straight to the home
coordinates at the default zoom. Zones now stand in whenever nothing
else is on the map, which goes through the animated fit like the
other tabs. With fit_zones they still join a fit of people and devices.
* Give tentative calendar events a striped background
A tentative bar gets a light fill in its calendar color with diagonal stripes in the text color, instead of the outline alone. A tentative dot keeps its color and size and gets stripes in the calendar's contrast color. The stripes set a tentative event apart from past days, which are already drawn faded. They repeat on whole pixels, so every stripe looks the same.
* Strengthen tentative dot stripes and add a forced-colors fallback
The stripes on a tentative dot are its only cue, so they now reach at least 3:1 against the dot. In forced colors the stripes and the dot's fill are dropped, so a tentative bar falls back to a dashed border and a tentative dot to a ring of the same size.
---------
Co-authored-by: 1358 <[email protected]>
2026-10-05 10:57:16 +02:00
792 changed files with 33095 additions and 11947 deletions
description:Home Assistant frontend component patterns. Use when implementing or reviewing dialogs, ha-form, ha-alert, keyboard shortcuts, tooltips, panels, Lovelace cards, or ha-button usage.
description:Home Assistant frontend component patterns. Use when implementing or reviewing dialogs, ha-form, ha-alert, loading states, keyboard shortcuts, tooltips, panels, Lovelace cards, or ha-button usage.
---
# HA Frontend Components
@@ -91,6 +91,36 @@ html`
`;
```
## Loading States
Pick the indicator by what is loading, following the [Loading pattern](https://contributors.home-assistant.io/design/patterns/loading/):
- Use a skeleton from `src/components/skeleton/` (`ha-skeleton`, `ha-skeleton-text`, `ha-skeleton-icon`) when content loads into a layout you already know, such as table rows, tile text, or a log. It holds the layout, so nothing jumps when the content arrives.
- Use `ha-spinner` only when a whole area loads and its shape is unknown.
- Use `ha-button` with `loading`, or `ha-progress-button`, for an action started from a button. Don't put a separate spinner next to it.
Wrap the indicator in `ha-fade-in` from `src/components/animation/ha-fade-in.ts`:
- Fade a skeleton in straight away over about 300ms (`.duration=${300}`), as the data table does. It stands in for the content, so a delay would leave an empty gap.
- Delay a spinner or progress bar, usually by `.delay=${500}`, so a load that finishes within that time never flashes it. Some slower areas, such as backups and updates, use `${1000}`.
- Don't fade in the loaded content itself.
Give a skeleton group the `progressbar` role and a translated label, as `ha-spinner` has, rather than visible "Loading" text. A loading state always ends in the content, an empty state, or an error.
Use `ShortcutManager` from `src/common/keyboard/shortcuts.ts` for keyboard shortcuts. It blocks shortcuts in input fields, can prevent shortcuts during text selection, and supports character and KeyCode shortcuts for non-latin keyboards. See `src/state/quick-bar-mixin.ts` for global shortcut examples.
@@ -40,7 +40,7 @@ Never run `tsc` or `pnpm lint:types` with file arguments. When `tsc` receives fi
Detailed guidance lives in `.agents/skills/<name>/SKILL.md`. Load every matching skill before detailed implementation or review. For reviews, load `ha-frontend-review` alongside all companions that apply to the changed code or behavior:
- `ha-frontend-contexts`: Lit contexts, `hass` migration, and rerender-sensitive state access.
"_comment":"Initial JS budget (raw/uncompressed bytes) for the cold-load critical entrypoints. Enforced by build-scripts/check-bundle-size.cjs in CI. Re-seed after an intentional change with `--update --headroom=<percent>`.",
// The "neon" style: the VersaTiles "colorful" cartography repainted as a night
// map, glowing roads on near-black ground. A dark set and no light one, which
// is what makes the style dark whatever the theme mode.
exportconstNEON_MAP_COLORS_DARK={
background:"#07021a",
land:"#07021a",
water:"#13215e",
labelWater:"#00e5ff",
glacier:"#1b2a6b",
natureWood:"#0d1f2e",
natureGrass:"#101a33",
naturePark:"#12203a",
natureLeisure:"#111a30",
natureAgriculture:"#0d1528",
natureWetland:"#0e2440",
natureSand:"#1a1230",
natureRock:"#140c28",
areaResidential:"#0c0524",
areaCommercial:"#140430",
areaIndustrial:"#10062a",
areaWaste:"#160a2e",
areaBurial:"#0d0a26",
siteParking:"#0f0728",
siteSports:"#0a1c2c",
siteEducation:"#160a34",
siteHospital:"#2a0a2a",
siteDanger:"#ff1744",
sitePrison:"#1c0a28",
siteConstruction:"#3a1d5c",
building:"#160a33",
buildingBg:"#8a2be2",
roadStreet:"#00e5ff",
roadStreetBg:"#062b52",
roadSecondary:"#8f7bff",
roadSecondaryBg:"#241058",
roadTrunk:"#ff6ec7",
roadTrunkBg:"#4a0f38",
roadMotorway:"#ff2d95",
roadMotorwayBg:"#5c0033",
transitRail:"#7b2cff",
transitSubway:"#b14cff",
transitCycle:"#00ffa3",
transitFoot:"#2e2a55",
boundary:"#39ff14",
boundaryDisputed:"#1f7a0d",
label:"#ff9ad5",
labelHalo:"rgba(7,2,26,0.9)",
labelSymbol:"#ff9ad5",
labelPoi:"#00e5ff",
labelShield:"#12002a",
labelHousenumber:"rgba(0,229,255,0.35)",
};
/** Builder options the style comes with, under whatever a card adds */
exportconstNEON_MAP_OPTIONS={
recolor:{saturate: 0.35,contrast: 1.15},
text:{
transform:"uppercase",
letterSpacing: 0.12,
haloWidth: 2,
haloBlur: 0,
},
};
Loaded 100 of 792 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.