The maintenance, climate and security panels carried the same ~300 line shell: config loading with revision guards, queued saves with failure recovery, the edit visibility toggle and view generation. Move it into HaHomeSummaryPanelBase, keyed on the system data key, which doubles as the strategy type, URL path and translation namespace. Security only adds its strategy options and the favorite and alert handlers, which the base binds as optional hooks.
The view container now takes min-height: 100% of the top app bar's scroll area. Climate still had min-height: 100vh from before its top bar migration, which overflowed the scroll area by the header height, while security and maintenance had dropped it and cut themed backgrounds off below short content.
The home panel subscribed to and loaded the three configs in three copies of the same block. Keep them in one record keyed the same way and loop over the keys.
Give the security page a hidden_entities list like the other pages and replace its edit dialog with the inline edit mode. In edit mode every tile, favorites included, carries an eye, a star and, for alert-capable domains, a bell that cycles through none, warning and alert. Each tap saves at once. The Home dashboard summary badge, overview check and area views take the same hidden list.
Hidden, favorite and alert flags stay independent, so a hidden entity can still be a favorite or raise alerts, and the Home security summary stays while alerts are configured.
In edit mode the sidebar lists every configured alert, active or not, in its configured color, so a saved alert can always be changed or cleared there, including one whose entity no longer matches the page or was deleted. These entries pick the level with a three-way control instead of the cycling bell, so clearing is an explicit choice and an entry does not vanish on the way from alert back to warning. Changing a level keeps the alert in its place in the list. Favorites and alert entries show regardless of the hidden list, so they carry the star and the alert control but no eye. A deleted entity shows its ID in place of its card, so two stale entries stay distinguishable.
Toggling the eye only flips the wrapper's own flag and saves, like on the other pages. A star or bell tap moves the entity between the grid and the sidebar lists, so those regenerate the view, and the wrapper card's focus restoration covers the star and the alert controls and tells the grid tile and the sidebar entry of one entity apart, so the recreated wrapper focuses the control that fired. When a tap removes the entry itself, focus moves to the same control on the next entry in that list. When the list runs out, the last favorite hands focus to the first tile of the section that takes its place at the top of the page, and the last alert entry to the entity's own page tile if it has one; the strategy puts that fallback on the wrapper. When there is no fallback, or the recreated tile cannot take focus because the narrow layout shows the Activity tab, the card tells the panel and the panel focuses its exit-edit-mode button instead, so keyboard focus never drops to the page. Entering edit mode drops any request left over from an entry without either. ha-control-select gains a focus() that lands on its selected option for this. A failed save reloads the configuration and forces a rebuild so the wrappers show the stored state again.
The dialog, its alerts editor, the entity filter helper and the dialog test go away. Strategy tests pin down that a hidden entity keeps its favorite and alert entries, that stale favorites and alerts only show in edit mode, and the shape of the edit-mode wrappers.
Build the visible area sensor map a single time with the hidden list applied, so the page and the Home badge share one rule and the separate showAreaSensor check goes away. Label the temperature and humidity tiles from the area slot they fill instead of reading device_class back off the state, which produced an empty name for sensors set through the API without a device class. Drop the trend-graph branch for non-area entities, which the climate filters never admit.
Give the climate page the same hidden_entities system data, config
loading and inline visibility edit mode the maintenance page has. The
climate resolver also covers the area temperature and humidity
sensors, which the page adds outside the entity filters, so they can
be hidden too and the Home climate badge skips them.
Cover both modes of the generated view: hidden entities are left out
normally, and while editing they are kept as entity-visibility wrappers
with hidden set so users can show them again.
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 and the overview check 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.
In edit mode every entity is shown anyway, so a tap only flips the wrapper's own hidden flag and the panel just saves; the view is regenerated when edit mode ends. Nothing is rebuilt per tap and focus stays on the tile that fired. A failed save reloads the configuration and forces a rebuild so the wrappers show the stored state again.
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.
* Choose the area of a helper that has no device
Storage helpers get an Area field in their create form. Config flow
helpers get an area picker on the success step when any of their
entities has no device; the area is set on all of them. The redirect
after creating then uses the entity ID as regenerated for the area.
* Count the area as an unsaved change in the helper dialog
* Disable the helper area picker while saving
* Open the new helper after creating it
From the helpers page, a new helper now opens its device page when its
entities belong to one device, or its more-info dialog otherwise.
* Open the right entity after creating a helper
Look up the storage helper's entity by its unique ID, because its entity
ID can differ from the helper ID. Start the config entry lookup when the
success step shows and skip the redirect if the dialog closed meanwhile.
* Ignore the helper lookup after the dialog closes or reopens
* Ignore a helper created after its dialog closed or reopened
* Keep dialogs while navigating
* Apply suggestions
* Keep navigate async when keeping dialogs
Skipping the await committed the navigation synchronously, so a navigate()
called right after it saw the committed count change and was dropped as
stale.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Give the raw Supervisor client error some context when loading the app
info fails, show a dedicated message when a non-admin opens an
admin-only app, and include the underlying error when creating the
ingress session fails.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Add an embedded mode to hui-root that leaves the page's URL alone
* Add a dashboard component to the demo embed
* Keep embedded dashboards' back navigation, height and sidebar tabs inside the embed
* Follow anchors, replacements and new configs in embedded dashboards
* Go back past a followed back_path in embedded dashboards
* Isolate embedded view containers so view backgrounds show
* Match decoded and aliased views and skip empty embedded dashboards
* Read the sidebar tab once sections views have their dashboard
* Declare the navigate events in HTMLElementEventMap
* Skip the Lovelace info fetch and its mock in embedded dashboards
* Add a view component to the demo embed
* Show only the latest generated dashboard in the demo embed
* Stop stale and repeated strategy results in embedded views
* Draw view themes and backgrounds in embedded views
* Share embed provider lookup and theme in a base element
* Add devices to the demo embed provider
* Add a section component to the demo embed
* Draw section backgrounds and accept more section configs in the demo embed
* Filter the device classes by the chosen unit
* Keep unsupported device classes selectable
* Only preselect a required device class and keep the default rows
---------
Co-authored-by: Xyolyp <[email protected]>
* Add device class column and filter to entities table
* Localize device classes and preserve redirect filters
* Fix import conflict after rebase
* Refactor device class filter based on review feedback
* Avoid rebuilding device class options while filter is collapsed
* Address final device class review feedback
* Use shared device class translation key for filter
* Prevent a double Finish click in the config flow success step
* Keep only the double-click test and stub ha-input
---------
Co-authored-by: Xyolyp <[email protected]>
Since the "lovelace" dashboard got a real url_path (#28446), the panel threw
the config preloaded in core.ts away and fetched it a second time on every
page load at / or /lovelace/.
Use the preload for the "lovelace" dashboard and fetch again if it failed.
Only preload the config on /lovelace/, as / opens the default panel, which
is not known yet and is home for new installs.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Migrate the Cast launcher view picker from ha-list
The view picker in the Cast launcher was the last use of the mwc-based
list outside of open pull requests. It is now a single-select
ha-list-selectable.
Views without an icon get the dashboard icon in the start slot, which
mwc never showed because it was put in a slot ha-list-item does not
have. As every row has an icon when any view has one, the hide-icons
attribute is no longer needed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ln7qMagr2YDdssKkaBiqf5
* Enhance view selection in HcCast component with accessible header and click handling
* Type Cast view click event target
Co-authored-by: wendevlin <[email protected]>
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
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.
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
590 changed files with 25405 additions and 8508 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>`.",
subtitle: State history as timelines and line charts.
---
`<state-history-charts>` renders the state history of entities: a timeline for entities with text states, and a line chart per unit for numeric ones. It is used by the history panel, the history graph card and the more-info history. The data below is generated, so the charts work without a backend.
subtitle: Long-term statistics as a bar or line chart.
---
`<statistics-chart>` renders long-term statistics from the recorder. It is used by the statistics graph card, the more-info history and the statistics developer tools. The data below is generated, so the charts work without a backend.
Loaded 100 of 590 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.