Web Awesome's tab group never emits wa-tab-show for a click or Enter on the
already-active tab, so the same-view branch in _handleViewSelected has not
been reachable by user input since the migration in #26951. It only fires
for the group's own sync events, and once hui-root binds the group's active
property (#54613) those events also fire when the group is re-created after
leaving a subview or edit mode, overriding the restored scroll position.
* Fix map marker placement for zones with duplicate names
* Ensure entities are spread in ha-map-engine test
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Remove unused zone tracker state import
* Fix legacy map zone grouping and simplify tests
* Fix map zone selection when the active zone is hidden
* Update map markers when referenced zones change
* Align map coordinate fallback with Core zone selection
* Simplify map zone grouping and tests
---------
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Add alert badge
* Use shared alert domains in the alert badge
* Only alert while the alert badge entity is in an alert state
* Default the alert badge color to red and show its colors side by side
* Use consumeLocalize in the alert badge
* Rename the alert badge colors to alert color and clear color
* Add selection mode with bulk remove to the labels page
* Seed demo labels and align the registry stub with tags
* Align the label overflow menu with Remove wording
* Trigger event on label removal
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Use Delete wording for label destruction
* Clear deleted labels from entity assignments in the demo
* Clear deleted labels from retired entity entries in the demo
* Copy the seed array in the label registry stub
---------
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Stop alert card pulse when reduced motion is on
* Add visual editor for the alert card
* Suggest the alert card when adding a card by entity
* Share the alert active states with the alert card
* Only alert while the alert card entity is in an alert state
* Show the alert card colors side by side
* Default the alert card color to red
* Only offer alert entities in the security alerts picker
* Rename the alert card colors to alert color and clear color
* Show the local apps folder toggle in backup settings
Local apps have the slug local_<slug>, never local, so the toggle for
the addons/local folder was never shown.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Tests for this change
* Remove rendering tests for the local apps toggle
The frontend testing guidelines don't allow rendering tests.
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Set up Music Assistant through its config flow in the ESPHome wizard
The Music Assistant config flow now installs, starts and onboards the app
itself, so open it instead of installing the app and waiting for its
discovery flow.
* Don't open the Music Assistant flow after the offer was skipped
* Add selection mode with bulk delete to the blueprints page
* Tidy bulk delete state handling and add demo delete stub
* Distinguish failed usage checks from in-use blueprints
* Let fresh usage checks decide which blueprints can be deleted
* Add selection mode with bulk uninstall to the Marketplace installed tab
* Refactor mock imports in dashboard test
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
---------
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
The label layout callback gets the node rect in zoomed screen pixels,
so only the slack around the node scales with the zoom. Report the new
zoom before the setOption calls and actions that lay the labels out
again, so a reset or double-click doesn't size them for the old zoom.
@types/chromecast-caf-receiver 6.0.27 types getDeviceCapabilities() as
nullable, since it returns null until the cast system is ready. This
broke tsc on dev.
* Use server timezone for demo statistics calendar buckets
* Preserve server timezone in derived energy statistic boundaries
* Clamp demo production day boundaries to the requested end
* Use supported array indexing in demo statistics test
* Align demo sub-day statistics with the UTC bucket grid
* Use server timezone for demo statistics calendar buckets
* Preserve server timezone in derived energy statistic boundaries
* Clamp demo production day boundaries to the requested end
* Use supported array indexing in demo statistics test
---------
Co-authored-by: SeoFood <[email protected]>
* Add LoRaWAN device overview and connection details
* Add LoRaWAN overview, connections, and topology views
* Match Bluetooth connection list navigation
* Simplify LoRaWAN add-device dialog navigation
* Group LoRaWAN devices by connection and simplify mobile rows
* Fix LoRa icon wave paths
* Use floating Add device action on LoRaWAN overview
* Widen LoRaWAN connection picker and simplify demo connections
* Use public console destinations in LoRaWAN demo
* Show two TTN gateways and one ChirpStack gateway in demo
* Simplify LoRaWAN connection picker heading
* Keep LoRaWAN Add device dialog compact on mobile
* Simplify LoRaWAN device details and widen unsupported-device alert
* Use the brand server for LoRaWAN demo icons
* Revert demo loading screen layout change
* Move LoRaWAN topology helper into the panel
* Select LoRaWAN device in subscription callback
* Only show LoRaWAN info for matching device integrations
* Name LoRaWAN pages by their purpose
* Identify LoRaWAN integrations by their dependency
* Fix LoRaWAN table sorting and column updates
* Refresh LoRaWAN details when navigating between devices
* Fix LoRaWAN device search, grouping, and demo entity IDs
* Treat listed LoRaWAN connections as active
* Use sensecap_lorawan domain in LoRaWAN demo
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]>
* Use separate translation keys for device and service info headings
The "{type} info" heading cannot be translated correctly in languages
that form compound words.
* Use new translation key for device info heading
The Next button condition was not updated when water was added to the
wizard, so the water step showed Done and the individual devices step
was unreachable. Derive all step checks from one step count.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Show translated overview labels in ObjectSelector
Use the optional `<translation_key>.fields.<field>.overview_label`
translation, with a `{value}` placeholder, to render field values in the
object selector overview. Falls back to the plain value when no
translation exists. Placeholder values are forwarded in config, options
and subentry flows.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Forward placeholder values in repair flow selectors
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Forward placeholder values in action, trigger and condition selectors
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Explain blocked updates in the update dialog during a backup
* Pause the updates page group buttons during a backup
* Only pause Supervisor updates during a backup
* Show the backup alert for System-only updates
* Fix Assist microphone not supported message showing [object Object]
The not supported microphone message was built as a Lit TemplateResult,
but chat messages are rendered through ha-markdown which expects a
string, so it was displayed as "[object Object]". Build the message as
markdown instead and narrow the message text type to string.
Fixes#29769
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Jza8VBEqKGoFP35sCcz35H
* Load Assist voice responses from Home Assistant's URL
* Explain when HTTPS or the microphone blocks a feature
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Fix map activity for zones with duplicate names
* Preserve legacy zone icons in person activity
* Simplify map activity zone handling
* Fix map demo history zone membership
* Add dialog to delete statistics in a time range
A bad measurement ruins the graphs of a statistic. Mean statistics
cannot be adjusted, so the only fix was to delete the complete
statistic. A new action in the statistics table opens a dialog to pick
a time range. The dialog previews the statistics in the range and
deletes them with the recorder/clear_statistics_range command.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014uzH9wKHRBfTGqXypaYEgV
* Use recorder/clear_statistics with a time range
The backend adds the optional time range to recorder/clear_statistics
instead of a separate command.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014uzH9wKHRBfTGqXypaYEgV
* Preview the full first period when clearing a statistics range
The recorder also deletes the statistics period in which the range
starts. The preview started at the exact start time, so it did not show
that period. Start the preview at the beginning of the period.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014uzH9wKHRBfTGqXypaYEgV
* Fall back to hourly statistics in the clear range preview
The recorder purges the 5-minute statistics before the hourly
statistics. For an older short range, the preview showed "No statistics
found" and disabled Delete, although hourly statistics existed. Show the
hourly statistics when no 5-minute statistics are found.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014uzH9wKHRBfTGqXypaYEgV
---------
Co-authored-by: Claude <[email protected]>
* Add buttons or slider style to target temperature card feature
* Always disable the target temperature fallback slider
---------
Co-authored-by: Alexander Schieweck <[email protected]>
* Prevent stale Energy refreshes from overwriting the selected period
* Preserve Energy data when reapplying the current selection
* Handle Energy subscription refresh failures
* Refresh invalidated Energy selections on resubscribe
* Use checked Energy collection construction and a typed cache
* Restore Energy collection access on the connection
* Retain Energy preferences when the selected period changes
A field description of 123 saved through the config API or YAML reaches the editor as a number, and description?.trim() threw while rendering the field row and its sidebar. Reuse getRowNote, like the automation row notes.
Co-authored-by: SeoFood <[email protected]>
* Explain debug logging banner and make disable the main action
* Improve debug logging description clarity
Updated the description for debug logging to clarify log file download availability.
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
---------
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Add opt-in URL sync for data table search, sorting and grouping
Adds a shared query params controller and lets hass-tabs-subpage-data-table
keep its search, sorting and grouping in the URL (`search`, `sort`,
`direction`, `group`) when a page sets `sync-url`. Writes replace the current
history entry, search writes are debounced, and empty or default values are
left out. Params in the URL win over stored preferences; a link without table
params keeps what was picked before. Columns can give a short public name
(`urlName`) for the URL, grouping is only written for columns that have one.
No page opts in yet, so behaviour is unchanged.
Part of #54788
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Re1ptLgqtYo2EKj8FezHCp
* Keep links and searches intact in data table URL sync
- Read the URL once the table is opted in and has its columns, and only
write after that, so a link isn't overwritten when sync is turned on or
columns arrive after the first render
- Write a pending search as soon as the search field loses focus, so a
dialog opened right after typing doesn't pick up the write and Back
doesn't bring an older search back
- Keep other query params exactly as they are written instead of
re-encoding them
- Note that a column's URL name must not match another sortable column id
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Re1ptLgqtYo2EKj8FezHCp
* Move the Marketplace tables onto the shared URL sync
* Shorten the urlName comment to one line
* Keep statuses picked on another Marketplace tab when applying a link
* Drop the rendering check from the Marketplace link test
* Cancel pending URL writes on navigation
* Write data table URL after update and reset typed search
* Track the host's first update in the URL params controller
* Add tests for the URL params controller
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Use light color scheme for themes with an explicit light mode
* Only force the light scheme when a theme defines a light mode
---------
Co-authored-by: SeoFood <[email protected]>
* Refactor section title handling in picker components and add utility function
- Removed unused sectionTitleFunction properties from ha-generic-picker and ha-picker-combo-box.
- Implemented findSectionTitle utility function to determine section titles based on visible items.
- Updated ha-picker-combo-box to use findSectionTitle for section title management.
- Cleaned up related code in ha-target-picker and ha-automation-add-search to enhance readability and maintainability.
- Added tests for findSectionTitle to ensure correct functionality.
* review
* Guard automation row notes against non-string YAML values
A note set in YAML is not guaranteed to be a string (e.g. `note: 123`),
so calling `note?.trim()` in the trigger, condition, action and option
rows and sidebars could throw while rendering. Add a `getRowNote` helper
that only trims string notes and use it everywhere notes are shown.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01W88iUfrBdggmpdR4NUHXUk
* Remove getRowNote comment
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01W88iUfrBdggmpdR4NUHXUk
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
* Use rounded squares for avatars everywhere
The map draws person and device avatars as rounded squares, while the
user and person badges in the sidebar, people list, profile, pickers
and editors, the picture previews and the badge card's entity picture
were still circles. They now share one --ha-avatar-border-radius
token, defaulting to the large radius the map already used, so themes
have a single knob and the map markers follow the same token. The
large picture previews take the extra-large radius to keep the same
proportion.
* Use shared picture radius across entity images
Apply the theme radius to state badges, picture previews, and the crop overlay while keeping state-badge-specific overrides.
Co-authored-by: Copilot <[email protected]>
* Keep the small radius for media artwork
Media player and camera badges share the has-media-image class; the 8% fallback was replaced by the picture radius, contradicting the PR description.
* Give camera pictures the shared picture radius
Cameras were grouped with media players and kept the small artwork radius; only media artwork keeps it.
* Use a small picture radius for related item thumbnails
The shared 12px picture radius collapses to a circle on the 20px
related item pictures, so add a themeable small variant that keeps
visible straight edges at that size.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
---------
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Claude Fable 5.1 <[email protected]>
Players that are off with an assumed state but no TURN_ON support are
treated as unavailable, so the controls are not rendered. The controls
were still computed though, which dropped the no-controls class and
removed the bottom padding, clipping the card.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Add live log dialog to integration page
* Move live log button into the debug banner
* Limit retained integration live log lines to 5000
* Add filters and pause to the live log dialog
* Make integration live log retention configurable
* Align live log line limit input with toolbar controls
* Resubscribe live log after reconnect and use DialogMixin
* Keep reading position while searching live log
* Only show live log unsupported for unknown command
* Simplify the live log dialog
* Handle live log review feedback
* Apply live log review suggestions
---------
Co-authored-by: Lucas Deutschland <[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
* Show the color feature for light group members in color mode
* Show hue and saturation for light group members
* Show only hue for light group members
* Follow up on choose option YAML editing
Make action and trigger migration safe for malformed YAML, drop the
option-specific sequence migration, type the option sidebar save value
as unknown and wire option YAML mode through the sidebar like the
other sidebars.
* Guard malformed trigger options during migration
* Add accessible names to icon-only badges and interactive tile icons
ha-badge and ha-tile-icon could render a focusable button with no
accessible name. Add an accessibleLabel property to both, set as
aria-label on the button element (for ha-badge only in icon-only mode,
so visible text still names the button), and pass the entity or
shortcut name from the entity badge, shortcut badge, tile card and
toggle group card.
Fixes#54890
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018ysguoFdwq848WzsuFHkSn
* Name shortcut badges without an action label and document accessibleLabel
Fall back to the localized shortcut badge name when the shortcut has no
action-derived label, such as hold-only or double-tap-only badges, and
add accessibleLabel to the ha-badge gallery API table.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018ysguoFdwq848WzsuFHkSn
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Show a home icon for the location marker in onboarding
The onboarding location step passed no icon for its marker. On Leaflet that
fell back to the stock pin; since the MapLibre engine it is a bare dot, the
same element that marks a zone's draggable center. Use the home icon for the
editable marker, both before a search and for the highlighted search result,
so the step shows what the marker stands for.
* Draw the onboarding home marker like the home zone on a map
The location step showed a bare home icon, while the map card draws the
home zone as a colored circle with the icon. Let a point marker in the
locations editor opt into that zone marker, and use it in onboarding
with the home zone's color for the editable marker and the highlighted
search result, so the step shows the same marker the user will see later.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Make the home marker color a memoization input
The memoized locations read the primary color implicitly, so a theme change left a stale radius color. Resolve the color in render and pass it in.
---------
Co-authored-by: Claude Fable 5.1 <[email protected]>
* Fade light color features when the light is off
* Keep light color features faded while focused
* Show a neutral saturation gradient when the hue is unknown
* Link each run in the activity details to its own trace
An automation and the scripts it calls share one context, and the trace contexts the logbook loads keep only one run per context, so every "View trace" link of such a chain opened the same trace. Core now names the run of a run row in its logbook entry: link that run, and keep the link by context for entries recorded before.
* Only link runs with trace contexts
Trace contexts are only loaded for admins and only hold stored traces.
Keep them as the condition for the link, so non-admins get no link to a
trace page they cannot open.
* Use getTraceUrl for the run trace link
---------
Co-authored-by: Xyolyp <[email protected]>
canOverrideAlphanumericInput exempts textarea and the text-entry input types,
but has no branch for contenteditable, so an element the user is actively
typing into is still treated as fair game for global shortcuts. For an admin
that means a, c, d, e and m fire in the middle of a word.
The rest of the codebase already treats contenteditable as a typing surface
when it makes this decision, in ha-list-base and in the integrations
dashboard, and the HA-CODE-EDITOR tag name check in this same function is
that omission handled narrowly for CodeMirror, which is contenteditable
itself. Add the general check.
The HA-CODE-EDITOR entry is left in place. It is redundant in the common case
but it matches on the whole composed path rather than the event target, and
narrowing that belongs in its own change.
Adds tests for this function, which had none.
* Keep pending target temperature when entity state updates
Thermostat and water heater +/- taps were lost when the entity updated
during the 1 second debounce: willUpdate copied the entity's setpoint
over the value the user was still adjusting, so the debounced
set_temperature call sent the old value. This commonly happens when the
state update confirming the previous call arrives right after a new tap.
Add PendingValueSync, which skips copying entity state while a change is
scheduled or its service call is in flight, and resyncs from the latest
state once nothing is pending if an update was skipped or the call
failed. Use it in the target temperature card feature and the climate
and water heater more-info controls.
The single-setpoint buttons in the card feature now also display the
pending value instead of the entity attribute.
Fixes#54832
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NecjfzKMkYK4LPY3yxnXgu
* Keep pending target humidity when entity state updates
Apply PendingValueSync to the climate and humidifier humidity controls
and the target humidity card feature, which had the same race as the
temperature controls. The card feature calls the service on every tap,
so state updates from an earlier tap could reset the value before the
next one. It now also displays the pending value.
Always sync from state when the control switches to another entity, so
a pending value is never shown for or sent to a different entity. This
matters because more-info and card features reuse the control element.
When the slider commits a value, cancel any queued button call so it
does not clear the pending state too early.
Don't rethrow failed service calls: callService already shows a toast,
and the control resyncs from the entity state.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Implement cancel method in PendingValueSync and update entity state handling in climate controls
* review
* reduce helpers
---------
Co-authored-by: Claude <[email protected]>
* Match zones by label in the location visibility condition
Add `target` (zones, or zones with a label) and `away` options to the
location visibility condition. `target` is matched against the person's
`in_zones` attribute, so overlapping zones match too. Existing `locations`
configs keep working and can be converted from the editor.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Make resolveLocationZones a private helper
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Cache resolved location zones, validate target, add tests
- Cache resolved zones per target and refresh them only when the entity,
device or area registry changes, instead of scanning on every update.
- Validate `target` with the shared targetStruct.
- Match the existing helper text wording.
- Add tests for validation, area targets, cache refresh, and the editor's
value handling and conversion.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Change button size from 'small' to 's'
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Fix button size attribute in ha-card-condition-location
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Validate location target and away shape, keep mixed fields in editor
- Reject a `target` that isn't a mapping and a non-boolean `away` in the
location validator; evaluation ignores them instead of throwing.
- Keep `target` and `away` when editing a legacy condition, and merge
into them when converting, so hand-written mixed configs aren't lost.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Migrate legacy location conditions when the editor opens
Replace the Convert button with automatic migration, following the
existing pattern for deprecated options. A warning alert explains that
the condition now matches any zone the person is in. Zone names that
match no zone are kept and shown as not found instead of being dropped.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Show legacy location conditions migrated, keep same-named zones
Migrate for display only, like toCoreEditorCondition: the new format is
written once the condition is edited, so opening a card doesn't create
unsaved changes. Zone names aren't unique, so migrate a name to every
zone that has it, as the old condition matched all of them. Mention the
in_zones attribute in the alert.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Don't reuse an existing zone ID for a missing zone name
A renamed zone keeps its old ID, so slugifying a name that matches no
zone could select a zone the condition never matched. Add _2, _3 and so
on until the ID is unused, like the script editor and core do for new
entity IDs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Keep missing location names that slugify the same apart
Two names that match no zone but slugify to the same ID got the same
generated ID and collapsed into one. Reserve generated IDs while
migrating, and skip repeated names so they don't get a second one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Validate location target IDs, not just that target is a mapping
A target like {entity_id: 5} passed validation and threw while
resolving zones. The entity filter card, entity filter badge and map
card evaluate conditions without catching errors. Check target keys and
ID types the way targetStruct does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Keep other keys when editing a location condition
Spread the existing condition like the other condition editors, so
keys such as alias aren't dropped. Migrate legacy conditions when the
condition changes instead of on every state update.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Describe Away as the person being away, including passive zones
away matches the not_home state, which passive zones don't change, so
a person only in passive zones is away even though in_zones lists them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Pick up zone changes in the location editor, clarify texts
Redo the legacy migration when states change, so a zone renamed while
the editor is open isn't saved under a stale ID. Keep the same data
object when nothing changed. Drop backticks from the alert, which is
plain text, and mention areas and floors in the Zones helper.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Potential fix for pull request finding 'Add the required Oxford comma'
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Keep zones named home or not_home when migrating
A zone can be named home or not_home, and the old condition matched a
person in it as well, so collect zones by name for those values too.
Describe away as the not_home state in code comments.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Handle zones without a state in location resolving and migration
Resolving skips entities without a state, so a labeled zone evaluated
while zones reload was cached as missing until a registry change.
Resolve again on state changes while a registered zone has no state.
When migrating, treat registered zone IDs as taken even without a
state, so a missing name can't take over a reloading zone's ID.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Only redo location migration when zones change
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Export the location migration and test it directly
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Watch registered zones without a state in the location editor
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Add match_mode so converted location conditions keep matching the same way
Converted conditions get match_mode: active_zone, which matches the zone
shown as the person state like locations did. any_zone stays the default.
The editor warns when active_zone has passive zones selected.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Match the active zone by ID, localize the mixed-config error
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Skip location zones without a state on each check instead of in the cache
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Match the person state when in_zones is empty, for legacy device trackers
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Msmy1J8Q6xPA4i3diLjwbr
* Treat not_home as away in the location fallback, not as a zone name
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Msmy1J8Q6xPA4i3diLjwbr
* Cache location zones on the registries only
Resolve registered zones without a state through a placeholder, so one
missing zone no longer makes every location condition resolve again on
each state change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Msmy1J8Q6xPA4i3diLjwbr
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Use the snap bottom sheet for the automation sidebar
* Use a fixed collapsed height for the automation sidebar sheet
* Make room for the half sheet before scrolling to an opened row
* Close the automation sidebar sheet by dragging it below the collapsed state
* Fall back to vh where dvh is not supported in the automation sidebar sheet
* Show exact values in the adjust statistic dialog
The dialog rounded each change to the display precision and showed the
raw JavaScript number. This caused three problems:
- The adjustment was calculated from the rounded value. Setting a
change of 1.2345 to 0 left 0.0045 in the database.
- Changes smaller than the precision showed as 0. They could not be
found or set to 0, because the new value equaled the shown value.
- The numbers did not use the number format of the user's locale.
The adjustment is now calculated from the stored value. Small changes
get more digits, and the values are formatted with the user's locale.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014uzH9wKHRBfTGqXypaYEgV
* Wrap the time field below the date field on narrow screens
The date and time fields of the datetime selector are too wide for one
row on a phone. The time field overflowed the dialog and cut off the
AM/PM field in the adjust statistic dialog.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014uzH9wKHRBfTGqXypaYEgV
* Redraw the adjust statistic dialog when the locale changes
The values use the number format of the user's locale. The dialog only
redrew on hass changes when the translations changed, so a new number
format did not show until another change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014uzH9wKHRBfTGqXypaYEgV
* Show a missing change as zero in the adjust statistic dialog
A statistic row without a sum has a null change. The digit count
treated it as a very small value and showed 20 decimals.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014uzH9wKHRBfTGqXypaYEgV
---------
Co-authored-by: Claude <[email protected]>
* Add sticky group headers to data table
lit-virtualizer positions rows absolutely, so CSS sticky positioning
can't be used. Render an overlay copy of the current group header at the
top of the scroller, pushed up by the next group header, and allow
collapsing the group from it.
With the group header now always visible, leave the grouped column out
of the narrow secondary line (except for auto-height tables, which have
no sticky header).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qmCtZuDLFdT8P7zqUcELk
* Keep sticky group header aligned and clear of focused rows
Shift the sticky group header's cell with the scroller's horizontal
scroll, so it lines up with the group header rows it covers, including
in RTL.
Add scroll padding to grouped tables, so a focused control in a row is
not left hidden under the sticky group header. Collapsing a group from
the sticky header scrolls a rendered header to the top directly, as
scrollIntoView would now leave room for the padding.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qmCtZuDLFdT8P7zqUcELk
* Keep the sticky group header fixed instead of pushing it
Browsers scroll off the main thread, so the push offset set from scroll
events lagged behind the rows. Scrolling fast showed one header taking
over the other late, and scrolling up on mobile left a gap above the
next group header where rows showed through.
The sticky group header now stays at the top of the scroller, and the
next group header slides under it, so it no longer needs to move in
step with the scroll.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qmCtZuDLFdT8P7zqUcELk
* Resize the sticky group header with the data table
The sticky group header's width was only measured on scroll, so after
resizing the window it kept its old width until the next scroll, letting
rows show through beside it. Observe the table's size to update it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qmCtZuDLFdT8P7zqUcELk
* Count a sticky group header within a pixel of the top as reached
The virtualizer can place rows at fractional positions, so a group
header scrolled to the top could sit a fraction of a pixel below it.
The sticky header then kept showing the previous group over it, for
example after collapsing a group from the sticky header.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qmCtZuDLFdT8P7zqUcELk
* Refresh the sticky group after renders and make its whole row clickable
Filtering, sorting or collapsing can replace the virtualizer's items
without changing the visible range, so no visibility event fires and the
sticky group header kept showing the previous group until the next
scroll. Recompute it after each render.
The sticky header's cell shifts with horizontal scrolling, which left a
strip at the end of the header that didn't respond to clicks. Handle
clicks on the whole sticky row instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qmCtZuDLFdT8P7zqUcELk
* Explain the sticky group scroll back for unrendered headers
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qmCtZuDLFdT8P7zqUcELk
* Memoize the data table row renderer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015qmCtZuDLFdT8P7zqUcELk
---------
Co-authored-by: Claude <[email protected]>
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]>
* Migrate image cropper dialog to DialogMixin and wait for cropper ready
Closing the cropper dialog with the header close button or Escape did not
run closeDialog(), so the Cropper instance stayed bound to the removed
<img>. The next open called replace() on that stale instance: the new
image never got a source or crop overlay, and clicking Crop before the
hidden reload finished threw because getCroppedCanvas() returned null.
Using DialogMixin gives every open a fresh element, and the cropper is
destroyed on disconnect. The Crop button is now disabled until the
cropper is ready, and _cropImage bails out if no canvas is available.
Refs #54793
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011UTN4HCGBVyXdJ7F7bQnBX
* Add file handling improvements to upload components and cropper dialog
- Reset native input value when cleared externally in ha-file-upload
- Introduce _pickedFile state in ha-picture-upload for better file management
- Ensure cancelCallback is invoked if cropping is not completed in image-cropper-dialog
- Update HaImageCropperDialogParams to include optional cancelCallback
* Handle unsupported file formats in picture upload and ensure dialog connection during blob creation
* Add handling for dialog closing state in image cropper dialog
---------
Co-authored-by: Claude <[email protected]>
* Open the Marketplace with a search from the link
* Add My links for the Marketplace tabs and custom repositories
* Sort ascending when a Marketplace link gives no direction
* Keep the Marketplace link in step with the table
* Apply Installed deep-link settings behind Marketplace warning
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
* Show Thread network information on its own page
The Thread panel showed a network's details behind an unlabeled info
icon that opened a plain-text dialog. Zigbee shows the same kind of
details through a "Network information" row that opens its own page,
so move Thread to that pattern.
Each network card with a stored dataset now ends with a "Network
information" row that opens /config/thread/network-info/<dataset_id>.
A new thread-config-router serves the panel and the page, like the
Zigbee and Bluetooth routers, and the border router lookup moves to
findOTBRInfoForDataset so both match border routers the same way.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Align the Thread network delete button with the network name
The 48px icon button was top-aligned with the shorter header text, so it
sat about 10px low. Center it on the name, and pull its block margins in
so the header keeps the same height as cards without the button.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Show an error when Thread network info fails to load
If thread/list_datasets rejected, the page never finished loading and
stayed empty, with an unhandled rejection. Catch the failure, always
finish loading, and show the error message on the error screen.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Show the Thread dataset before border router details load
otbr/info asks every OpenThread border router over REST, with a 10 s
timeout per call, so the network information page stayed empty while
an unreachable border router timed out. Show the dataset as soon as it
loads, without waiting for otbr/info, and show a Loading row in place
of the border router details until it answers.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Rename the Thread config router to thread-config-dashboard-router
thread-config-router read like a Thread border router. Match
zha-config-dashboard-router and the other config dashboard routers.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Say what the Thread network information page is loading
Use "Loading border router details…" for the row shown while otbr/info
answers, instead of a bare "Loading".
Assisted-by: Claude Opus 5.5 <[email protected]>
* Name each Thread network information landmark after its network
Every network card had a navigation landmark named "Network
information", so screen reader landmark navigation couldn't tell them
apart. Include the network name, or its extended PAN ID when it has none.
Assisted-by: Claude Opus 5.5 <[email protected]>
Hide Marketplace last updated on narrow rows
A bare relative time like "4 months ago" has no context in the narrow
secondary line. The repository overview already shows it with a label.
Claude-Session: https://claude.ai/code/session_01RYz4pEoJju2EYeHerCi5CG
Co-authored-by: Claude <[email protected]>
Match the Marketplace update badge to the Apps update tag
The update badge on the Installed tab and its narrow dot used the
warning color, while Apps marks an available update with a light
primary tag. Use the same primary fill and text colors so both read
the same way.
Claude-Session: https://claude.ai/code/session_012e1fHsMG8ZxDjAzsM5FMvZ
Co-authored-by: Claude <[email protected]>
* Keep adjust statistics dialog open and refresh list after adjusting
* Guard adjustment against dialog close and handle refresh errors
* Ignore results of outdated statistics fetches
* Reset dirty state on close and guard failed adjustment
The MQTT panel rendered nothing when there is no active MQTT config
entry, leaving a blank page. That happens when another integration
loads MQTT as a dependency, so the Connectivity page still links to
the panel. Show an empty state with a button that starts the MQTT
config flow instead.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Set no-controls on media control card when entity is unavailable
An entity in the unknown state with assumed_state and turn on/off
support gets control buttons from computeMediaControls. The card hides
the controls for unknown and unavailable entities, but still left out the
no-controls class. That removed the bottom padding and made the card too
short.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wa4wcvqMtHTDDqGChBiGcD
* Treat unknown media player state as available in media control card
A media player in the unknown state is available. The card now shows
its title and controls, such as the turn on and turn off buttons of an
assumed state player, instead of the unavailable layout.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wa4wcvqMtHTDDqGChBiGcD
---------
Co-authored-by: Claude <[email protected]>
The info page and the system log detail dialog unsubscribe from the
system health callback. The callback runs again for each update that
arrives before the unsubscribe completes, and each run unsubscribes
again. The finish event can unsubscribe once more. The backend rejects
every extra unsubscribe with "Subscription not found.", which ends up
as an unhandled promise rejection.
Make the unsubscribe function from subscribeSystemHealthInfo run only
once, which fixes all callers.
Co-authored-by: Paulus Schoutsen <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
The backend sends endpoint as null for configuration values on the
root endpoint, because zwave-js-server-python types Value.endpoint as
int | None. The frontend typed it as number and grouped parameters
with item.endpoint.toString(), so the page threw on every render and
stayed on the loading spinner.
Type endpoint as number | null. Treat null as endpoint 0 when grouping
parameters and when setting a parameter, because the backend set
command only accepts an integer endpoint.
Co-authored-by: Paulus Schoutsen <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
The card can update with hass before setConfig is called. The progress
controller reads the state object on every host update, and the
_stateObj getter read _config.entity without a check. Each card then
threw a TypeError as an unhandled promise rejection.
The frontend logger maps the stack of every unhandled rejection with
source maps on the main thread. On a dashboard with 40 media control
cards, the 80 rejections blocked the main thread for about 3 seconds
on load.
Return undefined from _stateObj until the card has a config.
Co-authored-by: Paulus Schoutsen <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
* Close the previous ZHA permit subscription on search again
"Search again" called _subscribe() while the subscription from the first
search was still open, since _deactivate() only stops the UI at the end of
the permit window. Each zha/devices/permit subscription keeps ZHA debug
logging enabled in the backend until it is closed, so the leaked one kept
the backend in debug mode until the websocket connection closed.
Close any previous subscription before opening a new one.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Wait for the previous ZHA permit subscription to close before resubscribing
_unsubscribe() only called the previous unsubscriber from a promise
callback, so _subscribe() sent the new zha/devices/permit subscription
before the old one was closed, and the backend started the next debug
logging session while the previous one was still running.
Chain the new subscription onto the previous unsubscribe instead.
_subscribed always holds the latest chain, so a quick second search waits
for the first one, and leaving the page unsubscribes whatever that chain
ends up subscribing.
Co-authored-by: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[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]>
* Add demo water statistics and long energy source names
* Align demo water statistics scaling and consumption costs
* Preserve non-admin permissions when the test app refreshes its user
* Restore original demo energy meter friendly names
* Remove unrelated authentication scenario fix from water statistics PR
* Derive demo costs generically and scale water by bucket duration
Cover creating an automation from the picker through the save dialog,
the save dialog refusing an empty name, and discarding a new automation
when leaving the editor.
The app harness now registers the demo's display registry mock, which
answers the refetch the app makes when a saved automation fires
entity_registry_updated.
Claude-Session: https://claude.ai/code/session_011L15xnuJiRZnioxrmNwDfb
Co-authored-by: Claude <[email protected]>
* Link trace steps to the script or automation run they started
A step that runs a script or triggers an automation already records the started run as child_id in its trace, but the step details only dumped it as raw YAML. Show a link to that run's trace instead, named after the script or automation. A link to another run of the same item, like a script that starts itself, only changes the query string, so the trace pages now pick up a changed run_id themselves. The logbook's trace link and the new link share one URL helper, which encodes the item id.
* Keep the shown trace run in the URL
A link to another run of the same script or automation adds a history entry, but the trace page only followed in-app navigation, so browser back and forward left the URL and the shown run apart. Write the shown run into the current history entry, follow popstate within the same trace page, and let only the latest trace list request update the page. Entries without a run_id are ignored, since closing a dialog with back lands on one with the same URL.
* Let only the latest trace request update the trace page
Going back to the shown run while a linked run was still loading left the older request in charge: it then showed the linked run and wrote it into the history entry the user went back to. Remember which run the latest trace list request is for and drop it when the URL returns to the shown run. Loading the same run twice could also let the older response win, so trace requests are numbered as well.
* Let a URL change replace the first trace list request
While a trace page was loading its first trace list, a link or browser history step to another run of the same script or automation was ignored, so that first request won and wrote its run into the newer history entry. Handle such a change right away, so it replaces the pending request.
* Let a run picked on the trace page win over a pending link
While a link to another run of the same script or automation was loading, a run picked with the trace picker or the arrows was overwritten once the link's trace list arrived, and Refresh dropped the linked run while the URL kept it. A picked run now cancels the pending link, and Refresh keeps the run the link asked for.
* Move the trace run navigation into a shared controller
Both trace pages carried the same logic to follow the URL, number their requests and write the shown run back into the URL. Move it into a TraceRunController with unit tests, build the trace page path in one place, and split the child trace name from its link. No change in behaviour.
* Link trace timeline steps to the run they started
The step details link a step that ran a script or triggered an automation to the run it started. Show the same link under that step in the trace timeline, and share the name lookup and the link between both views.
* Compare trace paths decoded
The route decodes the item id, so a trace page can also be opened by a
link that encodes it differently, like the logbook's links, which do not
encode it. Compare the decoded paths, so that the URL follows the shown
run there too.
---------
Co-authored-by: Xyolyp <[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.
* Add area labels to the pull request labeler
* Label Labs pull requests from the Labs API as well as paths
* Rename the Landingpage label to Landing Page
* Label Supervisor pull requests that change src/data/hassio
* Label pull requests from area code as well as paths, and only add labels
* Remove stale labels again, owning code-matched ones in the script
* Warn instead of failing the labeler, and summarise changes
* Move the run summary into a shared helper
* Split the Activity label from History
* Add labels for the remaining areas and split broad ones
* Apply the Home Assistant Link label from the content script
* Catch more companion app usage in the content labels
* Add Notifications, Search and Sidebar labels and widen some rules
* Add the AI label
* Label trace changes as Automations
* Match content labels on whole calls against the current labels
* Run the labeler one at a time per pull request
* Label dashboard creation, quick bar and theme loading changes
* Compare Labs and companion app calls with their arguments
* Match full companion app calls and imports in content labels
* Label updates list changes as System
* Match companion app property reads and renamed files in content labels
* Remove
Co-authored-by: Petar Petrov <[email protected]>
* Add ingress panel
Co-authored-by: Petar Petrov <[email protected]>
---------
Co-authored-by: Petar Petrov <[email protected]>
* Allocate less per point when building history line chart data
For a single dataset, a plain numeric value went through
pushData(date, [value]), which allocates a one element array and a
forEach callback for every point. Push [timestamp, value] directly and
reuse one array for prevValues, which is only ever read for its latest
entry. The output is unchanged.
pnpm test:bench state-history-chart-line-data, median of 8 alternating
runs: mixed large (100k states) 8.62 -> 5.83 ms, mixed medium (10k
states) 0.80 -> 0.58 ms. In a production browser build the function took
about 101 ms before and after on 1.34 million points.
Assisted-by: Claude Sonnet 5.5 <[email protected]>
* Tighten the per-point loop in history line chart data
Profiling generateStateHistoryChartLineData on a large history put most
of the remaining time in the per-point path. Iterate the states with an
indexed loop instead of forEach, read the series array once, and track
the y range inline instead of through trackY, since the value is always
finite there. The output is unchanged.
These only help together. On their own, the hoisted array and the inline
min/max measured no change, and for...of loses most of the gain, so
@typescript-eslint/prefer-for-of is disabled for this one loop.
Cold first call in a fresh headless Brave, median of 7 runs, on 1.34
million states: 87.9 -> 77.2 ms on top of the previous commit (98.7 ms
on dev). At 100k states: 5.2 -> 4.1 ms.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Add a test for history line chart states past the end time
The characterization tests never had a state after endTime, so the path
that drops those points was not covered. That path is where the faster
pushValue skips updating the reused prevValues array, and the end point
of the line depends on it.
Add a numeric sensor with a value and a gap that only closes after
endTime. The snapshot was generated on dev before being run against this
branch, so it pins the previous behavior. Updating the reused array
before the endTime check makes only this test fail.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Drop the indexed loop from history line chart data
The previous commit gives nearly all of the gain, so the indexed loop,
the hoisted series array and the inline y range aren't worth disabling
@typescript-eslint/prefer-for-of and keeping a second copy of trackY.
This reverts commit 952022b91.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Show therm gas in therms on the energy dashboard
Gas sources with an energy unit class were always converted to and shown
in kWh. Now that therm is a native energy unit, show gas in therms when
every gas source reports therms, and request those statistics in therms
for both the current and the compare period. Any other energy unit, or a
mix of units, keeps showing kWh as before.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* Keep kWh when a therm gas statistic is also a device
The device charts read the same statistics map and label values as kWh.
If a therm gas statistic is also configured as an individual device, the
therm values would be shown as kWh there, so keep kWh for gas in that case.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* Use the unit of energy gas sources for any energy unit
Volume gas and water already use the display unit of their sources when
they all share one. Do the same for energy gas instead of always showing
kWh, by adding an ENERGY_UNITS list next to VOLUME_UNITS and requesting
the gas statistics in that unit. Different units, or a gas statistic that
is also an individual device, still fall back to kWh.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
* Refactor energy statistics handling in energy.ts
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Test gas statistics shared with grid, solar and battery
Cover the fallback to kWh when a therm gas statistic is also used as a
grid, solar or battery statistic, and update the comment to match.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
---------
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Make the Marketplace warning a friendlier modal over the Marketplace
Replace the full-page first-run warning with a calmer dialog shown over
the Marketplace, which stays visible but can't be used until the notice
is accepted. Continue is enabled once the checkbox is ticked, without
the 30 second countdown. Go back, or Escape, leaves the Marketplace.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LZuGZHa3FZ9tNHtuuSy8c3
* Update the Marketplace warning to the new design
Lead with what installing can do, in a highlighted list, followed by
numbered steps to stay safe, and agree to the risks explicitly.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LZuGZHa3FZ9tNHtuuSy8c3
* Update the Marketplace warning tests to the modal
Test the warning's accept, failure and go back logic instead of the
countdown, test that the Marketplace is inert behind the warning and
starts over once it is accepted, and drop the countdown strings.
Focus the checkbox when the warning opens, and drop the close handler:
neither Escape nor the scrim close the warning, Go back leaves it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LZuGZHa3FZ9tNHtuuSy8c3
* Keep links from acting behind the Marketplace warning
Until the warning is accepted, the Marketplace behind it shows the
catalog whatever the route is, so a link can't add a repository or open
a dialog over the warning. It starts over on the route once accepted.
Use the animation duration token for the agree row, which respects
reduced motion.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LZuGZHa3FZ9tNHtuuSy8c3
* Add role attribute to marketplace warning list
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Enhance ha-dialog-header and ha-marketplace-warning components with improved structure and styling
* Refactor marketplace panel tests to improve clarity and structure
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Co-authored-by: Wendelin <[email protected]>
* Show only serial integrations when setting up an ESPHome serial port
The add integration dialog gets an integrationFilter param with a list of
domains and trims the integration descriptions to them. The ESPHome serial
Set up button fetches usb/list_serial_integrations and passes it. If that
fails, it opens the unfiltered dialog as before.
* Check for a newer add integration load after loading translations
Since MapLibre 6.9.0 Arabic and Hebrew labels are shaped and reordered by
MapLibre, and setRTLTextPlugin is deprecated. We were still loading the
0.4.0 plugin, which only replaced that built-in implementation with the
same result. Version 1.0.0 of the plugin drops the UMD bundle we copied
into static assets and no longer works as a plugin at all, so bumping it
breaks the build. Remove the dependency, the static copy and the plugin
registration instead.
* 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
* Use the entity name formatter in dashboards
* Refresh map trail names when the entity name formatter changes
* Show the device only once in the card editor entity search
* Make the name optional in the bootstrap entity name formatter
Encode the item id in the activity trace link
Automation ids are free-form, so an id with a question mark, a hash or a
percent sign broke the "View trace" link of the activity details. Encode
it like the automation editor and the automation list do.
Co-authored-by: Xyolyp <[email protected]>
* Add a delay calculation for live history updates
Rebuilding the History charts for a large history takes tens of
milliseconds, and it grows with the number of states, so applying every
live stream message to one makes the page hitch.
Add countHistoryStates and computeHistoryUpdateDelay. A history under
100,000 states is never delayed. For a larger one the delay is the time
one chart pixel covers (assuming 1000 pixels), between one second and a
maximum the caller passes in.
Assisted-by: Claude Sonnet 5.5 <[email protected]>
* Coalesce live updates on large history charts
When a History range ends in the future, Core keeps streaming states and
every message rebuilds the whole history: computeHistory, the line chart
data and the downsampling all run again over every point. With 1.34
million states (30 days of a sensor updating about every 2 seconds)
that is a 150 to 200 ms long frame per message.
Apply the first message right away, then coalesce later ones with
computeHistoryUpdateDelay, using MIN_TIME_BETWEEN_UPDATES as the maximum.
Every message carries the full history, so the latest one wins. The
timer is cleared when the panel unsubscribes.
A replaced subscription can still deliver until its unsubscribe is
acknowledged. Ignore those messages, so a coalesced one can't land
minutes later on top of the new range.
Over 25 s on a production demo build with a mocked live stream (median
of 5 runs): rebuilds 21 -> 0, long frames 22 -> 0, main thread blocked
2057 -> 0 ms, heap after GC 500 -> 288 MB.
Assisted-by: Claude Sonnet 5.5 <[email protected]>
* Use the measured chart width for the live update delay
The delay was the time one pixel covers on a chart assumed to be 1000
pixels wide. Measure the charts the same way the timeline downsamples
them, so wider or high density charts update as often as a pixel
changes.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Keep the history legends current while live updates wait
The line chart legends show the current state of each entity, but the
charts skip updates where only hass changed. While a live update waited,
the legends could lag behind by up to five minutes. Request an update of
the charts when an update is held back, so they follow the current
states.
Assisted-by: Claude Opus 5.5 <[email protected]>
* Drop hass from the distribution card
The card consumes the states of its own entities, the formatters,
localize and the core config. It no longer re-renders when an
unrelated entity changes.
* Re-render the distribution card when the theme changes
The card resolves its graph colors from the theme when it renders, so
it has to render again when the theme changes.
The helper took a hass-shaped object and an unused entity ID. It now
takes the two values it reads. The dead .hass bindings on hui-warning
are removed too; the element has had no hass property since #52926.
* Avoid recomputing chart series when only options change
The history line chart downsamples every point of a series before it is
handed to ECharts. ha-chart-base did that again on every options change:
_updateHiddenStatsFromOptions requested an update for _hiddenDatasets
each time it ran, even when no dataset had been hidden, and
_applyChartUpdate rebuilds and downsamples all series whenever
_hiddenDatasets is flagged.
Only the history line chart samples its data, and its x axis bounds
change together with its data, so rebuilding on data or hidden dataset
changes covers every input of _getSeries.
On a 30 day range of a sensor with 1.34 million states, loading the chart
called _getSeries 4 times, and now calls it once. Median of 7 runs on a
production demo build: _getSeries 85 -> 29 ms, time from data delivery to
the first frame 273 -> 209 ms.
Assisted-by: Claude Sonnet 5.5 <[email protected]>
* Drop the chart screen reader connection when the options change
Chart2Music is built with the axis names and value labels of the
current options. It used to be dropped on every options change because
_hiddenDatasets was always flagged, which now only happens when a
dataset gets hidden. Drop it whenever the options change, so the next
keyboard focus rebuilds it with the current ones.
Assisted-by: Claude Opus 5.5 <[email protected]>
* WIP
* Implement unit of measurement picker and selector
* Disable units for device classes that do not support units
* Fix no unit
* Follow sensor entity unit rules in unit of measurement selector
Non-numeric device classes only allow no unit, and a device class or
state class without a unit table (such as monetary or measurement)
no longer restricts the units.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Follow up on comments
* Lint error
* Add context info to config
* Only clear the unit when the context filters change
ha-form, the blueprint editor and the gallery create a new context object
on every render, so editing an unrelated field counted as a context
change and could clear a saved unit. Compare the filter values instead.
A filter can be a list of device classes or state classes, so compare
them by value rather than by reference.
Co-authored-by: Petar Petrov <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Petar Petrov <[email protected]>
* Add a source filter and group by source to the marketplace
* Plural form
* Test that marketplace browse links keep the source
* Drop the Marketplace filter pane rendering test
* Update dependency jsdom to v30.1.2
* Focus shadow-rendered buttons in dialog-form tests
jsdom 30.1.2 no longer focuses light-DOM children of a shadow host
without a slot. The ha-form mock has no slot, so the opener buttons
were never rendered. Put them in the form's shadow root, where real
ha-form controls live.
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Petar Petrov <[email protected]>
* 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]>
* Add group by status and type to the marketplace
* Update the marketplace dashboard test for grouping
* Group the marketplace by status by default
* Type the Marketplace grouping events and drop the panel test
* Use generic paths to address Lovelace dashboard items
* Return node target for container paths
* Remove dropped card node when creating a section from a drop
* Refuse to write through a missing list item
* Only build card and badge paths in edit mode
* Use optional badge config in edit badge dialog params
* Append cards in a single write in addCards
* Append badges in a single write in addBadges
* Pass a save callback to the badge edit dialog
* Handle ll-create-card from custom layouts that don't bubble it
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]>
* 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.
* Correct system and diagnostics API types in the frontend
Compare the system health, log, repairs and related data modules with
the handlers that produce them, and fix the mismatches.
The system health failure detail was read from a key that does not
exist, so a failed check rendered blank. Repairs issues carry several
always-present nullable fields and no active flag. Saving the core
config and ignoring a repair return nothing, and the icon types were
missing a nesting level.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YDqKNfAAvMV7zJJNodsHfK
* Surface the HTTP fallback port and the failure reason
Warn separately when the server fell back to the previous default
port, which the saved configuration warning did not cover.
Show the diagnostic message a failed configuration carries, and fall
back to the error code when there is none.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YDqKNfAAvMV7zJJNodsHfK
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
* Abandon superseded WebRTC starts to avoid leaking peer connections
If ha-web-rtc-player was disconnected (or restarted) while awaiting the
WebRTC client configuration, the start continued afterwards and created
an RTCPeerConnection that nothing ever closed.
Track a start id that is bumped on every clean up and bail out when a
start has been superseded. Negotiation now also checks that it still
belongs to the current peer connection.
Fixes#54482
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014ZzvX8SAHt1knzzJNULtZx
* Bind WebRTC signaling callbacks to their own peer connection
Events of an offer subscription that belongs to a superseded start could
still reach the current stream before the unsubscribe completed, sending
candidates to the old session or applying the old answer to the new
offer. Ignore events whose peer connection is no longer the current one,
and don't report errors from operations rejected because a superseded
connection was closed.
Also show an error instead of leaving an unhandled rejection when
fetching the WebRTC client configuration fails.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014ZzvX8SAHt1knzzJNULtZx
* Localize WebRTC player errors and handle rejected offer subscriptions
Error messages of ha-web-rtc-player were hardcoded English strings. They
are now stored as an error type plus the backend message and localized
when rendering.
The try/catch around webRtcOffer() never caught anything because the
subscription returns a promise, so a rejected offer left an unhandled
rejection and no error. Handle the rejection, and don't let clean up
re-raise it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014ZzvX8SAHt1knzzJNULtZx
* Rename WebRTC start id to clean up count
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014ZzvX8SAHt1knzzJNULtZx
* Don't start a WebRTC stream on a disconnected player
Lit still runs a pending update after the element was disconnected, so
a player added and removed in the same task (or given a new entityid
after removal) started a stream after its last clean up, which nothing
closed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014ZzvX8SAHt1knzzJNULtZx
---------
Co-authored-by: Claude <[email protected]>
* Add a gallery page for ha-picker-combo-box
The picker combo box is only ever seen inside a popover or dialog, which
makes its two render paths and two row highlights hard to inspect. This
page renders it directly in four states: a plain list under the
MAX_PLAIN_LIST_ITEMS threshold, a virtualized list of 200 items, an empty
list, and one with a current value pinned into view.
The component takes no hass, so the page provides internationalizationContext
directly rather than going through a fake hass object.
Co-Authored-By: Claude Opus 5 <[email protected]>
* WIP - combobox gallery
* Tighten the picker combo box gallery text
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Improve documentation for ha-picker-combo-box
Clarified rendering behavior and filtering logic in the documentation. Updated keyboard shortcuts and added information about selected sections.
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Fix Prettier formatting in picker combo box gallery page
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5 <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
* Drop hass from the home summary card
The card consumes the contexts it reads and computes its summary text
from statesContext, so it only re-renders when the text changes. It
subscribes to the energy collection through EnergyCollectionController,
which gains a beforeSubscribe hook to set today's period, and fires
hass-action like the button card. isSecurityAlertActive and the battery
filters take the values they need.
* Test isSecurityAlertActive against every alert domain
* Fetch nightly translations artifact directly with backoff
* Type nightly translations fetch and use shared retry helper
* Retry GitHub API calls in workflow scripts
* Upgrade @home-assistant/webawesome to version 3.14.0-ha.0 in package.json and pnpm-lock.yaml
* Update bundle size budget for frontend-modern and frontend-legacy
The badges and the card subscribe to the energy collection through
EnergyCollectionController and consume contexts for everything else.
statistics-chart moves to contexts with them and regenerates on a theme
change. The controller unsubscribes when config() returns undefined, so
turning energy_date_selection off stops the subscription.
* Migrate energy, power, gas and water cards to context instead of hass
The cards consume only the context slices they read. A new
EnergyCollectionController replaces SubscribeMixin for the energy
collection subscription. ha-chart-base, ha-sankey-chart and
hui-energy-period-selector move to contexts too, and the energy and
recorder helpers take Pick<HomeAssistant, ...> slices.
* Inline the energy collection Pick type
* Pass values instead of hass-shaped objects to energy and recorder helpers
The helpers take states, formatEntityName, callWS, locale or panelUrl
directly. getEnergyDataCollection takes the connection plus an options
object, since it needs several inputs.
* Use consumeContext in EnergyCollectionController
The controller extends ContextController and declares its consumed
values as decorated fields instead of wiring each subscription by hand.
* Consume the internationalization context once in energy cards and charts
ha-chart-base, the period selector and the energy cards read both
localize and locale, so they consume the whole context as _i18n
instead of subscribing to it twice.
* Drop the connected-host guard from EnergyCollectionController
ContextController now defers its setup on connected hosts, so the
controller no longer needs to guard against running before its options
are set.
* Add a consumeContext decorator for reactive controllers
Controllers extend ContextController and decorate their fields with
consumeContext({ context, subscribe, transform }). The base class keeps
each field current and calls contextUpdated() only when the transformed
value changes. ServiceInfoController uses it instead of its own
ContextSubscriptionController wiring.
* Defer context setup on connected hosts and check consumeContext fields
A controller created on a connected host no longer runs subclass code
from super(): Lit calls hostConnected() from addController(), so the
subscriptions are set up after construction instead. consumeContext
also rejects public fields that cannot hold the stored value, like
@consume does.
* Read config and translations from context in shared layouts
* Use context instead of hass in Marketplace pages
* Provide contexts in Marketplace and tabs subpage tests
* Use provideHass for the Marketplace dialog test host
* Pass entities to tab page filters
* Update tab filters when entities change
* Fall back to hass localize in error screen
* Note when to inline error screen localize
* Forward OAuth authorization request parameters to the login flow
* Defer OAuth resource forwarding to a separate change
* Test preservation of empty PKCE parameters
* Do not forward state to the login flow
Core does not use state. The frontend appends it to the redirect
after the login flow finishes. The createLoginFlow type excludes
state, because Core rejects unknown fields.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Reduce the login flow change to forwarding response_type
Keep the positional createLoginFlow signature, the existing property
and import order, and the existing tests. Forward response_type and
send empty PKCE values instead of dropping them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Paulus Schoutsen <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* 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.
* Add light color card feature with hue scrubber
* Keep only the hue example in the scrubber gallery page
* Fix cancel event, card suggestion and white light hue in light color feature
* Update saturation gradient live while dragging hue
* Send terminal slider-moved after keyboard changes in scrubber
* Fix End key in wrap mode and keep focus when swapping light color controls
* Center collapsed hue preview on current hue and animate scrubber wrap by shortest path
* Hide the light color indicators when the light has no color
* Show the Marketplace risks alert full width without an icon
The risks alert spans the full card width, so it reads as a band in the
card. The text aligns with the card content. The alert icon is hidden
because the heading already shows a large warning icon.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wt676c2KAQ1NnNWNRm7dV5
* Scope the full-width alert styles to the risks alert
The error alert for a failed acceptance keeps its icon, corners and
padding.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wt676c2KAQ1NnNWNRm7dV5
* Start-align the Marketplace warning heading and intro
The heading and intro line up with the risks and the checkbox below
them. The warning icon stays centered.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wt676c2KAQ1NnNWNRm7dV5
---------
Co-authored-by: Claude <[email protected]>
The demo registers the Marketplace panel and mocks its read commands.
A list of community repositories shows on the discover, browse and
installed pages, and each repository has its own page. The warning
screen is skipped.
The mocks load with the config panel chunk. A loaded Marketplace config
entry lets the panel start. Commands that change the Marketplace, such
as install and uninstall, are not mocked.
Claude-Session: https://claude.ai/code/session_01Wt676c2KAQ1NnNWNRm7dV5
Co-authored-by: Claude <[email protected]>
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
* Add signed and offset modes to the duration selector
* Remove translation key from the duration selector
* Address review comments on the duration selector
* Expose the duration selector mode in the selector editor
* Remove allow_negative from the selector editor
* Migrate allow_negative to mode when editing a duration selector
* Derive the magnitude of mixed-sign legacy durations from their total
* Test the duration sign and value helpers
* Merge the duration sign helpers into normalizeDuration
* Omit the negative flag for positive signed durations
* Keep the pending offset type in local state instead of writing negative false
* Keep the offset type when the duration is cleared to zero
* Rename the offset type state
* Address remaining Copilot comments on the duration selector
* Simplify the duration sign handling
* Reset the offset type when the duration changes from outside
* Translate the duration selector mode in the selector editor
* Write negative durations as negative components instead of a negative key
* Simplify duration sign handling around a single normalizeDuration
* Keep the offset type as local state and refresh duration fields with live
* Forget the emitted duration after an external change
* Keep the duration sign at zero and use selector units everywhere
* Simplify duration sign handling and compute typed negative values
* Hide the clear button of offset durations
* 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]>
* Add dependency review workflow with npm metadata checks
* Temporarily run dependency review on push
* Remove temporary push trigger from dependency review
* Encode the full package name in npm registry URLs
* Check optional/peer dependencies and fail on npm registry errors
Co-authored-by: timmo001 <[email protected]>
* Convert npm metadata check to TypeScript
* Type workflow scripts with Node 24 types instead of DOM
* Detect npm alias target changes as new dependencies
---------
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
* 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.
* Add store panel as a copy of the HACS frontend
Verbatim copy of src/ from hacs/frontend at d97dce0 into src/panels/store. Imports still point at the HACS submodule layout and the iframe shell is still present; this is the baseline for the port.
* Rewrite store panel imports and drop the iframe shell
Every import that pointed at the old `homeassistant-frontend` submodule now
resolves inside this repository's `src/` tree. Five components were renamed or
removed upstream, so they point at their current equivalents: `ha-button-menu`
and `ha-menu` to `ha-dropdown`, `ha-md-menu-item` to `ha-dropdown-item`,
`ha-fab` to `ha-button`, and `ha-circular-progress` to `ha-spinner`. The
`@material/mwc-button` and `@material/mwc-linear-progress` side-effect imports
are gone with them; those packages are no longer dependencies here.
`main.ts` becomes `ha-panel-store.ts` and registers `ha-panel-store`. It now
receives `hass`, `narrow`, `route` and `panel` from the panel resolver, so
everything that only made sense inside an iframe is removed: applying themes to
the parent element, forwarding haptic and keydown events to the main window,
bridging `location-changed`, the document-level navigation click handler, the
dialog manager, the injected body stylesheet, and the redirect to the default
route. `entrypoint.ts`, `extra.ts`, `html/` and `version.ts` are deleted for the
same reason.
* Move store panel translations into the frontend translation tree
The panel's own translation bundle is gone. Its keys now live under
`ui.panel.store` in `src/translations/en.json`, and `panel.store` gives the
sidebar its "Community store" title. Every `hacs.localize("some.key")` call
became `hass.localize("ui.panel.store.some.key")`, which means the `localize`
member on the `Hacs` interface, the `HacsLocalizeKeys` type and the localize
bootstrap in `HacsElement` are no longer needed. `TranslationDict` is derived
straight from `en.json`, so the moved keys type-check without any casts.
`dialog_about.frontend_version` did not come along: its only consumer was the
frontend version constant that went away with the iframe shell.
* Hold store panel state in the panel instead of a HACS mixin
The panel element now owns the repository list and the info payload as
internal state, loads both on first update and refreshes them from the
store/subscribe signals through SubscribeMixin. That drops the
HacsElement base class and its logger, so the hacs object handed to the
child components is plain data. Children that used to patch that object
with an update-hacs event now fire store-refresh instead and let the
panel refetch.
Also moves every WebSocket command over to the store/ prefix and trims
the info payload to what the backend still returns.
* Update the store panel to current frontend components
Rebuilds the panel on the components the frontend ships today: the
dialogs get their heading from ha-dialog itself and their buttons from
ha-dialog-footer, the overflow menus become ha-dropdown with
ha-dropdown-item, the download action becomes a fab-slotted ha-button,
and the progress and spinner indicators move to ha-progress-bar and
ha-spinner. brandsUrl loses the dropped useFallback option.
Also clears the eslint backlog the panel came in with: no arrow
functions in templates, no console calls, alt text on the brand images,
and private members named consistently.
* Register the store panel and route old HACS paths
Add the store panel to the panel resolver so the backend can serve it,
and move every internal navigation path in the panel from /hacs to
/store. The /hacsfiles static prefix stays, the backend serves it.
Repoint the hacs_repository my-redirect at the store panel and add a
store_repository alias next to it. Both keep the same target, so the
owner, repository, and category parameters still reach the panel's
redirect handler. Existing README badges keep working.
* Rename HACS to Community store in the store panel
Bring the ported panel in line with the repository conventions: panel
parts are ha-*, dialogs are dialog-* with a matching show-dialog-*
helper, and the typo in the overflow menu filename is fixed.
Rename the code identifiers to match, so the panel talks about a store
instead of HACS: StoreData, StoreInfo, StoreDispatchEvent, a store
property on the child elements, storeStyles and --store-* custom
properties.
Reword every user-facing string that named HACS. Backend names stay put:
the hacs_dispatch_* signal values, the /hacsfiles static prefix, the
hacs_repository my-redirect key, and the hacs.xyz documentation and
catalog infrastructure.
The stale HACS-REPOSITORY-PANEL nodeName check now matches the element
it was always meant to match, so removing a repository from its own page
navigates back again.
* Show a message when the store panel is opened without the integration
The panel is registered on every install while the integration is only set up on some, so an empty store must not try to talk to a backend that is not there.
* Link the Community store from the Settings dashboard
The store panel is hidden from the sidebar, so reach it from the Settings dashboard instead and give its pages a way back.
* Fit the store pages into the main layout
The repository page still sized itself to the viewport and the router recoloured the header with sidebar colours, both leftovers from running inside an iframe. Next to the sidebar that made the page a sidebar-width too wide, pushing the toolbar menu off-screen and un-centering the card.
* Let the quick bar create My links for store repositories
The repository page listened for the "m" key on the document body and opened a My link itself, which fought the quick bar's own "m" shortcut once the panel stopped living in an iframe. The quick bar now resolves a store repository page to the hacs_repository redirect by looking up the repository name, and the panel's handler is gone. The reload after installing a dashboard resource no longer goes through the main window helper, and unused scrollbar styles are dropped.
* Remove the external links from the store menu
The About dialog, the issue tracker link and the link to the HACS GitHub organisation pointed at a project outside Home Assistant. The store is part of Home Assistant now, so they go. The documentation link stays until the documentation moves.
* Fix runtime bugs in the store panel
* Remove external HACS leftovers from the store panel
* Tidy types and conventions in the store panel
* Clean up the store panel translations
* Give the store redirect error screen its hass and narrow properties
Without them the screen's back and refresh buttons render without labels.
* Use the store signal names and local resource URLs
The backend renames these in lockstep: the dispatcher signals lose their
`hacs_dispatch_` prefix, and downloaded dashboard resources are served
from `/local/community/` with a `v` cache-busting parameter instead of
the store-owned `/hacsfiles/` route and `hacstag`.
* Drop the disabled reasons the store backend no longer reports
Setup failures now surface through the config entry state instead of a disabled store, so only the runtime reasons remain.
* Rename the Community store to Marketplace
The panel, its elements, dialogs, data types, WebSocket commands,
dispatcher signals and translations now use marketplace, matching the
renamed backend domain. The hacs_repository My link keeps its slug.
* Finish the Marketplace panel
* Confirm replacing a built-in integration with the download
* Fix README links, older versions and grouping in the Marketplace panel
* Handle list continuations and placeholder characters in Marketplace READMEs
- Indented lines inside a list item are treated as text, so their links and issue references get rewritten.
- A README that already contains the private placeholder characters is left alone.
* Keep indented code inside list items literal in Marketplace READMEs
* Fix the warning title, icon colors and dead subscriptions in the Marketplace panel
- The warning takes its title from the Marketplace translations, it is not empty on a direct visit.
- Icons keep the colors of the menu or chip they are in.
- Dismissing new repositories is offered whatever the filters show.
- The dialogs no longer subscribe to an error signal the backend never sends.
- The custom repositories list names the category in the language of the user.
* Handle the edges of the Marketplace panel more carefully
- The warning can be continued again when the panel did not take over.
- Only a query with an owner and a repository opens a repository as a My link.
- Links to /hacs inside Home Assistant open the Marketplace.
- A rate limit after the download dialog closed opens nothing.
- A removed entry takes its catalog with it.
- The quick bar says why it could not make a My link for a repository.
* Type the Marketplace panel against the backend contract
- The data types match what the backend sends.
- Errors are caught as unknown and read through one helper.
- Events are typed from their declarations.
- Spacing uses the theme tokens.
* Make the wording of Marketplace actions clearer
* Leave the way back from the Marketplace warning to the header
* Show the download progress in the Marketplace download dialog
* Offer to set up a newly downloaded integration right away
* Make the Marketplace download dialog plainer and move other versions to the menu
- One sentence says what the download or update does, no paths or technical details.
- Warnings span the top of the dialog, like in the more-info dialog.
- The progress shows where the update dialog shows it, and the button shows it is busy.
- A restart is only mentioned for updates of integrations.
- Choosing another version moved to the repository menu, it opens on its own when the newest version is too new.
* Use the consume decorator of the frontend in the Marketplace dialogs
* Save the Marketplace search, and keep search and filters per session
* Lay out the Marketplace repository page like the other Settings detail pages
* Bring the Marketplace list page in line with the other Settings lists
* Use the standard Settings dialogs, styles and strings in the Marketplace
* Name the Marketplace data helpers like the rest of the frontend and drop the global refresh event
* Center the Marketplace warning and say how long accepting it lasts
* Call it installing in the Marketplace, and set up right after installing
* Use the uninstall command of the Marketplace
* Fix the row menu, the failed setup screen and README links in the Marketplace panel
* Guide uninstalling an integration that is in use, and offer it after deleting the last entry
* Fix reinstalling, filters, stale pages and README handling in the Marketplace panel
* Explain why an integration in use can not be uninstalled
* Test that accepting the warning and uninstalling in use really happen
* Point HACS links and icons at the Marketplace, and clarify its copy
* Share the error message and menu rendering of the Marketplace panel
* Split the long renders of the Marketplace panel
* Treat Marketplace repository ids as strings
* Guide the user after installing or uninstalling from the Marketplace
* Count down before the Marketplace warning can be accepted, and show it only once
* Show what to do next and who made it on the Marketplace repository page
* Title the Marketplace repository page Marketplace and keep signals one line
* Say which Home Assistant a Marketplace repository requires
"Works with Home Assistant 2025.1 or newer" read as a promise it had been tried there. It is a minimum the repository asks for, so say it that way.
* Explain adding to the Marketplace from a GitHub link
The dialog was called "Custom repositories" and opened on a list of them. Someone adding their first one saw an empty list and two bare fields.
It now says what it is for, and that what is added this way is made by the community like everything else in the Marketplace. Both fields say what they want. The list of what was added from links comes after that, under its own heading, and only once there is any.
* Give the Marketplace Discover, Browse and Installed tabs
The Marketplace was one long table, grouped by status. Finding what you have installed, or what waits for an update, meant scrolling past everything else.
Discover opens with what is popular and what changed lately. Each section links to Browse, sorted and filtered the way the section picked from it. The sorting and filters are in that link, so it works in a new tab or when shared too. Browse is the full table, without the grouping. Installed lists only what is installed, and its tab says how many updates wait.
On the tables, the icon shows a check for what is installed and an arrow for what waits for an update. The search says how many items it looks through. Adding from a GitHub link also got its own button in the toolbar.
Tabs take an optional badge for the update count.
* Find out what is added from a link in the Marketplace
The dialog asks only for the link. What it holds is found out when it is added, and only when that does not settle it, the dialog asks for the type, from what it might be.
* Fix races and lost answers in the Marketplace panel
The add from link dialog could add another link than the one it looked at, a See all link followed a second time kept the filters of the first, Retry did not retry a My link, and a My link that finished late replaced the repository opened meanwhile. A /hacs link from a dashboard showed the Marketplace without its translations. Uninstalling after deleting the last entry could hang when it failed, and a failed removal from the list closed as if it worked. Code inside a quote in a README got issue links.
* Tidy up small things in the Marketplace panel
The README was prepared again for every change of an entity, a screen reader heard the name of a tab but not its badge, and four exports and a type member were left from before.
* Show Marketplace errors in the language of the user and refresh less
Errors from the backend are translated with their key and placeholders in
the panel and when offering to uninstall after deleting an entry, with the
message of Home Assistant as fallback. The repository page reloads after
updating its information, since the list does not carry everything it
shows. Repository signals share one list request, with one more after it
when anything changed meanwhile, and Discover parses each date once.
* Fix the Marketplace strings that showed placeholders and wrong plurals
The add repository question showed {repository} literally, the quotes
around it escaped the placeholder. Seconds, downloads, stars and issues
now say one instead of ones, the status names match across the list and
the repository page, and the GitHub and log messages read the same as
the rest.
* Handle the review of the Marketplace panel
Links to /hacs reach beforeRender again, a route alias was resolved
before it. A refetch on a signal no longer opens the GitHub dialog by
itself, a failed release list is told once, and a failed first load is
translated without logging to the console. Deleting an entry only offers
the uninstall for custom integrations, after the restart and credential
prompts. The YAML resource address carries the version and is encoded.
Applied links mark their history entry instead of listening on the
window, and the last updated column sorts on a number.
* Rewrite Marketplace README addresses once they are rendered
ha-markdown gets an optional rewriteUrl, which runs over every link and
image of the rendered HTML in an inert template, before anything loads.
The README of a repository uses it, so reference links, entities and
unquoted attributes no longer slip through as addresses on Home
Assistant. The regular expressions that set aside links and addresses
for issue references only start where those can start, a README with a
long word or many unclosed links no longer freezes the page.
* Replace ha-ripple Material base with own implementation
* Remove ha-ripple origin comment
* Restore ha-ripple control setter and CSS zoom handling
* Defer ha-ripple forced-colors check to event handling
* Move pickers onto the new list components
Pickers, the quick bar, the automation element search and the card
picker entity tree now use ha-list-base and its item family instead of
Material Web list items.
- Add a virtual-focus mode to ha-list-base and ha-list-virtualized:
focus stays in a search field and the list marks the active row.
- Render picker rows as ha-list-item-option, with the current value as
the selected option.
- Make ha-combo-box-item a presentational row layout on ha-row-item.
- Render ha-picker-field as a native button with the clear button next
to it.
- Let held arrow keys repeat in pickers and the automation search.
- Fix list items not unregistering after they leave the DOM, and an
unhandled rejection when a virtualized list is removed mid-layout.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Fix section titles
* fix e2e
* mage entity tree search like picker and automation
* remove comments
* fix picker field aria label
* fix disabled field style selector
* Update src/components/ha-picker-combo-box.ts
Co-authored-by: Petar Petrov <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Petar Petrov <[email protected]>
The persistent cache version still hashed `.yarn/patches`, which moved to
`patches/` in #54392. The lookup is guarded by `existsSync`, so it failed
silently and patch contents stopped contributing to the version at all.
That hash is the only thing that notices a patch change: a patch alters a
package's files but not its version, which is all rspack's node_modules
snapshot checks. Editing a patch therefore left the cache version
identical and the build reused stale compiled modules.
Co-authored-by: Claude Opus 5 <[email protected]>
* 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]>
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
-`consume` decorator and`ContextSubscriptionController`: `src/common/decorators/consume.ts` (use instead of `@lit/context`'s `consume` and `ContextConsumer`, enforced by ESLint)
-`consume` decorator,`ContextSubscriptionController`, and `consumeContext` / `ContextController` for reactive controllers: `src/common/decorators/consume.ts` (use instead of `@lit/context`'s `consume` and `ContextConsumer`, enforced by ESLint)
| `reducedMotionContext` | `prefers-reduced-motion` media query |
Lazy contexts subscribe on first consumer and tear down after the last consumer: `labelsContext`, `fullEntitiesContext`, `configEntriesContext`, `manifestsContext`, `triggerDescriptionsContext`, and `conditionDescriptionsContext`.
@@ -50,7 +51,9 @@ Import `consume` from `src/common/decorators/consume`, not from `@lit/context`.
-`@state()` plus `@consume` plus `@transform`: rerenders only when the transformed value changes.
-`@consume` alone: the field stays current but never triggers a render. Use this for values only read in event handlers or callbacks, such as `apiContext`, or for data read on demand, such as picker item callbacks.
In controllers or for lazily created subscriptions, use `ContextSubscriptionController` from the same file instead of `@lit/context`'s`ContextConsumer`, which also forces a host update. Its callback must store the value in a reactive field or call `host.requestUpdate()` itself when the host needs to rerender.
In reactive controllers, extend `ContextController` and decorate fields with `consumeContext({ context, subscribe, transform })` from the same file. `transform` stores one part of the context value, and`contextUpdated()` runs only when that part changes. `@state()` and `@transform` do not work on controller fields; call `host.requestUpdate()` when the host needs to rerender. Example: `src/data/service-info-controller.ts`.
For lazily created subscriptions, use `ContextSubscriptionController` from the same file instead of `@lit/context`'s `ContextConsumer`, which also forces a host update. Its callback must store the value in a reactive field or call `host.requestUpdate()` itself when the host needs to rerender.
Use entity-scoped helpers when the component watches an entity ID held on the host:
- Consult the public [frontend developer documentation](https://developers.home-assistant.io/docs/frontend/) for documented architecture, data flow, design, and development workflows.
- Consult the public [frontend contributor documentation](https://contributors.home-assistant.io/frontend/) for documented architecture, data flow, design, and development workflows.
- For new leaf components, load `ha-frontend-contexts` and verify they consume narrow contexts instead of introducing a broad `hass` property; containers and external APIs may still require `hass`.
- Verify backend assumptions against the owning Core, Supervisor, or WebSocket implementation, and component assumptions against the exported component contract.
- Prefer canonical repository helpers and test setup over duplicate local implementations.
on:pull_request_target# zizmor: ignore[dangerous-triggers] -- safe: only runs actions/labeler, no PR code checkout
on:pull_request_target# zizmor: ignore[dangerous-triggers] -- safe: checks out base repo scripts only and reads PR files through the API, never PR head code
# Runs one at a time per pull request, so an older run can't undo a newer one
"_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>`.",
* Side-effect imports register a custom element. ESLint's unused-import rules
* cannot judge them — there is no binding to be unused — so a registration
* outlives the template that needed it and nothing reports the dead import.
*
* Deliberately not fixable: removing the import is only safe once every file
* that renders the element imports it itself, which this rule cannot see.
*
* Off in eslint.config.mjs while a backlog of those paired cases remains;
* `pnpm lint:element-imports` enables it as a warning to list them. Flip it to
* "error" there once the list is empty.
*
* No disable comments while it is off: ESLint reports them as unused
* directives, which fails lint:eslint. Imports that must stay get theirs in
* the change that turns the rule on.
*/
exportconstnoUnusedElementImport={
meta:{
type:"suggestion",
docs:{
description:
"Disallow side-effect imports of a custom element the importing file never uses",
},
schema:[],
messages:{
unused:
'"{{request}}" only registers <{{tags}}>, which this file never uses. Remove the import, adding it to whichever file renders the element if that file relies on this registration.',
A horizontal control where the value sits under a fixed window in the middle and the background strip moves. Drag the strip to change the value, or tap a point of the strip to bring it under the window.
Set `--control-scrubber-track-width` to make the strip wider than the control, so only a part of the range is visible at a time and each pixel of drag changes the value less. Set `wrap` for cyclic ranges such as hue, where the strip repeats and the value wraps around at the bounds. Set `show-handle` to draw the window, and leave it off when there is no current value to point at, such as a light that is off.
| getItems | Function | - | Required. Returns the items, optionally filtered by search string and section. |
| getAdditionalItems | Function | - | Extra items appended to search results, for example "add new". |
| value | String | - | Id of the current item. Marks its row `selected`, and the virtualized list opens with that row in view. |
| label | String | - | Placeholder for the search field. Falls back to a localized "Search". |
| mode | "popover"/"dialog" | "popover" | Adjusts padding for the surface the list sits in. |
| shown | Boolean | true | Whether the surface finished animating. `ha-generic-picker` sets it so the virtualizer does not measure rows mid-animation. |
| sections | Array | - | Filter chips. Section headers are plain strings returned by `getItems`, and the sticky title shows the one above the first visible row. |
| selectedSection | String | - | Section filter selected when the list is initialized. |
@@ -33,4 +33,4 @@ We want to make it as easy for designers to contribute as it is for developers.
## Developers
Everything you need to get started developing can be found in our <a href="https://developers.home-assistant.io" rel="noopener noreferrer" target="_blank">Home Assistant Developer Docs</a>.
Everything you need to get started developing can be found in our <a href="https://contributors.home-assistant.io/frontend/" rel="noopener noreferrer" target="_blank">Home Assistant contributor docs</a>.
Youcanuseour[\`ha-alert\`](https://design.home-assistant.io/#components/ha-alert) component in markdown content rendered in the Home Assistant Frontend.
Youcanuseour[\`ha-alert\`](https://contributors.home-assistant.io/design/components/alert/) component in markdown content rendered in the Home Assistant Frontend.
- start the dev server with following optional env vars:
- `SUPERVISOR_HOST` to have real supervisor data, you can [setup a supervisor remote API access](https://developers.home-assistant.io/docs/supervisor/development/#supervisor-api-access) and set the host of your supervisor. e.g.: `SUPERVISOR_HOST=192.168.0.20:8888`
- `SUPERVISOR_HOST` to have real supervisor data, you can [setup a supervisor remote API access](https://contributors.home-assistant.io/os/supervisor/core-connection/#get-access-to-the-supervisor-api) and set the host of your supervisor. e.g.: `SUPERVISOR_HOST=192.168.0.20:8888`
- `SUPERVISOR_TOKEN` the supervisor api token you get from the Remote API proxy Addon Logs
- `FRONTEND_PATH` the path inside your container should be `/workspaces/frontend`
- example: `SUPERVISOR_TOKEN=abc123 SUPERVISOR_HOST=192.168.0.20:8888 FRONTEND_PATH=/workspaces/frontend go run main.go http.go mdns.go`
'printf "%s\n" "Translation files should not be added or modified here. Instead, make the necessary modifications in src/translations/en.json. Other languages are managed externally. Please see https://developers.home-assistant.io/docs/translations/ for details." '+
'printf "%s\n" "Translation files should not be added or modified here. Instead, make the necessary modifications in src/translations/en.json. Other languages are managed externally. Please see https://contributors.home-assistant.io/translate/ for details." '+
Loaded 100 of 999 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.