* Split agents into skills, keeping architecture and commands in main file with routing for agents which dont follow standards
* Rename `copy` skill to `user-facing-text`
The consume* decorators paired @lit/context's @consume({ subscribe: true })
with @transform to narrow a context down to a single value. ContextConsumer
calls host.requestUpdate() on every provider notification, so every consumer
ran an (often empty) render cycle on every unrelated statesContext change.
Replace the built-in consumer with a small reactive controller that subscribes
the same way but leaves update scheduling to the property setter, which already
gates on hasChanged. A render is now requested only when the selected value
actually changes.
The preset ranges in ha-date-range-picker were computed once in
connectedCallback(). If the component connected before the translation
chunk containing the range labels had loaded, localize() returned an
empty string for every key, collapsing all presets into a single
empty-keyed entry — the preset list rendered blank and never recovered.
Compute _ranges in willUpdate() instead, recomputing whenever _i18n,
_hassConfig, or extendedPresets changes. This also fixes the presets
not updating when the locale or timezone changes while the page is open.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Add HTTP server settings to the network panel (#51981)
* Fix focus loss in ha-input-multi when items change
* Add HTTP server settings to the network panel
* Surface fetch errors and validate before saving in HTTP config form
* Update src/panels/config/network/ha-config-http-form.ts
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
* Group HTTP form fields into collapsible sections
* Add bottom margin to ha-input-multi add button
* Only apply add-button margin when helper text is present
* Update HTTP config form to new WebSocket API
---------
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
* HTTP config: confirm or revert pending changes after restart (#52452)
* HTTP config: prompt admin to confirm or revert pending changes after restart
After saving HTTP server settings, core now writes the new config as pending,
restarts Home Assistant, and keeps the previous stable config as a recovery
fallback. The frontend now:
- Reads {stable, pending} from http/config and adds a promoteHttpConfig call.
- Asks the admin to confirm before saving (since saving now auto-restarts).
- After reconnect, shows a non-dismissable popup for admins with the changed
fields, where Confirm promotes pending -> stable and Revert clears pending
(which triggers another restart).
- Renders an info banner above the form whenever pending is set.
* Drop pending banner and pending handling from HTTP form
The popup blocks any other interaction while pending exists, so the form
is only reachable when pending is null. Stop fetching/displaying the
pending config and the unconfirmed-config banner.
* Use HTTP settings in dialog title
* Show auto-revert countdown in HTTP pending-config dialog (#52799)
* Show auto-revert countdown in HTTP pending-config dialog
Surface the new revert_at deadline (core PR #174428) in the popup that
opens after saving HTTP server settings. While pending, the dialog shows
a ticking 'Settings will revert in M:SS.' line; when the deadline passes
it switches into a reverted state with an info alert and a Close button.
Confirm/Revert remain available until the deadline.
* Add bottom margin to ha-alert in pending-config dialog
Without it the changes list sits flush against the reverted-state info
alert.
* text tweak
* Render under-a-minute revert countdown as '45 seconds'
secondsToDuration returns a bare '45' for under-a-minute durations, so
the line read 'Settings will revert in 45.'. formatNumericDuration uses
Intl unit formatting in that range, giving 'Settings will revert in
45 seconds.', and keeps the M:SS format above one minute.
* Fix Prettier formatting in dialog-http-pending-config.ts and ha-config-http-form.ts
---------
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* No need to show rename when alias is already provided
* Update src/panels/config/script/ha-script-editor.ts
* Apply suggestion from @MindFreeze
---------
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Fix Z-Wave JS config parameter value type
Type ZWaveJSNodeConfigParam.value as number | null instead of any,
matching what the backend sends. This surfaced two comparison bugs:
- The enumerated picker no-op check compared the stored number against
the picker's string value, so it never matched and re-selecting the
current option re-sent the command to the device.
- The numeric input guard coerced null (unknown value) to 0, so
entering 0 for a parameter with an unknown value was ignored.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Treat empty numeric input as invalid instead of 0
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Remove usage labels from energy cards and rename Sources to Costs
- Remove redundant "usage" text from energy card titles and labels
- Simplify Sources table labels by removing "total" text
- Rename Sources table title to Costs
* Apply suggestion from @mindfreeze
* Apply suggestion from @balloob
* Keep 'total' in source type summary labels
Addresses review feedback from @karwosts — in the detail view,
dropping "total" from labels like "Gas total" makes the summary
row ambiguous when listed alongside individual source names.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Dont open demo
* Scripts to run script/develop*
* Scripts for dev demo and gallery
* Background wrapper for agents with `--background`
* Background `yarn dev` and `dev:serve` for agents
* Dedupe lifecycle functions
* Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
For built-in triggers, conditions, and actions, the help icon in the editor
config pane (and Developer Tools) linked to the integration page. Point it at
the dedicated page for that specific trigger/condition/action instead, e.g.
/triggers/air_quality.co2_changed. Custom integrations keep their own
documentation URL.
The About templates panel still pointed at the upstream Jinja2 docs and a
single extensions page. Rewrite the intro and link to the current templating
documentation instead: the learning guide (introduction, working with states,
debugging) and the searchable template functions reference.
* Show device area and entity count in connected devices list
Display each connected (via) device's area and entity count as secondary
text, so devices that share a name (e.g. multiple Floor Heating devices)
can be told apart by their location and size. The count is read from the
full entity registry so it matches what the integration page shows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Apply suggestions from code review
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Fix duplicate logbook entries after reconnect in more info dialog
When a more info dialog is left open while the app is backgrounded, the
WebSocket connection drops and reconnects on resume. The logbook stream
subscription relied on home-assistant-js-websocket's auto-resubscribe,
which replays the original subscription with its stale start_time. The
backend then resends the entire historical chunk, and ha-logbook appends
streamed events without deduplicating, so every entry was shown twice
(and a third time after another background/reconnect cycle).
Mirror the approach already used for the history stream: disable the
library's auto-resubscribe for the logbook event stream and have
ha-logbook listen for the connection "ready" event, resubscribing from a
clean state on reconnect instead of appending a replayed history chunk.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WYMwT7ZQJrjyzrNfGQyWaU
* Simplify logbook reconnect comments
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
* Localize hardcoded UI strings in lovelace, logs, cloud, and media browse
Wire existing translation keys where available and add scoped keys for lovelace error sections and cloud support package privacy text.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Keep loading ellipsis outside translatable strings
Localize the loading and preview labels without dots, then append ellipsis in the template so translators are not asked to copy punctuation.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix manual entry localize key path
Use ui.components.selectors.selector.types.manual so the key resolves in en.json and TypeScript.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Use property bindings for localized dialog and badge labels
Bind headerTitle and label as properties so localized strings pass correctly to ha-dialog and ha-badge.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Format
* Use better path
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Use choose selector for legacy trigger fields
Replace the duration-only selector on the `for` field in the state,
numeric_state, and template triggers with a choose selector that
offers both duration and template options.
Replace the hand-rolled lower_limit/upper_limit select toggle for
above/below in the numeric_state trigger with a choose selector
that switches between a fixed number and an entity reference.
Add translation entries for the choose selector toggle button labels.
* Shorten the numeric state value toggle label
Use "Value of an entity" instead of "Numeric value of another entity" for
the numeric state trigger toggle, so it stays compact.
Add a "Frequencies" column to the radio frequency devices (proxy) list so
users can see which frequency bands each transmitter supports. The supported
frequency ranges are formatted into a human-readable, locale-aware string
(picking Hz/kHz/MHz/GHz automatically) with a helper in the data layer.
Claude-Session: https://claude.ai/code/session_01SYyMTtBdrt7EBrVEt869Uw
Co-authored-by: Claude <noreply@anthropic.com>
The jinja2 editor mode is rendered on a YAML base, so Ctrl+/ inserted a "#"
line comment, which does nothing useful in a template. Give the jinja2
language a Jinja block comment token so toggle-comment wraps with {# #},
while the plain YAML mode keeps its # comment.
* Add Zigbee configuration handling and loading state to ZHA dashboard
- Introduced `findActiveZhaConfigEntry` function to filter active Zigbee config entries.
- Updated ZHAConfigDashboard to manage loading state and display a spinner while loading.
- Added UI elements for not configured state with appropriate translations.
- Created tests for `findActiveZhaConfigEntry` to ensure correct functionality.
* fix: remove unused config entry logic and update initialization checks
* Restore active config entry filter in _fetchConfigEntry
* Remove redundant config entry ternary in render
* Update babel monorepo
* Migrate Core-JS polyfilling for Babel 8
Babel 8.0.1 removed preset-env's `useBuiltIns`/`corejs` options. Replace
them with the babel-plugin-polyfill-corejs3 provider directly
(`usage-global`), and pin transform-runtime's `moduleName` to
`@babel/runtime` so the provider doesn't redirect helpers to the
uninstalled `@babel/runtime-corejs3`.
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Update babel monorepo to v8
* Bugfixes option has been removed and is enabled by default
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
The manage-cloudhook dialog showed "managed by an integration and cannot
be disabled" for webhooks the user enabled manually (e.g. an automation
webhook trigger), while letting integration-managed webhooks (e.g. the
mobile app) be disabled.
Core sets `managed: true` only for cloudhooks created programmatically by
an integration (via async_create_cloudhook) and `managed: false` for hooks
the user creates through the cloud panel. The dialog negated this flag, so
the message and disable link were shown for the wrong cases. Check the flag
directly so user-created hooks expose the disable link and integration
hooks show the informational message.
Claude-Session: https://claude.ai/code/session_01QuvU786Re5Rm1iCa8BhT8d
Co-authored-by: Claude <noreply@anthropic.com>
* Use related context in entity picker
* Include current item in context builder
* Fix
* Add tests
* Remove comment
* Support area context in card
* Add from rebase
* Add window.haContext.related to tests
* Sign brand images in state-badge via connection context
state-badge only signed entity_picture URLs when a `hass` object was
passed, calling `hass.hassUrl()` to append the brands access token.
Components migrated to Lit contexts (e.g. ha-config-updates on the
Settings → Updates page) no longer pass `hass`, so brand icon URLs like
/api/brands/integration/<domain>/icon.png were fetched without a token,
returning 403 and triggering unauthenticated-request log entries in core.
Consume connectionContext to obtain `hassUrl` so the token is added even
when `hass` isn't provided, and skip brand URLs entirely when they can't
be signed yet so no unauthenticated request fires.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Remove now-unused hass property from state-badge
With brand image signing handled via connectionContext, `hass` was only
used by state-badge to reach `hassUrl`. Drop the property entirely and
remove the `.hass` binding from all call sites; the connection context
provides `hassUrl` everywhere the component renders.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Fix app panel flicker while waiting for app to start
Keep the loading screen up as a stable overlay while the ingress iframe
is still returning 502, and reload the iframe content in place instead
of unsetting the addon and rebuilding the whole panel each retry.
https://claude.ai/code/session_019fWWygHqYbM2H6FN9jWJu1
* Increase timeout
---------
Co-authored-by: Claude <noreply@anthropic.com>
Make energy dashboard card visibility declarative via the catalog
Route the five energy view strategies and the dashboard strategy through
the shared ENERGY_CARD_CATALOG instead of re-deriving each card's
applicability conditions inline. A new isEnergyCardVisible() helper makes
the catalog the single source of truth for whether a card is shown, so the
strategies and the customise dialog can no longer disagree.
Behavior-preserving; adds a contract test pinning isEnergyCardVisible to
the catalog for every entry.
A state condition with match "any" joins its entities with "or", but the
summary kept a plural verb for multiple entities, reading "If A or B are
on". With "or" English uses singular agreement: "If A or B is on". The
match "all" case joins with "and" and correctly stays plural.
Nest a select on a new matchAny flag inside the multiple-entities plural
branch so the verb agrees with the join. Other languages keep their
count-based plural (the extra argument is ignored). Add a test that renders
the actual en.json string to lock in the grammar.
Negative "untracked" values (tracked devices reporting more than total
consumption, usually a meter resolution mismatch) rendered as confusing
below-zero bars in the devices detail graph. Move them into their own
"Over-reported consumption" series with its own legend item so users can
toggle them off, and only add the series when negatives actually exist.
The gauge card reads its display value from the formatted state parts.
A monetary value is split into multiple value parts around the currency
symbol, so the minus sign lands in its own part. Taking only the first
value part meant a value like -182.95 GBP rendered as just "-".
Join all value parts so the full number is shown again.
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
The left list column used flex: 4 against a flex: 6 right panel. Because
flex items default to min-width: auto, the right panel's content (which
varies per group) could dictate the split, so the left column width
shifted while browsing groups.
Give the left column a fixed width (flex: 0 0 360px) and let the right
panel take the rest with min-width: 0 so its content shrinks instead of
pushing the left column around.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The metadata suggestion schema sent the internal category and floor IDs as
the select options, so the model only saw opaque IDs and never picked one.
The result processor already maps the chosen value back to an ID by name, so
the two halves disagreed.
Offer the names as the option values instead, matching what the processor
expects, so the model can actually choose a category or floor.
The network status on the Z-Wave dashboard added the not-ready nodes to
the provisioning entries and labeled the total as not included. Not-ready
nodes are included though, their interview just has not completed yet, so
this was confusing.
Report not-ready nodes as not ready and keep not included for the
provisioning entries that have not joined the network yet.
The day, hour, minute, second, and millisecond inputs were rendered as
separate fields with a label that was not associated with them, so screen
readers announced them as unrelated inputs.
Wrap the fields in a role=group and label that group with the visible label,
so they are announced together as one labeled control.
* Add UTC time zone to the time zone picker
The time zone list is built from google-timezones-json, which is missing
the bare "UTC" and "Etc/UTC" zones. Both are valid IANA identifiers and a
common server default, so an instance configured to UTC showed up as an
unknown time zone in the settings.
Add the two zones to the picker options, guarded against duplicates in
case the source list starts including them.
* Accept UTC time zone in the clock card config
The clock card validated its time_zone against the raw timezone list, so
it rejected UTC even though the picker now offers it. Validate against the
shared timezone options instead, keeping a single source.
* Correct the invalid Asia/Sakhalin time zone id
google-timezones-json ships Asia/Yuzhno-Sakhalinsk, which is not a valid
IANA identifier, so selecting it failed backend validation. Map it to the
correct Asia/Sakhalin id.
Subscribing to a template render is asynchronous and is triggered from a few
places, so two renders could overlap. The second subscription overwrote the
handle of the first, leaving it running, and its late results overwrote the
current ones. That is why the result window sometimes showed the output of a
previous template until the editor was nudged.
Track a render id and bail out (and ignore incoming results) once a newer
render has started.
When a voice was required and no value was set, the picker displayed the
first voice in the dropdown but kept its own value undefined and never
fired a value-changed event. As a result, the parent (for example the TTS
test card in the media browser) never learned the voice: the selected
voice id footer stayed hidden and no voice was sent on synthesis. This was
most noticeable for languages with a single available voice, where the
selection could not be changed to force an event.
Auto-select and emit the first voice when one is required and the current
value is missing or no longer valid for the loaded voices, so the value
matches what the dropdown shows. Non-required usages keep clearing the
value as before.
The control slider flipped its value mapping whenever the document
direction was right-to-left, including for vertical sliders. RTL only
mirrors the horizontal axis, so a vertical slider ended up upside down:
the light brightness and color temperature sliders in the more info
dialog reported the opposite of what they showed (1% gave the brightest
output, 100% the dimmest).
Only mirror for right-to-left when the slider is horizontal.
* List main entities first on the device page
* Update src/panels/config/devices/device-detail/ha-device-entities-card.ts
Co-authored-by: Aidan Timson <aidan@timmo.dev>
---------
Co-authored-by: Aidan Timson <aidan@timmo.dev>
* Add rf panel
* Tweaks
* Align canShowPage with dev PageNavigation type
* Restore page filter check in canShowPage
* Add transmitters list to radio frequency panel
Show transmitter status and a devices data table (name, type, last used)
with links to the device info page, and use the radio tower domain icon.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Update src/panels/config/integrations/integration-panels/radio_frequency/radio-frequency-transmitters.ts
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* adjust comments
* Rename radio frequency transmitters page to devices page
Mirror the infrared panel: rename the transmitters page to a devices
page, source the type column label from the integration's
entity_component name, fetch transmitters in the router and pass them to
both pages, and align user-facing copy to "devices".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Handle radio frequency transmitter load errors
Wrap the transmitter fetch in try/catch and surface failures via an
alert dialog instead of leaving an unhandled rejection.
Also drop the duplicate PageNavigation.filter declaration introduced by
the dev merge (it already exists on dev).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Add IR panel
* Tweaks
* Redesign infrared panel: device dashboard + table
Show a Bluetooth-style status card with the count of online IR devices,
linking to a separate data-table page that lists devices grouped from
their proxy entities. Devices expose a type (Emitter, Receiver, or
"Receiver, Emitter") and a "Last used" column derived from the most
recent entity state timestamp.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Pass localize instead of hass into _data memoizeOne
* Fix Prettier formatting in infrared-devices-page.ts
* Derive infrared devices from registries instead of infrared/list
Drop the infrared/list WebSocket call and compute the device dataset
from the entity/device registries in the dashboard router, passing it
down to the dashboard and devices pages.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Use infrared entity_component device class names for type labels
* Remove fallback strings from localize calls in infrared devices page
* Fix device class translation
* Cleanup
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Redesign the Activity (logbook) as a timeline with entity context
* Update color
* Refine logbook timeline layout and entry rendering
- Three layout modes in ha-logbook-entry: wide (entity → state inline),
compact (entity/state + context/time), inline (state + cause icon + time)
- Entity name bold in wide and compact modes, consistent with tile card
- Cause icon shown inline next to the time in inline (single-entity) mode
- Unavailable state rendered as an empty circle dot
- Flash icon for entity-triggered causes
- "Show more" chevron link in logbook card, device page, and area page
- Extract _renderWide / _renderCompact / _renderInline from render()
- Scope entity-name flex layout to .line1 > .entity-name (compact only)
Co-Authored-By: Paul Bottein <paul.bottein@gmail.com>
* Show cause icon in inline logbook entries
- Show cause icon (user avatar, trigger type, integration brand) next to
the time in single-entity inline mode
- Use ha-trigger-icon for trigger-platform causes
- Use ha-domain-icon with brand-fallback for integration causes when
context_domain is available, falling back to mdiPuzzle
- Tooltip with cause name on hover
- Icon size 18px, user avatar 18x18px
Co-Authored-By: Paul Bottein <paul.bottein@gmail.com>
* Adjust cause icon sizes: 18px standalone, 16px inline with text
Co-Authored-By: Paul Bottein <paul.bottein@gmail.com>
* Fix somes issues
* Refine logbook timeline rendering
* Fix logbook dot alignment, header link, and graph colors
* Use deterministic colors for select/input_select in logbook timeline
Assign colors by options list index instead of encounter order so
logbook dots always match the history chart colors, regardless of JS
chunk boundaries.
* Add relative time to logbook entries
Show short relative time alongside absolute in all layouts.
Cause moves to its own third line in compact when icon mode is active.
* Replace dual time display with click-to-toggle in logbook
Clicking any time value toggles between absolute (default) and relative
short format. State lives in the renderer and propagates via Lit
re-render when shouldUpdate allows it.
Date headers now show "Today · June 15" and "Yesterday · June 14"
for recent dates via Intl.RelativeTimeFormat.
* Fix time toggle not updating entries in virtualizer mode
Use @queryAll to directly update showRelative on all visible entries
after toggling, covering the virtualizer case where Lit re-render
alone does not propagate prop changes to already-rendered items.
Also remove the !item guard in _renderRow to fix the RenderItemFunction<T>
type mismatch.
* Refine logbook compact/wide layout and cause display
- Move time column to the right in wide layout
- Right-align time in compact cause row by wrapping cause+trace in meta-main
- Hide cause icon/label for automation and script entries in compact/inline mode (only show trace link)
- Make automation/script entity name always clickable (opens more-info)
* Refactor logbook cause into typed kinds with text phrases
Replace the untyped `iconPath`/`triggerPlatform` fields on `LogbookCause`
with a `kind` discriminator (`user`, `automation`, `script`, `state`,
`scheduled`, `homeassistant`, `integration`).
In timeline layout, causes now render as readable text phrases
("By Paul", "By automation: Mode nuit", "By state change: Porte entrée",
"Scheduled", "Via HomeKit") with a `·` separator before "View trace".
Entity names in those phrases are clickable when an entity id is available.
In list/inline layout, the icon badge uses the kind to pick the right
icon (avatar, robot, script, brand domain, puzzle) — no trigger-type
icon component needed anymore.
* Add show-cause mode to logbook list layout
Add a `show-cause` boolean prop to `ha-logbook-entry`, `ha-logbook-renderer`,
and `ha-logbook` that switches list mode from a compact icon badge to a full
cause phrase on a third line.
The third line uses a fixed-width prefix span and a flex-1 truncatable entity
button so long automation/script/entity names ellipsize cleanly. The trace
link always stays right-aligned on the same line.
Enable the mode in `ha-panel-logbook` so the main activity feed shows full
cause context for every entry.
* Rename logbook model identifiers to match HA conventions and clean up
- Rename resolve*/build* → compute*, kind → type, LogbookWhat → LogbookValue,
model.what → model.value across model, renderer, and tests
- Merge EntryRenderCtx into LogbookRenderItem (extends LogbookItem) so layout
methods receive one flat object instead of ctx.model.xxx
- Inline _causeUser, drop dead possibleEntity branch in message formatter
- Remove unused .cause and .cause-name CSS classes; fix padding-block
inconsistency on timeline content
* Use ha-relative-time in logbook for auto-updating relative times
Replace the static relativeTime() string with <ha-relative-time> so the
displayed time updates every 60 s without a full re-render. Add a format
prop (Intl.RelativeTimeFormatStyle) to ha-relative-time to support the
short style needed by the logbook. Fix text-overflow ellipsis in the time
column by restructuring .time to use align-items: stretch with an inner
.time-content block that owns overflow/ellipsis, and display: contents on
ha-relative-time so its text participates in the parent's inline flow.
Also rename computeLogbookItem's internal param from item to entry to
avoid shadowing the outer item variable.
* Fix automation run value detection and timeline arrow display
User-triggered automation runs had context_user_id set but no source or
context_event_type, so isAutomationRun was false and the raw backend
message "triggered" (lowercase) was shown instead of the localized
"Triggered". Add context_user_id to the isAutomationRun check so all
automation runs get the proper localized value.
Restore the state arrow (→) in the timeline for all value.type === "state"
entries, including automation runs.
* Fix ha-relative-time interval and use textContent
The 60-second auto-update interval was never started when datetime is set
via Lit property binding, because connectedCallback runs before Lit sets
properties. Move the interval start/stop logic into update() watching the
datetime property change instead.
Also replace innerHTML with textContent since the relative time string is
always plain text.
* Remove comments
* Feedback
* Align integration dashboard grid with design tokens
Replace hardcoded values with design tokens
Add mobile-safe min() in minmax, and add margin/margin-bottom to match
the apps page container spacing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Remove useless margin bottom
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add config panel with cloud to demo
Enable the config panel in the demo and add a logged-in Home Assistant
Cloud account mock so the cloud panel renders with realistic data
(subscription, remote access, text-to-speech, and webhooks).
https://claude.ai/code/session_01LuAsCpbhKpSKufH9FAHNxh
* Add rich mock data for all config panels in demo
Mock the WebSocket commands behind the remaining config panels so they
render with realistic data instead of erroring: integrations, devices,
entities, helpers, automations, scripts, blueprints, voice assistants,
zones, people, logs, backup, about, network, tags, and application
credentials. Adds coherent demo config entries, devices, and integration
manifests so the integrations and devices dashboards are populated.
* Demo: load brand images from CDN and fix more config panels
- Map brand/hardware images to the public brands.home-assistant.io CDN in
demo mode, since there is no backend to serve the token-gated brands API.
- Honor the config entries domain filter so the Bluetooth card is no longer
shown (and Bluetooth, which can't be mocked, stays out of the demo).
- Mock automation/script config and the trigger/condition platform
subscriptions so the automation editor opens.
- Mock search/related so device, entity, and area pages stop erroring.
https://claude.ai/code/session_01LuAsCpbhKpSKufH9FAHNxh
* Demo: mock remaining config WS commands found by crawling
Crawled deep into every config panel (lists, detail pages, editors, and
dialogs) and mocked the WebSocket/REST commands that were still missing:
- auth/sign_path (log download)
- frontend/get_system_data
- config/entity_registry/get_entries (voice assistant expose)
- device_automation trigger/condition/action list + capabilities (device pages)
- validate_config (wired the existing config stub)
- cloud/alexa/entities and cloud/google_assistant/entities (expose)
- config/scene/config REST endpoint (scene editor)
https://claude.ai/code/session_01LuAsCpbhKpSKufH9FAHNxh
* Demo: code-split config panel mocks into a lazy chunk
The config panel mock data is no longer bundled into the demo's main entry
chunk. A loader is registered eagerly at startup and dynamically imports the
config mocks the first time a config-only WS/REST command is requested (i.e.
when the config panel is opened).
- Add mockLazyLoad(shouldLoad, loader) to the mock connection. On an unmocked
command/path matching the predicate it awaits the loader (once) and retries,
so there is no race between panel data fetches and mock registration.
- Move the config-only mocks behind stubs/config-panel.ts, imported lazily.
- Keep manifest/list eager since it is consumed app-wide via the manifests
context and would otherwise pull in the chunk on the regular dashboard.
https://claude.ai/code/session_01LuAsCpbhKpSKufH9FAHNxh
---------
Co-authored-by: Claude <noreply@anthropic.com>
Forward the color option from StateLabelBadgeConfig through to the
underlying entity badge so legacy state-label-badges can pick a color
and pick up state-based icon coloring like hui-entity-badge.
Co-authored-by: Claude <noreply@anthropic.com>
Reverse import/export on grid neutrality gauge
Swap so that export is on the left, and import is on the right. This matches the orientation of the grid energy balance card, and means export is the negative side which visually makes more sense.
* Preserve unchanged entity display entries across registry updates
* Compare all display fields (integration reload can change source-defined ones)
* Use a generic preserveUnchangedRecord helper with deepEqual
* Add deterministic fixtures and characterization tests for chart data processing
* Extract statistics chart data processing into a pure function
* Extract state history line chart data processing into a pure function
* Add benchmark suite for chart data processing
* Add chart data optimization playbook
* Point agent instructions at the chart optimization playbook
The area page resolved device and entity display names by writing them
back onto the registry entries returned by the (memoized) memberships,
which are the shared objects from hass.devices and the entity registry.
That overwrote each entry's raw name during render and leaked app-wide: an
unnamed device would get name set to the localized "Unnamed device", which
then short-circuits the entity-derived fallback elsewhere, and a device or
entity with no user name would have its name field corrupted in the
settings dialog.
Compute the display names on shallow copies and sort/render those instead,
leaving the shared registry objects untouched. The area page renders the
same names and order as before.
* Pass the integration domain to the flow form context
In options flows the flow handler is the config entry id, not the
integration domain. Expose the resolved domain (falling back to the
handler for config flows) on the flow form context so selectors can rely
on the actual domain regardless of the flow type.
* Use the flow domain for serial port recommendations
The serial port selector marked a port as recommended by comparing
matching integrations against `context.handler`. In options flows the
handler is the config entry id, so an integration's own ports were
classified as "not recommended" and labelled "Used by <integration>".
Prefer `context.domain` (with a fallback to `handler`) so the
integration's own ports stay recommended in options flows too.
* Drop the handler fallback for the serial port selector domain
`step-flow-form` is the only context producer that sets `handler`, and it
now always sets `domain` alongside it, so the fallback never resolves to a
different value. In options flows `handler` is the config entry id anyway,
which would be the wrong value to fall back to.
The member entity list in a group entity's more-info dialog always
prefixed each tile with the device name, even when every member belongs to
the same device (for example WLED segments under a single device), where it
adds no information.
Mirror the existing area_name handling: omit the device name when all
members share the same device, and fall back to the entity name so each
tile still has a label.
The createTimezoneListEl helper in src/components/timezone-datalist.ts has
no references anywhere in the codebase. The google-timezones-json
dependency it used is still imported by other modules, so it is kept.
The default-exported scrollToTarget function in
src/common/dom/scroll-to-target.ts (a legacy copy from
paper-scroll-header-panel) has no references anywhere in the codebase.
Remove unused replaceTileLayer and LeafletDrawModuleType
Neither the replaceTileLayer helper nor the LeafletDrawModuleType type in
setup-leaflet-map.ts is referenced anywhere in the codebase.
The copy and restore icon buttons next to the entity ID field in the
entity settings dialog had no accessible name. Add descriptive labels
using two new translation keys.
Icon-only ha-icon-buttons have no accessible name, so screen readers
announce nothing. Add labels (using existing translation keys) to the
conversation trigger's add/remove sentence buttons and the integration
documentation buttons in the trigger and condition platform editors.
The automations picker data builder resolved each automation's entity
registry entry by scanning the whole entity registry with find, and each
label by scanning the label registry, making both lookups scale with the
registry size for every row.
Build entity_id and label_id lookup Maps once and use them, so each row's
lookups are O(1). Behavior is unchanged.
A config flow field guarded by vol.In([1, 5, 6]) sends numeric option
values, but the underlying select returns the chosen value as a string, and
ha-form-select forwarded that string unchanged. On submit the backend
validated "1" against [1, 5, 6] and rejected it as an invalid selection.
Map the selected value back to its original option in ha-form-select so the
source type is retained. String options are unaffected. The mapping is
extracted into matchSelectOptionValue and covered by unit tests.
getEntities builds the item list over every entity each time an entity
picker, the quick bar, or a target picker opens. On a large installation
that list construction did avoidable per-entity work:
- the include/exclude entity and domain filters used Array.includes,
a linear scan per entity (O(entities x filter));
- computeRTL was recomputed for every entity although it only depends on
the language and translation metadata;
- domainToName (a localize lookup) was called for every entity even though
domains repeat heavily across entities.
Use Sets for the filters, hoist computeRTL out of the map, and cache the
domain name per domain. This speeds up opening pickers on large installs.
Behavior is unchanged. Add tests for getEntities, which had none.
computeCssVariable built its nested var() fallback chain with
props.reverse(), which reverses the caller's array in place. Every current
caller passes a freshly built array so there is no visible corruption
today, but mutating an input parameter is a latent trap: a shared,
memoized, or frozen array would produce wrong output or throw.
Use reduceRight to build the same chain from last to first without
mutating or copying the array. Output is unchanged. Add tests for
computeCssVariable (including a no-mutation guarantee) and computeCssValue,
which previously had no coverage.
The devices dashboard data builder looked up each device label by scanning
the label registry with find, making it O(devices x labels x labelReg).
Build a label_id to entry Map once and use it for the lookups. Behavior is
unchanged.
While the helpers configuration table is open, willUpdate recomputes the
helper entity list on every state change by filtering all states. The
membership test used Array.includes against the entity-source keys, a
linear scan run for every state, making the filter O(states x sources)
per state update.
Build a Set of the entity-source ids once and use Set.has for O(1)
lookups, dropping the filter to O(states + sources). Behavior is
unchanged.
When createLogMessage received an error whose stack stacktrace-js could
not parse (for example a DOMException such as "AbortError: Transition was
skipped"), fromError threw "Cannot parse given Error object". That throw
escaped createLogMessage, so the global unhandledrejection handler logged
"Failure writing unhandled promise rejection to system log" and the
original error was never recorded.
Wrap the stacktrace extraction in a try/catch and fall back to the raw
error stack (or the provided stack fallback) so the logger stays robust
and still records the original error.
The zone condition summary joins multiple entities with "or", so "If A or B
are in zone X" is grammatically wrong in English; "or" takes singular
agreement: "If A or B is in zone X".
Make both plural branches render "is" while keeping the numberOfEntities
placeholder, so it stays visible in the source language and translators can
keep a real plural where their grammar needs it.
The numeric state trigger summary joins multiple entities with "or" but
switched the English verb to plural, reading "If A or B are above X". With
"or" the trigger fires when any one entity crosses the threshold, so English
uses singular agreement: "If A or B is above X".
Make both plural branches render "is" in the three English numeric_state
trigger description strings. The numberOfEntities placeholder is kept (not
replaced by a static "is") so it stays visible in the source language and
translators can keep using plural agreement where their grammar needs it.
The numeric_state condition is unchanged: it joins entities with "and" and
requires all to match.
While the entities configuration table is open, willUpdate rebuilt the
list of entities without a unique id on every hass update. Because each
state update produces a new states object, the oldHass.states !==
this.hass.states guard was always true, so on every state tick the panel
allocated a Set over all registry entities, iterated every state, and built
StateEntity objects, then discarded the result unless a non-registry entity
had actually been added.
Detect a newly added entity up front and only enter the rebuild when the
set of entity ids could have changed (or a registry, entity-sources, or
exposed-entities dependency changed). A plain state value change on an
existing entity can no longer trigger the rebuild. Behavior is unchanged:
the inner assignment already only ran when an entity was added.
computeStateToPartsFromEntityAttributes runs for essentially every entity
state that is rendered, but it rebuilt several constants on every call: a
3-element domain array, a 15-element timestamp-domain array (both only used
for an includes() check), and the monetary part-type map.
Move these to module-level Set/object constants (matching the existing
STATE_COLORED_DOMAIN pattern in state_color.ts) and use Set.has(). Behavior
is unchanged; the domain lists are now named and documented.
formatNumberToParts, the engine behind formatNumber, built a new
Intl.NumberFormat on every call. It is invoked for essentially every
numeric state render, and constructing an Intl.NumberFormat is
comparatively expensive.
Cache the formatters in a Map keyed by (locale, options). Unlike the
single-entry memoizeOne used for the date formatters, the number format
options are derived per value, so a multi-entry cache is needed to avoid
thrashing. The number of distinct combinations is small and bounded in
practice. Output is unchanged.
select() looked up each id with a find over all filtered rows and checked
membership with includes on the growing checked-rows array, making a large
batch selection O(rows x ids) plus O(ids squared).
Build a row lookup Map once and track membership with a Set, dropping the
batch selection to O(rows + ids). Behavior is unchanged.
When a voice was required and no value was set, the picker displayed the
first voice in the dropdown but kept its own value undefined and never
fired a value-changed event. As a result, the parent (for example the TTS
test card in the media browser) never learned the voice: the selected
voice id footer stayed hidden and no voice was sent on synthesis. This was
most noticeable for languages with a single available voice, where the
selection could not be changed to force an event.
Auto-select and emit the first voice when one is required and the current
value is missing or no longer valid for the loaded voices, so the value
matches what the dropdown shows. Non-required usages keep clearing the
value as before.
The reorder handle lives in the mwc list item meta slot, which is a fixed
24px tall box. The handle's vertical padding of 16px made the icon 56px
tall, overflowing that box and pushing the drag icon visually below the
row center.
Remove the vertical padding so the 24px icon fits the 24px meta slot and
stays centered. The horizontal padding is kept unchanged.
When the async Clipboard API is unavailable or rejects, copyToClipboard
falls back to a hidden textarea and execCommand. It appended that textarea
to deepActiveElement()?.getRootNode(). When the deepest active element
lives in the light DOM, getRootNode() returns the Document node, and
appending an element to a Document throws HierarchyRequestError (only the
single documentElement is allowed), so the fallback copy silently failed.
Append to document.body when the resolved root is a Document. Shadow roots
and elements keep holding the textarea directly, preserving execCommand
behavior inside dialogs that trap focus.
* Show installing status for update entities regardless of state
An install can be in progress while the update entity state is "off"
(e.g. downgrading firmware, where installed_version is newer than
latest_version). The installing check was nested inside the state ===
"on" branch, so these entities incorrectly showed "Up-to-date" instead
of "Installing (xx%)". Hoist the in-progress check above the state
branching so it always takes precedence.
* Show installing icon for update entities regardless of state
When an install is in progress (e.g. downgrading firmware), the entity
state can be "off" while installing. Show the installing icon
(mdi:package-down) whenever an install is in progress, before the
state-based icon selection.
Note: for entities with a brand entity_picture (e.g. Zigbee/ZHA
firmware updates), state-badge hides the icon in favour of the picture,
so this only affects update entities without a brand image.
Add the Matter symbol as a bundled custom icon, available as
mdi:matter. Like the existing ESPHome and Music Assistant logos, the
SVG path is lazy-loaded and rendered with the foreground color.
The backend defaults network_only to True, which causes BLE
commissioning to be skipped even when BLE proxies are connected. Passing
false lets the Matter server use BLE when proxies are available while
falling back to network commissioning when they are not.
It is only set to false for the following path:
Settings -> Matter -> Options -> Add manually
Replace the per-point grouping (a Map of arrays of {point, x, y} wrapper
objects, plus a second pass that re-walks each frame and, in mean mode,
two reduce() passes) with a single fixed accumulator per frame:
- mean mode keeps running sumX/sumY/count in arrival order, so the
floating-point summation order is unchanged
- min/max mode tracks the min and max point incrementally with the same
strict comparisons (first occurrence wins on ties) and the same
min-before-max emit ordering
Each point is touched once; ~100k transient objects per large payload are
eliminated. Output is bit-identical. Speedups range from ~1.2x (mid-size)
to ~1.5-1.8x (100k-point) payloads, with substantially lower run-to-run
variance from reduced GC pressure. Payloads below maxDetails still
early-return unchanged.
* Add DirtyStateProviderMixin to dialogs (areas, backup, energy, helpers, person, voice assistant)
Migrate dialogs to DirtyStateProviderMixin for dirty state tracking via
Lit context. These dialogs do not use PreventUnsavedMixin and are fully
independent of the PreventUnsavedMixin contract change.
* Add disabled state
* Fix disable state
* Add check
* Migrate second set of dashboard dialogs to dirty state provider
* Fix create dialog
* Add DirtyStateProviderMixin to Lovelace raw config editor
* Fix yaml mode check from review
* Fix
Pre-validate the credential on camera_proxy, camera_proxy_stream and
image_proxy URLs before letting them hit core. Requests with a missing
or "undefined" token, or with an authSig JWT whose exp has passed, are
short-circuited to a synthetic 401 and never reach the server.
This silences spurious "Login attempt or request with invalid
authentication" warnings from homeassistant.components.http.ban that
fire when the browser replays a stale <img src> after BFCache restore,
tab resume, or a network change. The signed-path TTL is short (30s by
default) and image elements happily hold onto the URL long after that.
Limitations: service workers only run on secure contexts, so this does
not help users on plain http LAN access. A core-side fix to ban.py
that distinguishes expired-but-validly-signed paths from real login
attempts remains the principled fix and covers all clients.
Pre-validate the credential on camera_proxy, camera_proxy_stream and
image_proxy URLs before letting them hit core. Requests with a missing
or "undefined" token, or with an authSig JWT whose exp has passed, are
short-circuited to a synthetic 401 and never reach the server.
This silences spurious "Login attempt or request with invalid
authentication" warnings from homeassistant.components.http.ban that
fire when the browser replays a stale <img src> after BFCache restore,
tab resume, or a network change. The signed-path TTL is short (30s by
default) and image elements happily hold onto the URL long after that.
Limitations: service workers only run on secure contexts, so this does
not help users on plain http LAN access. A core-side fix to ban.py
that distinguishes expired-but-validly-signed paths from real login
attempts remains the principled fix and covers all clients.
* Add DirtyStateProviderMixin to dialogs (config entry, credentials, supervisor apps, voice assistant, zones)
Migrate dialogs to DirtyStateProviderMixin for dirty state tracking via
Lit context. These dialogs do not use PreventUnsavedMixin and are fully
independent of the PreventUnsavedMixin contract change.
* Fix disabled state
* Fix disabled state
* Drop voice assistant pipeline dialog changes (handled in dialogs-b)
* Remove isDirty from PreventUnsavedMixin, migrate to DirtyStateProviderMixin
Drop the legacy isDirty getter from PreventUnsavedMixin and switch to
isDirtyState (provided by DirtyStateProviderMixin). All consumers that
previously overrode isDirty are migrated to use DirtyStateProviderMixin
with proper dirty tracking:
- AutomationScriptEditorMixin: deep config comparison
- ha-scene-editor: revision counter with shallow comparison
- Blueprint editors + manual editor mixin: consume dirty context
This is the atomic core change — all isDirty overriders are migrated in
the same commit so no compatibility layer is needed.
* Fix dirty state not updated when YAML editor has invalid content
- ha-scene-editor: call _updateDirtyState on invalid YAML to increment
the revision counter, marking the editor dirty
- ha-script-editor, ha-automation-editor: override isDirtyState to also
return true when yamlErrors is set, ensuring the PreventUnsavedMixin
navigation guard fires even when the user has never produced a valid
intermediate config change
* FIx/migrate editor-toast
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* Migrate vaccum segment mapping to dirty state provider
* Use typed value-changed event in vacuum mapping view
* Use consumer key for vaccum segment mapping instead of new provider
* Completeness
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* Show entity area and device in scene editor entity lists
Entities already added to a scene only displayed their friendly name, so
several similarly named entities (e.g. multiple lights named LED) were
indistinguishable. Add a secondary line with the entity's area and device,
reusing computeEntityPickerDisplay so it matches the add-entity picker.
Applies to both the device-grouped and standalone entity lists.
* Add type and integration context to scene editor entity rows
Mirror the pickers used to add scene members: device-grouped entities show
their integration (e.g. Matter), like the device picker, while standalone
entities show their domain (e.g. Light), like the entity picker. Combined with
the area and device on the second line, this keeps entities distinguishable
once they are part of the scene.
---------
Co-authored-by: Przemysław Szypowicz <2733699+pszypowicz@users.noreply.github.com>
* Dedicated gallery AGENTS.md
* Dont auto open the dev server for gallery
* Refactor and theme gallery
* Add icons
* Better positioning of icons
* Reorganise sidebar
* Remove extra title
* Remove header/toolbar height override
* Add some global spacing for content
* Show flipped theme mode for comparisions
* Remove unnesassary headings
* Fix eslint webpack resolution path for gallery vscode import errors
* Scroll item into view
* Fix theme variables
* Fix theme when system theme is dark and set to light
* Review
* Review
* Review
* Review
* Add mock
* Fix buttons
* Dirty state context provider
* Shallow state (new)
* Deep state (existing)
* Fix loop
* remove cast
* Move dirty state provider to dialog build level using deferred state
* Prevent scrim closure on category dirty state
* Discard dirty state on view change
* Move more info outside cache
* Refactor to allow multiple keys in dirty state, use default if not provided
* Fix child view rendering
* Deep clone to avoid mutations
* Fix state timing
The state selector previously always defaulted its inner label to the
generic "State". When an attribute is configured (e.g. the source field
of media_player.select_source), default to the attribute's friendly name
instead, falling back to "State" when no concrete entity is available to
resolve the name.
Co-authored-by: Claude <noreply@anthropic.com>
Fix wrong scroll target in virtualized list setActiveItemIndex
When the requested row is outside the rendered range, setActiveItemIndex
scrolled to the raw index argument instead of the clamped/remapped
this.activeItemIndex it had just computed. If the requested index was out
of bounds or pointed at a non-focusable row (remapped to the first
focusable row), it scrolled to the wrong element (or none), so the active
row was never brought into view and keyboard focus was lost.
Use this.activeItemIndex, matching the other two element() call sites.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Fix missing domain on brand integration list items
When listing the integrations belonging to a brand in the add
integration dialog, the list item was missing the .domain property. The
click handler reads the domain from ev.currentTarget.domain, so it came
through as undefined and was sent to the backend as the config flow
handler, producing "required key not provided @ data['integration']".
This restores the .domain binding that was accidentally dropped in #52354.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Cap size-adjuster height in ha-textarea resize=auto
When resize="auto", Web Awesome's textarea base is a CSS grid where the
real textarea and the size-adjuster share one cell, and both receive an
inline height equal to the content scrollHeight. We only capped the
textarea's max-height, so with long content the size-adjuster inflated
the grid row and the centered textarea was pushed down instead of
scrolling. Apply the same max-height cap to the textarea-adjuster part.
* Add capped autogrow demo to ha-textarea gallery page
Demonstrates resize="auto" with content that overflows the max-height,
so the textarea scrolls instead of growing unbounded. Serves as a visual
regression guard for the size-adjuster grid-row fix.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* No animation for runnig state
* Remove state-dot-pulse animation and media query
Removed unused keyframes and media query for animation.
---------
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
* Use space tokens
* Fix overflow issue on entities
* Enforce narrow mode on target selectors
* Add action to full width selectors
* Function signature typing
Update AI coding instructions to match current frontend APIs
Fix outdated references in .github/copilot-instructions.md (symlinked as
CLAUDE.md and AGENTS.md): showAlertDialog import path, ESLint flat config,
ha-dialog width presets, ha-alert slots/props, ha-button variant/appearance
guidance, Web Awesome direction, and the querySelector pitfall.
* Simplify and improve landingpage
* add core download progress
* reduce to 2 seconds
* Use round to display full integer as progress percentage
* Use find to get the job object
* Don't show progress label when progress is at 0
Before download starts, progress is at 0. At this point we may trying
to reach a server (and error out), so we aren't really in downloading
phase just yet. Simply treat 0 as "not started" and hide the progress
label until we have a real progress value.
---------
Co-authored-by: Stefan Agner <stefan@agner.ch>
* Simplify and improve landingpage
* add core download progress
* reduce to 2 seconds
* Use round to display full integer as progress percentage
* Use find to get the job object
* Don't show progress label when progress is at 0
Before download starts, progress is at 0. At this point we may trying
to reach a server (and error out), so we aren't really in downloading
phase just yet. Simply treat 0 as "not started" and hide the progress
label until we have a real progress value.
---------
Co-authored-by: Stefan Agner <stefan@agner.ch>
Filter out updates that are already installing before calling the
update.install service, and disable a group's Update all button when
every update in it is already in progress.
* Create shared related context for quick bar and add automation element
* Provider mixin and dont pass sets when context can be used
* Direct use consume
* Move context provider into class and use in context mixin
* Cleanup
* Match signatures with lazy context provider
* Fix order of operations
* Typing
* Fix popstate on dialog launch/close
* Typing improvement
Drop dependencies that are no longer referenced anywhere in the codebase:
- @material/mwc-base: declared directly but never imported; still pulled
in transitively by the other @material/mwc-* packages, so the explicit
declaration was redundant.
- @types/mocha: the test runner is Vitest (test files import describe/it/
expect from "vitest"); no Mocha globals or namespace are used.
- @types/webspeechapi: no Web Speech API usage in the codebase, and the
modern TypeScript lib.dom already ships these definitions.
- @types/babel__plugin-transform-runtime: the plugin is only referenced by
string name in the Babel config, never imported as a typed module, so the
type stub is unused.
Co-authored-by: MindFreeze <noreply@anthropic.com>
Consume internationalizationContext for locale in ha-selector-date,
ha-selector-datetime, ha-selector-time, ha-selector-button-toggle, and
ha-selector-select (partial: hass kept where passed to children).
* Migrate hui-badge-edit-mode and hui-card-edit-mode to localize context
Replace this.hass.localize with @consumeLocalize(); remove hass prop and
caller .hass bindings from Lovelace edit-mode overlays.
* Migrate hui-view-badges to localize context
* Migrate avatar badges off hass property (Group K)
Consume connectionContext for hassUrl and statesContext plus
consumeEntityState for the user badge person entity picture.
Remove .hass bindings from callers.
* Migrate ha-person-badge and ha-user-badge to connection context
Use connectionContext for hassUrl; ha-user-badge also consumes entity state
for person picture lookup.
* Fix ha-user-badge updates and restore hass binding
Use PropertyValues.has() to detect _states changes. Restore .hass bindings on
ha-user-picker callers and ha-generic-picker, which still require hass.
* Fix type error and restore hass on person subpage
- Type willUpdate with PropertyValues so changedProps.has("_states")
type-checks (matches the other states-context consumers); fixes the
failing lint:types CI check
- Restore .hass on hass-tabs-subpage in ha-config-person, which still
requires hass and was crashing the person config page
- Drop now-dead .hass bindings on ha-user-badge in ha-user-picker
- Only rescan for the person entity when the user changes or the tracked
entity is missing, instead of on every state update
Order the energy fields as discharge then charge to match the power
fields in the energy panel battery settings dialog.
Co-authored-by: MindFreeze <noreply@anthropic.com>
* Render echarts tooltips with Lit templates
Replace raw HTML string interpolation in echarts tooltip formatters with Lit templates so user-controlled fields (entity friendly_name, device names, node labels) are auto-escaped instead of relying on per-string filterXSS. ha-chart-base now wraps any function tooltip.formatter into a stable per-formatter container and handles Lit TemplateResult / nothing / null returns; the public HaECOption type lets charts express Lit-returning formatters without per-callsite casts.
* Simplify
* Refactor _getSeries
* Small fix
* Fix merge mistake
* Marker component and wrapper test
* Migrate ha-relative-time and ha-absolute-time off hass property
Consume localize, locale, and config via Lit context so time primitives
only rerender when i18n or config slices change, and drop obsolete .hass
bindings from callers.
* Consume full i18n context in time display components
Use internationalizationContext directly for both localize and locale in
ha-relative-time and ha-absolute-time, avoiding mixed consumption patterns.
* Render echarts tooltips with Lit templates
Replace raw HTML string interpolation in echarts tooltip formatters with Lit templates so user-controlled fields (entity friendly_name, device names, node labels) are auto-escaped instead of relying on per-string filterXSS. ha-chart-base now wraps any function tooltip.formatter into a stable per-formatter container and handles Lit TemplateResult / nothing / null returns; the public HaECOption type lets charts express Lit-returning formatters without per-callsite casts.
* Simplify
* Refactor _getSeries
* Small fix
* Fix merge mistake
* Marker component and wrapper test
Resending the confirmation email reused the registration code path, so
the flash on the login screen said "Account created!" even though no
new account was created. Pass a message key to _verificationEmailSent
so resend can show "Verification email sent." instead.
_handleResendVerifyEmail also never set _requestInProgress, so the
resend button (and the start-trial button, which share that flag) were
not disabled while a resend was in flight and could be clicked
repeatedly. Set the flag at the start and clear it on terminal errors;
_verificationEmailSent already clears it on success.
Co-authored-by: Claude <noreply@anthropic.com>
* Update dependency echarts to v6.1.0
* Fix axis-proxy patch for echarts 6.1.0 AxisProxy internals
ECharts 6.1.0 uses hostedBy() and _window.value instead of direct model
comparison and _valueWindow. Update the boundaryFilter patch and contract
tests so CI passes with the dependency bump.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Fix stale wake word display after wake word change in satellite wizard
The config re-fetch was fire-and-forgotten, so the step transition to
STEP.WAKEWORD raced ahead with stale assistConfiguration. Awaiting the
fetch ensures the fresh active_wake_words are in place before rendering.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add-on iframe: delegate microphone + camera Permissions Policy
The add-on ingress iframe in ``ha-panel-app.ts`` ships without an
``allow=`` attribute, so the Permissions Policy default of *deny*
applies for ``microphone`` and ``camera`` on the cross-origin
iframe. An add-on that wants to call ``getUserMedia`` — voice
notes, dictation, video calls, photo capture — fails silently with
``NotAllowedError`` before the browser even surfaces the permission
prompt.
The failure is most visible on the Android Companion app, where
there's no "open in a new tab" escape: the user presses the mic
button and nothing happens, no toast, no logs.
Delegate ``microphone``, ``camera``, and ``clipboard-write`` to the
add-on iframe. Add-ons are first-party software the user explicitly
installs, and Chrome's runtime permission prompt still gates the
hardware access — the ``allow=`` attribute just lets the iframe
*request* the prompt instead of being blocked at the policy layer.
``clipboard-write`` is bundled in because the next-most-frequent
silent-fail in add-on land is ``navigator.clipboard.writeText`` for
"copy link" / "copy code" affordances, blocked by the same
mechanism.
* Sandbox add-on ingress iframe without allow-same-origin
Split IFRAME_SANDBOX into two constants: IFRAME_SANDBOX (without
allow-same-origin) for add-on ingress iframes that need origin
isolation, and IFRAME_SANDBOX_SAME_ORIGIN for external iframes
that need same-origin access.
This ensures add-on iframes can't inherit camera/microphone
permissions already granted to the Home Assistant origin, and
prevents same-origin iframes from removing their own sandbox.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Setup add to area page
* Remove 3 buttons, move to single add to button next to add a picture button
* Use normal size buttons
* Restructure layout with picture
* Remove div when both conditions are met
* Use mixin
* Fix imports
Some environments (e.g. Android WebView/emulator) return a UTC offset like
"+00:00" from Intl.DateTimeFormat().resolvedOptions().timeZone instead of an
IANA zone name. Submitting that to saveCoreConfig fails with "invalid time
zone", leaving users stuck on the country step.
Detect this by checking the resolved value against the google-timezones-json
list used by ha-timezone-picker, and surface the picker on the core-config
step when no IANA zone could be detected from the browser or the location
detect API.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add to for devices page
* Rename and reuse original dialogs, drop popover
* Reduce
* Lazy context
* Direct access lazy context
* Default width
* Merge automations and scripts cards
* Format
* Loading state
* Rename key
* Tooltip and move key
* Copy icons used in more info
* Sort
* Merge scenes into one "Related" card
* Adjust
* Fix no labs
* Use same wording for device actions
* Cleanup
* Comments for removal
* Cleanup
* Type check
* Template literals
* Add padding
* Rework weather forecast card features
* Add show labels option
* Some fixes
* Fixes and cleaning
* Update palette
* Add reference floor to precipitation bar scale
Light drizzle no longer fills the bar when it's also the period max.
Observed values above the floor still drive the scale (storms read full).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Feedbacks
* Use weather unit
* Force celcius for gradient
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Allow previews to use a domain
* Allow previews to specify preview entity domain
* Allow repair_flow to use previews
* Pull recent changes
* Add domain to previews for TemplatePreview
* first rough draft of Z-Wave credential mangement
* separate user and credentials, error handling, dialog tweaks
* align with upstream API changes, improve error handling
* align more with Matter, use lock entity for services
* remove get_credential_status service
* address review feedback, clarify user types
* user_index -> user_id, fix some pending states
* address review feedback
* clean up unused code, strongly type credential types
* Clear -> Delete, drop icons
* Simplify flow to 1 PIN/Password credential per user
* cleanup, comments, etc.
* address review feedback
* do not show existing credential data
* fix lint errors after branch update
* ignore non-enterable credential types when editing user
* refactor: use separate storage and display filters in backup page
Apply the two-lists pattern in backup page: _filters (@state, display only) +
_storageFilters (@storage sessionStorage, state: false). _storageFilters
is only updated when not in URL mode (_fromUrl flag). Init moved from
connectedCallback to willUpdate(!hasUpdated).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: use separate storage and display filters in scenes page
Apply the two-lists pattern in scenes page: _filters (@state, display only) +
_storageFilters (@storage sessionStorage, state: false, with
serializer/deserializer). _storageFilters is only updated when not in
URL mode (_fromUrl flag). Init moved from firstUpdated to
willUpdate(!hasUpdated). The existing updated() hook already calls
_applyFilters() when _entityReg changes, covering the reconnect case.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: use separate storage and display filters in automations page
Apply the two-lists pattern in automations page: _filters (@state, display only) +
_storageFilters (@storage sessionStorage, state: false, with
serializer/deserializer). _storageFilters is only updated when not in
URL mode (_fromUrl flag). _fromUrl is set before the await in the async
_filterBlueprint() to prevent any user change during the fetch from
persisting. Init moved from firstUpdated to willUpdate(!hasUpdated).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: use separate storage and display filters in scripts page
Apply the two-lists pattern in scripts page: _filters (@state, display only) +
_storageFilters (@storage sessionStorage, state: false, with
serializer/deserializer). _storageFilters is only updated when not in
URL mode (_fromUrl flag). _fromUrl is set before the await in the async
_filterBlueprint() to prevent any user change during the fetch from
persisting. Init moved from firstUpdated to willUpdate(!hasUpdated).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: don't mix URL filters with storage filters in automation,script and scene pages
When URL params are present, _filters starts empty so URL methods build
from scratch. Previously, _filters was pre-populated from _storageFilters
and the spread in _filterLabel()/_filterBlueprint() would merge storage
filters into the URL-injected ones.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update src/panels/config/backup/ha-config-backup-backups.ts
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
* fix(helpers): clear URL-injected filters on leaving helpers dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(helpers): restore previous filters after URL-injected navigation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: use separate storage and display filters
Apply the same pattern as devices and entities pages: split _filters into
a display-only @state and a _storageFilters persisted to sessionStorage.
_storageFilters is only updated when not in URL mode (_fromUrl flag), so
URL-injected filters never persist to storage.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: reapply filters when helper entities load on reconnect
_applyFilters() was never called when _filters was restored from
sessionStorage, leaving _filteredHelperEntityIds undefined and the
table appearing empty. Call it whenever _helperEntities updates and
active filters are present.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Setup default add to actions
* Setup default add to actions
* Move event into external only
* Split into sections
* Padding
* Refactor to single type and adapt app interface to frontend style and vice versa
* Refactor to single type and adapt app interface to frontend style and vice versa
* Condition action and navigation actions
* Open dialogs with trigger, condition, action dialogs
* Add divider before add to
* Move add to to the top
* Action
* Triggers and conditions labs feature check
* Suggestion
* Keep query state
* Change to automation_trigger
* Use typed key instead of finding with icon
* Apply suggestions from code review
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
* Finish
* Reset state
* Fix navigation resets
* stated
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
* Split
* Add import, sort imports
---------
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
Fix demo mock recorder data end times
The mock recorder was setting the start and end time for each of the samples to be the same value, causing the solar graph in the energy dashboard to render incorrectly.
Fix the recorder to set the end time of each sample to the start time of the next.
Fix empty padding to right of ha-switch
When the label slot for the ha-switch is empty, the initial margin is still present which causes an odd misalignment on the switches in e,g, the entities card.
To fix this, if the label slot is empty, hide the label to remove the unwanted margin.
* Add descriptions to Jinja2 tags, filters, expressions, tests and variables
All standard Jinja2 tags, filters, and expression completions now carry
info and detail strings so the autocomplete info popover shows meaningful
documentation when users browse them — not just HA-specific functions.
* Add keyboard shortcut tip to the template developer tool
A ha-tip below the editor card now shows users that Ctrl+Space triggers
autocomplete, Ctrl+F opens the search panel, and F11 toggles fullscreen,
making the editor's built-in features more discoverable.
* Add hover tooltips for Jinja2 functions, filters and expressions
Hovering over a function, filter, tag, test, or variable name inside a
Jinja2 template shows a tooltip with its signature and description.
Non-tag completions also get a help-circle icon linking to the
corresponding Home Assistant template-functions documentation page.
The tooltip is rendered as a custom Lit element (ha-code-editor-jinja-hover)
that takes the Completion object and an optional docUrl as properties.
The tooltip source (haJinjaHoverSource) is wired into ha-code-editor
via CodeMirror's hoverTooltip extension. The documentationUrl() helper
is used so the link points to the correct subdomain (www / rc / next)
based on the running HA version.
* Add hover tooltips for Jinja2 hover + arg value tooltips for entity/device/area
Wire haJinjaHoverSource into ha-code-editor via CodeMirror hoverTooltip.
Two types of hover are now shown in jinja2/yaml mode:
- Hovering a function/filter/tag/expression name shows its signature,
description, and a doc-link icon (non-tags only).
- Hovering a string-literal argument of a known HA Jinja function (e.g.
states(), device_name(), area_entities()) shows the friendly name,
current state, device, and area for entity_id arguments; the device
name and area for device_id arguments; and the area name for area_id
arguments. The same applies to states["entity_id"] subscripts.
The arg-value tooltip reuses CompletionItem / ha-code-editor-completion-items
(the same component used for autocomplete info popovers) via a new
ha-code-editor-jinja-arg-hover element. HA registry data is passed from
ha-code-editor via a HassArgHoverContext interface to keep jinja_ha_completions.ts
free of HomeAssistant type imports.
* only add tip for autocomplete
* review
* Expose Z-Wave exclusion instructions when removing device
* text tweaks
* Apply suggestion from @MindFreeze
* Apply suggestions from code review
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* bring back comment
---------
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
description: Home Assistant frontend component patterns. Use when implementing or reviewing dialogs, ha-form, ha-alert, keyboard shortcuts, tooltips, panels, Lovelace cards, or ha-button usage.
---
# HA Frontend Components
Use this skill when creating or reviewing Home Assistant UI components and common interaction patterns.
## Dialogs
Open dialogs through the fire-event pattern:
```ts
fireEvent(this,"show-dialog",{
dialogTag:"dialog-example",
dialogImport:()=>import("./dialog-example"),
dialogParams:{title:"Example",data: someData},
});
```
Dialog implementation requirements:
- Use `ha-dialog`.
- Implement `HassDialog<T>`.
- Use `@state() private _open = false` to control visibility.
- Set `_open = true` in `showDialog()` and `_open = false` in `closeDialog()`.
- Return `nothing` while required params are absent.
- Fire `dialog-closed` in the close handler.
- Use `header-title` and `header-subtitle` for simple header text.
- Use slots when standard header attributes are not enough.
- Use `ha-dialog-footer` with `primaryAction` and `secondaryAction` slots.
- Add `autofocus` to the first focusable element, such as `<ha-form autofocus>`, and forward it internally if needed.
Use standard dialog widths: `small`, `medium`, `large`, or `full`. Avoid custom dialog sizing unless there is a clear product need.
## Buttons
`ha-button` wraps the Web Awesome button in `src/components/ha-button.ts`.
- Use `appearance="filled"` for primary emphasis when needed.
- Use `appearance="plain"` for cancel and dismiss actions.
- Use `variant="danger"` for destructive actions.
- Place primary actions in `slot="primaryAction"` and secondary actions in `slot="secondaryAction"`.
## Forms
`ha-form` is schema-driven with `HaFormSchema[]` and supports common selectors for entities, devices, areas, targets, numbers, booleans, time, actions, text, objects, selects, icons, media, and location.
Use `computeLabel`, `computeError`, and `computeHelper` for translated labels, validation, and helper text.
Use `ShortcutManager` from `src/common/keyboard/shortcuts.ts` for keyboard shortcuts. It blocks shortcuts in input fields, can prevent shortcuts during text selection, and supports character and KeyCode shortcuts for non-latin keyboards. See `src/state/quick-bar-mixin.ts` for global shortcut examples.
Use `ha-tooltip` from `src/components/ha-tooltip.ts` for contextual hover help. See `src/components/ha-label.ts` for an example.
## Panels And Lovelace Cards
Panels commonly extend `SubscribeMixin(LitElement)` and receive route and narrow-layout properties.
Lovelace cards should implement `LovelaceCard`, validate config in `setConfig()`, handle loading, error, unavailable, and missing-entity states, and add a configuration editor when needed.
Cards are user-story surfaces. Support different households, entity types, responsive layouts, and accessible interaction states.
description: Home Assistant frontend Lit context and hass migration guidance. Use when adding or changing component state access, replacing hass reads, consuming entity or registry contexts, or reviewing rerender behavior.
---
# HA Frontend Contexts
Use this skill when a component reads Home Assistant state, registries, localization, services, config, UI data, connection state, or API helpers.
## Goal
Move leaf components away from the broad `hass: HomeAssistant` object. Broad `hass` access rerenders components for unrelated changes, hides the data a component depends on, and makes tests harder to mock.
Container components may keep `hass` when they own it and feed providers. Leaf components should consume the narrowest context that covers their reads.
Lazy contexts subscribe on first consumer and tear down after the last consumer: `labelsContext`, `fullEntitiesContext`, `configEntriesContext`, and `manifestsContext`.
The single-field contexts such as `localizeContext`, `themesContext`, and `userContext` are deprecated. Use grouped contexts instead.
## Consumption Patterns
Use entity-scoped helpers when the component watches an entity id held on the host:
Use `@transform` with `watch` when the transformer depends on a host property, such as a computed entity id. `consumeEntityState` only watches the first path segment.
To consume a whole group untransformed, omit `@transform` and type the field as `ContextType<typeof statesContext>` or the matching context type.
## Review Checklist
- The component consumes the narrowest context needed for the data it reads.
- A broad `hass` property is kept only when the component is a container or external API requires it.
- Entity-scoped reads use the consume helpers rather than ad hoc context transforms.
- Context fields are marked `@state()` so updates trigger rendering.
- Tests and mocks only provide the data the component actually consumes.
description: Home Assistant frontend PR and review guidance. Use when reviewing frontend changes, preparing a PR, checking recurring review issues, or applying the PR template.
---
# HA Frontend Review
Use this skill when reviewing Home Assistant frontend changes or preparing a pull request.
## Pull Request Body
When creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the body.
- Do not omit, reorder, or rewrite template sections.
- Check the appropriate "Type of change" box based on the actual change.
- Do not check checklist items on behalf of the user.
- If the PR includes UI changes, remind the user to add screenshots or a short video.
- Explain what the change does for users, not only implementation details.
- Use Markdown.
## Pre-Submission Checklist
-`yarn lint` passes when practical for the scope.
-`yarn test` or focused relevant tests are green when practical for the scope.
- Tests are added or updated for new data processing and utilities where applicable.
- User-facing text is localized and follows `ha-frontend-user-facing-text` guidance.
- Components handle loading, error, unavailable, and missing-entity states.
- Entity existence is checked before property access.
- Event listeners and subscriptions are cleaned up.
- UI is accessible to screen readers and keyboard users.
## Recurring Review Issues
User experience and accessibility:
- Forms need proper labels, helper text, and validation feedback.
- Form markup should not cause password managers to identify fields incorrectly.
- Clickable areas should be large enough for touch interaction.
- Hover, active, disabled, loading, and focus states should be clear.
Dialog and modal patterns:
- Multi-step operations should show progress.
- Dialog state should survive background operations correctly.
- Cancel and close buttons should behave consistently.
- Defaults should be helpful without blocking user override.
Component design patterns:
- Terminology should be consistent. Use words like "Join" or "Apply" instead of "Group" when that better matches the user action.
- Visual hierarchy should use appropriate font sizes, weights, and spacing ratios.
- Components should align to the design grid.
- Badges and indicators should be placed consistently.
Code quality:
- Null and undefined paths should be handled explicitly.
- Potentially undefined array and object access should be guarded.
- Event handlers, timers, observers, and subscriptions should be cleaned up.
Configuration and props:
- Make configuration fields optional when sensible.
- Provide reasonable defaults.
- Keep APIs extensible without adding speculative abstractions.
- Keep style-only comments secondary unless they affect maintainability or user experience.
- Prefer small, direct fixes over large refactors during review follow-up.
- Cross-load `ha-frontend-contexts`, `ha-frontend-components`, `ha-frontend-styling`, `ha-frontend-testing`, or `ha-frontend-user-facing-text` when a finding falls in that area.
description: Home Assistant frontend styling, theming, spacing, responsive layout, RTL, and View Transitions guidance. Use when editing CSS, layout, motion, or visual component structure.
---
# HA Frontend Styling
Use this skill when editing CSS, layout, visual hierarchy, theme integration, responsive behavior, RTL support, or view transitions.
## Theme And Layout Basics
- Use Home Assistant CSS custom properties instead of hardcoded colors.
- Use `--ha-space-*` spacing tokens instead of hardcoded spacing where possible.
- Keep components mobile-first and enhance for desktop.
- Keep layouts RTL-safe. Prefer logical properties when they fit.
- Prefer `ha-*` components and current Web Awesome wrappers.
- Avoid adding new legacy Material Web Components (`mwc-*`).
- Scope styles to the component. Do not rely on global styles for component internals.
Spacing tokens are defined in `src/resources/theme/core.globals.ts`. The scale runs from `--ha-space-1` at 4px through `--ha-space-20` at 80px in 4px increments. Common values are `--ha-space-2` at 8px, `--ha-space-4` at 16px, and `--ha-space-8` at 32px.
```ts
staticgetstyles() {
returncss`
:host {
padding: var(--ha-space-4);
color: var(--primary-text-color);
background-color: var(--card-background-color);
}
.content {
gap: var(--ha-space-2);
}
@media (max-width: 600px) {
:host {
padding: var(--ha-space-2);
}
}
`;
}
```
## Interaction States
- Make touch targets large enough for mobile.
- Provide clear hover, active, focus, disabled, loading, error, and unavailable states.
- Preserve keyboard navigation and visible focus indicators.
- Maintain WCAG AA contrast for text and essential UI affordances.
## View Transitions
Use the View Transitions API only for meaningful continuity between DOM states.
- Use `withViewTransition()` for fallback behavior.
- Keep transitions simple. Subtle fades and crossfades usually work best.
- Use `--ha-animation-duration-fast`, `--ha-animation-duration-normal`, or `--ha-animation-duration-slow` for timing.
- Ensure each `view-transition-name` is unique at any given time.
- Remember only one view transition can run at a time.
- View transitions operate at document level and do not work inside Shadow DOM style isolation. For web components, set `view-transition-name` on `:host` or use document-level transitions.
- The root gets `view-transition-name: root` by default. Target `::view-transition-group(root)` to customize the default page transition.
## Review Checklist
- Styling uses theme variables and spacing tokens where practical.
- Layout is mobile-first and RTL-safe.
- Component styles are scoped.
- Interactive states are clear and accessible.
- Motion is subtle, tokenized, and respects reduced-motion behavior through existing globals.
description: Home Assistant frontend validation workflow. Use when running lint, TypeScript checks, Vitest, Playwright e2e suites, dev servers, or chart-data benchmarks.
---
# HA Frontend Testing
Use this skill when choosing or running validation for frontend changes.
## Core Commands
```bash
yarn lint # ESLint + Prettier + TypeScript + Lit
yarn format # Auto-fix ESLint + Prettier
yarn lint:types # TypeScript compiler, run without file arguments
yarn test# Vitest
yarn dev # App dev server
yarn dev:serve # Local serving dev server
```
Never run `tsc` or `yarn lint:types` with file arguments. File arguments make `tsc` ignore `tsconfig.json` and can emit `.js` files into `src/`.
For focused type feedback on one file, use editor diagnostics instead of a file-scoped `tsc` command.
## Unit And Utility Tests
- Add or update Vitest tests for data processing, utility code, and behavior that can be tested without a browser.
- Mock WebSocket connections and API calls at boundaries.
- Cover loading, error, unavailable, and missing-entity states where relevant.
- Test accessibility-sensitive behavior when it can be asserted without brittle DOM internals.
## Dev Servers
`yarn dev` builds and watches the app, served by a running Home Assistant core configured through `development_repo`.
`yarn dev:serve` also serves locally and supports `-c` for the core URL and `-p` for the port. Default local serving port is 8124.
Dev server commands support `--background`, `--status`, `--stop`, and `--logs [--follow]`.
## Playwright E2E
Each suite has its own dev server port. Playwright reuses an existing server locally when the port is already running; otherwise it performs a slow full build. The rspack watcher recompiles on save, so reruns should not need a restart.
Start the relevant suite server, then run that suite:
Server reuse and `--stop` use the `/__ha_dev_status` health check, so starting or stopping twice is harmless.
Use `-g "<title>" --project=chromium` to narrow a run. `yarn test:e2e` runs all three suites. Run suites directly; piping through output truncation hides progress and failures.
The app suite uses a stripped-down harness for e2e. Demo and gallery use their normal dev servers.
## Benchmarks
For chart data transforms such as history, statistics, energy, and downsampling, follow `test/benchmarks/README.md`.
Use seeded fixtures, characterization snapshot tests, and `yarn test:bench` before and after optimization. Optimizations must keep output bit-identical.
## Verification Selection
- Documentation-only change: no code test required unless examples or commands changed.
- Type-only or utility change: run focused Vitest if available, then `yarn lint:types` if practical.
- Lit component change: run relevant tests plus lint or typecheck depending on scope.
- E2E-sensitive flow: start the relevant e2e dev server and run the narrow Playwright suite.
- Broad refactor: run `yarn lint` and relevant test suites when practical.
description: Home Assistant frontend copy, localization, terminology, and user-facing text guidance. Use when adding or reviewing labels, buttons, dialogs, errors, translations, or UI strings.
---
# HA Frontend User-Facing Text
Use this skill for all user-facing text, translations, labels, buttons, dialog copy, errors, helper text, and review comments about wording.
## Localization
- All user-facing text must be translatable.
- Add translation keys to `src/translations/en.json` when introducing new strings.
- Use the localization system instead of inline user-visible strings.
- Prefer complete localized strings with placeholders over concatenating translated fragments.
- Give translators enough context through key naming and placeholders.
You are an assistant helping with development of the Home Assistant frontend. The frontend is built using Lit-based Web Components and TypeScript, providing a responsive and performant interface for home automation control.
**Note**: This file contains high-level guidelines and references to implementation patterns. For detailed component documentation, API references, and usage examples, refer to the `gallery/` directory.
## Table of Contents
- [Quick Reference](#quick-reference)
- [Core Architecture](#core-architecture)
- [Development Standards](#development-standards)
- [Component Library](#component-library)
- [Common Patterns](#common-patterns)
- [Text and Copy Guidelines](#text-and-copy-guidelines)
- [Development Workflow](#development-workflow)
- [Review Guidelines](#review-guidelines)
## Quick Reference
### Essential Commands
```bash
yarn lint # ESLint + Prettier + TypeScript + Lit
yarn format # Auto-fix ESLint + Prettier
yarn lint:types # TypeScript compiler (run WITHOUT file arguments)
yarn test# Vitest
script/develop # Development server
```
> **WARNING:** Never run `tsc` or `yarn lint:types` with file arguments (e.g., `yarn lint:types src/file.ts`). When `tsc` receives file arguments, it ignores `tsconfig.json` and emits `.js` files into `src/`, polluting the codebase. Always run `yarn lint:types` without arguments. For individual file type checking, rely on IDE diagnostics. If `.js` files are accidentally generated, clean up with `git clean -fd src/`.
1. Always use `withViewTransition()` wrapper for automatic fallback
2. Keep transitions simple (subtle crossfades and fades work best)
3. Use `--ha-animation-duration-*` CSS variables for consistent timing (`fast`, `normal`, `slow`)
4. Assign unique `view-transition-name` to elements (must be unique at any given time)
5. For Lit components: Override `performUpdate()` or use `::part()` for internal elements
**Default Root Transition:**
By default, `:root` receives `view-transition-name: root`, creating a full-page crossfade. Target with [`::view-transition-group(root)`](https://developer.mozilla.org/en-US/docs/Web/CSS/::view-transition-group) to customize the default page transition.
**Important Constraints:**
- Each `view-transition-name` must be unique at any given time
- Only one view transition can run at a time
- **Shadow DOM incompatibility**: View transitions operate at document level and do not work within Shadow DOM due to style isolation ([spec discussion](https://github.com/w3c/csswg-drafts/issues/10303)). For web components, set `view-transition-name` on the `:host` element or use document-level transitions
**Specification & Documentation:**
For browser support, API details, and current specifications, refer to these authoritative sources (note: check publication dates as specs evolve):
- [MDN: View Transition API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API) - Comprehensive API reference
- [Chrome for Developers: View Transitions](https://developer.chrome.com/docs/web-platform/view-transitions) - Implementation guide and examples
- [W3C Draft Specification](https://drafts.csswg.org/css-view-transitions/) - Official specification (evolving)
### Performance Best Practices
- **Code split**: Split code at the panel/dialog level
- **Lazy load**: Use dynamic imports for heavy components
2.**Develop**: `script/develop` - Development server
3.**Lint**: `yarn lint` - Run all linting before committing
4.**Test**: `yarn test` - Add and run tests
5.**Build**: `script/build_frontend` - Test production build
### Common Pitfalls to Avoid
- Don't use `querySelector` - Use refs or component properties
- Don't manipulate DOM directly - Let Lit handle rendering
- Don't use global styles - Scope styles to components
- Don't block the main thread - Use web workers for heavy computation
- Don't ignore TypeScript errors - Fix all type issues
### Security Best Practices
- Sanitize HTML - Never use `unsafeHTML` with user content
- Validate inputs - Always validate user inputs
- Use HTTPS - All external resources must use HTTPS
- CSP compliance - Ensure code works with Content Security Policy
### Pull Requests
When creating a pull request, you **must** use the PR template located at `.github/PULL_REQUEST_TEMPLATE.md`. Read the template file and use its full content as the PR body, filling in each section appropriately.
- Do not omit, reorder, or rewrite the template sections
- Check the appropriate "Type of change" box based on the changes
- Do not check the checklist items on behalf of the user — those are the user's responsibility to review and check
- If the PR includes UI changes, remind the user to add screenshots or a short video to the PR after creating it
- Be simple and user friendly — explain what the change does, not implementation details
- Use markdown so the user can copy it
### Text and Copy Guidelines
#### Terminology Standards
**Delete vs Remove** (Based on gallery/src/pages/Text/remove-delete-add-create.markdown)
- **Use "Remove"** for actions that can be restored or reapplied:
- Removing a user's permission
- Removing a user from a group
- Removing links between items
- Removing a widget from dashboard
- Removing an item from a cart
- **Use "Delete"** for permanent, non-recoverable actions:
- Deleting a field
- Deleting a value in a field
- Deleting a task
- Deleting a group
- Deleting a permission
- Deleting a calendar event
**Create vs Add** (Create pairs with Delete, Add pairs with Remove)
- **Use "Add"** for already-existing items:
- Adding a permission to a user
- Adding a user to a group
- Adding links between items
- Adding a widget to dashboard
- Adding an item to a cart
- **Use "Create"** for something made from scratch:
- Creating a new field
- Creating a new task
- Creating a new group
- Creating a new permission
- Creating a new calendar event
#### Writing Style (Consistent with Home Assistant Documentation)
- **Use American English**: Standard spelling and terminology
- **Friendly, informational tone**: Be inspiring, personal, comforting, engaging
- **Address users directly**: Use "you" and "your"
- **Be inclusive**: Objective, non-discriminatory language
- **Be concise**: Use clear, direct language
- **Be consistent**: Follow established terminology patterns
- **Use active voice**: "Delete the automation" not "The automation should be deleted"
- **Avoid jargon**: Use terms familiar to home automation users
#### Language Standards
- **Always use "Home Assistant"** in full, never "HA" or "HASS"
- **Avoid abbreviations**: Spell out terms when possible
- **Use sentence case everywhere**: Titles, headings, buttons, labels, UI elements
- ✅ "Create new automation"
- ❌ "Create New Automation"
- ✅ "Device settings"
- ❌ "Device Settings"
- **Oxford comma**: Use in lists (item 1, item 2, and item 3)
- **Replace Latin terms**: Use "like" instead of "e.g.", "for example" instead of "i.e."
- **Avoid CAPS for emphasis**: Use bold or italics instead
- **Write for all skill levels**: Both technical and non-technical users
#### Key Terminology
- **"integration"** (preferred over "component")
- **Technical terms**: Use lowercase (automation, entity, device, service)
#### Translation Considerations
- **Add translation keys**: All user-facing text must be translatable
- **Use placeholders**: Support dynamic content in translations
- **Keep context**: Provide enough context for translators
pull_request_target: # zizmor: ignore[dangerous-triggers] -- safe:reads PR metadata from event payload only, checks out base repo scripts only, never PR head code
You are helping develop the Home Assistant frontend. This repository is a TypeScript application built from Lit-based Web Components for the Home Assistant web UI.
For gallery-specific documentation, demos, page structure, and examples, read `gallery/AGENTS.md` when working under `gallery/`.
## Essential Commands
```bash
yarn lint # ESLint + Prettier + TypeScript + Lit
yarn format # Auto-fix ESLint + Prettier
yarn lint:types # TypeScript compiler, run without file arguments
yarn test# Vitest
yarn dev # App dev server, supports --background/--status/--stop/--logs
yarn dev:serve # Local serving dev server, supports -c core URL, -p port, and dev flags
```
Never run `tsc` or `yarn lint:types` with file arguments. When `tsc` receives file arguments, it ignores `tsconfig.json` and can emit `.js` files into `src/`. Always run `yarn lint:types` without arguments. For individual file type checking, rely on editor diagnostics.
## Architecture
- The frontend uses custom elements built with Lit and TypeScript strict mode.
- Components communicate with the backend through the Home Assistant WebSocket API.
- Use `ha-` for Home Assistant components, `hui-` for Lovelace UI components, and `dialog-` for dialogs.
- Prefer `ha-*` components and current Web Awesome wrappers. Avoid adding new legacy `mwc-*` usage.
- Leaf components should consume narrow Lit contexts instead of taking the broad `hass` object unless they are containers that own and provide `hass`.
## Development Standards
- Use strict TypeScript, proper interfaces, and `import type` for type-only imports.
- Avoid `any`; model data with existing Home Assistant types or narrow new types.
- Keep imports organized and remove unused imports.
- Do not use `console`; use existing logging or user-visible error patterns.
- Use `@state()` for internal Lit state and `@property()` for public API.
- Do not query or manipulate DOM manually when Lit decorators, component refs, or render state are appropriate.
- Scope styles to components, use theme custom properties, and keep layouts mobile-first and RTL-safe.
- All user-facing text must be localized through the translation system.
## Project Skills
Detailed guidance lives in project skills under `.agents/skills/`. Load the matching skill before detailed implementation or review:
-`ha-frontend-contexts`: Lit contexts, `hass` migration, and rerender-sensitive state access.
-`ha-frontend-components`: dialogs, forms, alerts, shortcuts, tooltips, panels, and Lovelace cards.
-`ha-frontend-testing`: lint, typecheck, Vitest, Playwright e2e dev servers, and benchmarks.
-`ha-frontend-user-facing-text`: localization, terminology, sentence case, and Home Assistant text style.
-`ha-frontend-review`: PR template use, review checklist, and recurring review issues.
## Pull Requests
When creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the PR body. Preserve template sections, check only the appropriate type-of-change boxes, and do not check checklist items on behalf of the user. If the PR includes UI changes, remind the user to add screenshots or a short video.
"_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>`.",
Some 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.