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.
* 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>
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>
* 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>
The arrow-right icon next to the alert icon was decorative noise.
With automation comments (#52090) adding yet another icon, simplify
to a single mdiAlertCircleCheck indicator.
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* Update dependency @tsparticles/engine to v4
* Bump @tsparticles/preset-links to v4 to match engine
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Show both power buttons for assumed-state media players when unknown
Media players with assumed state report an unknown state when their
actual power state can't be determined. In that case the entity row and
more info should still expose both turn on and turn off controls so the
user can operate the device.
https://claude.ai/code/session_01JyZojNPCCY65HmRVQaASkG
* Treat media player unknown state like off instead of unavailable
The media player controls lumped the "unknown" state in with
"unavailable" and hid all controls. An unknown state is closer to "off":
the device exists but its power state isn't reported, which is common
for assumed-state players. Only "unavailable" should hide the controls,
so an unknown-state player now shows the turn on button (and both power
buttons when it has an assumed state) in the entity row and more info.
https://claude.ai/code/session_01JyZojNPCCY65HmRVQaASkG
* Adjust comments and variable placement for media player state check
https://claude.ai/code/session_01JyZojNPCCY65HmRVQaASkG
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(entities): clear URL-injected filters on leaving entities dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(entities): 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 page: 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>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(devices): clear URL-injected filters on leaving devices dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(devices): restore previous filters after URL-injected navigation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(devices): use separate storage and display filters
Replace the disconnect-callback approach with two distinct filter states:
- _storageFilters: persisted to sessionStorage, updated only when not in
URL mode (manual filter changes and clear)
- _filters: display-only state, initialized from _storageFilters on first
render, overwritten by URL params without touching storage
_storageFilters is frozen while _fromUrl is true, preserving the user's
previous manual filters for the next normal visit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update dependency eslint-plugin-lit to v2.3.1
* Fix lit/prefer-query-decorators violations
eslint-plugin-lit 2.3.0 introduced this rule. Replace querySelector
calls with @query/@queryAll decorators where the selector is static.
Use per-line disables for dynamic selectors that can't use decorators.
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
Replaces the per-render scrollHeight read from #52012 with a
ResizeObserver started in firstUpdated, writing --tip-height directly to
the host style. Removes the need for a manual refresh when the viewport
crosses a wrap threshold, drops the unreachable isNaN check, and lets
the @query decorator stand in for the editor-tip id.
fix(filter): prevent badge count from incrementing on panel re-open
Integrations and domains filter panels use lazy rendering: the list is
destroyed on close and recreated on open. On recreation, MWC fires a
`selected` event with a diff for each pre-selected item, which the
diff-based handler interpreted as a new user selection, appending
duplicates to `this.value` on every expansion.
Switch both handlers to the full-set approach (`SelectedDetail<Set<number>>`)
already used by labels, states, and voice-assistants, rebuilding the value
from the complete index set. Add the `preserved` pattern to retain
selections hidden by the search filter. Also add `_value` to the `_domains`
memoize signature to ensure cache invalidation when the selection changes.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.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.
* feat(lovelace): add mute button to media player volume buttons card feature
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add show_mute_button config option to volume buttons feature
* feat: disable show_mute_button option when entity does not support mute
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Apply suggestions from code review
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Migrate ha-drawer to Web Awesome drawer
* Make CI happy
* Implement swipe gesture support for ha-drawer and fix RTL support
* Fix CI
* Fix CI
* Readd border
* Fix sidebar
* Layout fix
* Fix sluggish scroll on mobile
* Fix CI
* Add transition
* add more options for history-graph-card
* add more options for history-graph-card
* Apply suggestion from @MindFreeze
---------
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Add context to statistics panel
* Lazy context
* Cleanup
* Types
* Use api context, use registries, update helpers to only need api
* Infer type
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Remove —
* Format
---------
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Generate third party license file during production build
* Add license check CI step
* Address review comments: use license-checker-rseidelsohn, add version validation for LICENSE_OVERRIDES
* Fix license-checker-rseidelsohn import (CJS module, use require)
* Add link to single integration entry warning
* Refactor single config entry warning: move function to dedicated file and update imports
* Implement single config entry warning dialog and update related functions
* Apply suggestions from code review
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
---------
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Position statistics chart bars at centre of time range
When displaying 5minute or hourly data periods, position each bar at the midpoint of its start/end time. This mimics the behaviour in the various energy cards for consistency.
* Move limit comparison into pushData
Results in clearer function argument usage.
* Add time range for statistics-chart bar tooltip
When using hour/5minute periods the bars are recentred. Update the tooltips to show time range they cover.
* Omit time from tooltip for bars with periods of day or longer
Don't clutter the tooltip with unnecessary times of 0:00 when using day/month/year timescales on bar charts, just show the date range.
For week/month/year, we now also include the range of dates of the bar rather than just the start date.
* Migrate dialog-expose-entity to new dialog and migrate everything thats needed for this.
* Load virtualizer after dialog show is ready
* Use entities context instead of registries in ha-state-icon
* fix types
* Update src/panels/config/voice-assistants/dialog-expose-entity.ts
---------
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
* Fixes tile card misalignment (#25745)
Removes an unnecessary vertical padding on the tile card content that causes a misalignment within the Android Companion app. This padding isn't needed because the contents are already vertically aligned with flexbox anyway.
* Added a min-height to tile container
As requested in the review, added a minimal height to the content of the
tile container to support non-section layouts.
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.
* Move buttons to standard footers
* Fix negative margin, use space tokens
* Space tokens with tweaks
* Hide form if empty
* Standardise padding
* Only show skip if no devices are assigned
* Use ref instead of queries
* nothing
* Token
* Typing
* Don't round bar chart end time for hourly periods
If we do this, it causes the last hour of the energy dashboard bar charts to be cut off. This went unnoticed previously because they were placed at times of xx:00, while now they are times of xx:30.
* Round to 30minute for hourly bars rather than leaving unrounded
This better matches the axes with line charts by cutting off padding into the next day, whilst leaving mid-point bars visible.
* Update tests to account for new behaviour
Fix type exception in chart _updateSankeyRoam
When there is no data for some series in the sankey chart, then the series map can contain null entries. This raised an exception as the _updateSankeyRoam tried to access the 'type' property on a null value.
Add an explicit check for null. Using != not !== to also filter undefined in case that ever shows up.
* add a possibility to customize color
* add a possibility to customize color
* add GraphEntityConfig
* add basic color support
---------
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Give less importance to the custom tag and tag id in the UI
* Make an expandable version prefill with a tagID
* Improve Edit tag dialog to be more usable
* Apply manually prettier
* Apply suggestion from @MindFreeze
---------
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* add new ha-list options
* Refactor ha-list components to use ha-list-selectable and ha-list-item-option
* fix types in gallery
* fix filter-floor-areas
* Review
* Fix list aria-label
* make a "description" expandable
* add "about" label for devtools->templates
* Update src/translations/en.json
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* expandedWillChange -> expandedChanged
* Add type annotation to _expandedChanged method
* Add import for HASSDomEvent type
* prettier
---------
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* 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>
* Ally: Add aria labels to ha-icon-button and hui-root
* use aria-hidden
* Add hidden content for label to satisfy ally review
* Make fix in button instead (probably should update upstream)
* Aria label (pending wa update)
* 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>
* Only show events output when there are any, types and margin
* Refactor to use pagination
* Fix
* Simplify, remove pinning and auto-follow, stay on event 1 and allow the user to move around manually
* Show info why only 30 events are keps
* Increase bufffer limit to 100, add explainer and tip when rolls over
* Update disclaimer
* Use buffer position and total instead of event id + total in counter
* Use fixed height and constrain editor
* Cleanup
* Cleanup
* Fix narrow layouts
The old heading "Device created" was a past-tense status message that didn't
communicate what the user needs to do next. The dialog contains a name field
and an area picker, so "Name and assign" better reflects the user's actual
task at this step.
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* Check for unknown value every render if value is unknown
* fix race in pickers that add items from the picker
* fix: use value imports for LitElement, html, nothing from lit
* feat: Split legend interaction (click vs label) with icons and hover effects
* Address review feedback on chart legend split
- Rename `externalHiddenState` to `clickLabelForMoreInfo` and the event
`chart-legend-click` to `legend-label-click` (the `chart-` prefix is
reserved for echarts-proxy events).
- Use proper translation keys
`ui.components.history_charts.{toggle_visibility,show_more_info}`
instead of `ui.common.*` which don't exist.
- Drop the redundant tooltip on the label when it would duplicate the
icon's "Toggle visibility" tooltip.
- Resolve the legend dataset id back to a real `entity_id` before
opening more-info: strip known climate-attribute suffixes for history
charts (`-current_temperature`, `-target_temperature*`, `-heating`/
`-cooling`/`-drying`/`-fan`), and skip `isExternalStatistic` ids for
statistics charts. Only fire `hass-more-info` if `hass.states[id]`
resolves.
* Address followup review feedback on chart legend
- Restore stat-type suffix stripping in statistics chart. When the chart
has a single entity, ha-chart-base falls back to raw series ids
(`${statistic_id}-${type}`) for the legend, so clicking
"sensor.foo (max)" otherwise tries to open `sensor.foo-max`.
- Cover humidifier multi-attribute datasets in the entity-id resolver
alongside climate / water_heater (`-current_humidity`,
`-target_humidity`, `-humidifying`, `-on`; `-drying` is already
contributed by CLIMATE_MODE_CONFIGS). Rename the constant to
ENTITY_DATASET_SUFFIXES to reflect the broader scope.
- Convert the two legend click targets to `<button type="button">` so
Tab navigation, Enter/Space activation, and screen-reader semantics
work. Add `aria-pressed` to the visibility toggle and a
`:focus-visible` outline. Read the dataset id from `data-id` instead
of `parentElement.id` so the handlers don't depend on DOM nesting.
- Scope the label hover underline to `.label-clickable` so charts that
don't opt into more-info (network, sankey, sunburst, energy) keep the
legacy non-underlined toggle behavior. Render the "Show more info"
tooltip only when the click will actually fire.
- Drop the redundant physical `margin-right: 0` on the legend toggle;
`margin-inline-end: 0` alone preserves the click-area extension on
the start side in RTL.
---------
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Modified the chart legend for climate temperature data sets to display the temperature value, not the overall entity state (previously hvac_mode).
* Use the helper computeAttributeValueDisplay in src/common/entity/compute_attribute_display.ts to format the temperature attribute values.
Co-authored-by: Copilot <copilot@github.com>
* Update src/components/chart/state-history-chart-line.ts
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* fix: rebuild ha-panel-custom on reconnect after suspendWhenHidden
Why:
HA's PartialPanelResolver._onHidden() starts a 5-minute timer when a tab
is backgrounded. When it fires, non-iframe / non-app custom panels
(component_name="custom" without embed_iframe) are removed from the DOM,
which triggers ha-panel-custom.disconnectedCallback() -> _cleanupPanel().
That nulls _setProperties and destroys the child custom-panel element.
When the user returns, _onVisible() re-appends the same <ha-panel-custom>
element. ReactiveElement's base connectedCallback schedules an update,
but update() only calls _createPanel(this.panel) when
changedProps.has("panel") && !deepEqual(oldPanel, this.panel) -- the
panel reference hasn't changed, so nothing happens. _setProperties is
also undefined, so the property-forwarding branch exits early too.
Result: <ha-panel-custom> is present in the DOM but empty -- the user
sees a blank panel until they hard-reload.
Add a connectedCallback() override that rebuilds when the element was
previously cleaned up. Three guards keep it scoped to the recovery path:
- !this._setProperties: sentinel for "_cleanupPanel ran".
_setProperties is set inside _createPanel's success paths and only
nulled in _cleanupPanel.
- !this.hasChildNodes(): defends against the async window inside
loadCustomPanel(config).then(...) for non-iframe panels, where a
rapid detach->attach cycle could otherwise call _createPanel twice
and append duplicate elements.
- this.panel: skips first-mount, when the router hasn't assigned a
panel yet. The existing update() path handles initial _createPanel
via the changedProps.has("panel") branch.
Mirrors the existing disconnectedCallback override for symmetry.
10 lines + comment, no other file changes.
Affects every non-iframe sidebar custom panel in the HACS ecosystem
(Alarmo, Browser Mod, Homematic(IP) Local, MeshCore, others -- ~60k
combined HA-analytics installs). Reopens dormant home-assistant/frontend
issue #14510 (filed 2022-12-02, stale-bot closed without a fix).
Tests:
- yarn build succeeds locally; the affected chunk is captured for the
pre-PR local-test on a real HA host.
- Manual repro on user's HA host (10.10.21.221) with the MeshCore
custom panel: baseline (no patch) reproduces the blank-panel symptom
after a 6-minute backgrounded tab. Patched chunk: panel rebuilds
within ~2 seconds of returning. Exemption paths (iframe panel, app
panel, custom panel with embed_iframe=true) verified to remain
unaffected. Original chunk restored after testing.
- yarn lint and yarn test pass before PR submission (run before
Phase 9).
* chore: trigger CLA recheck after author-email link
* increase an active "enlarge" area
Added a class to make the title span enlargeable.
* increase an active "enlarge" area
* increase an active "enlarge" area
* Add entity context to media browser player picker
Show area and device as a secondary line in both the active player pill
and the player picker dropdown, so users can identify speakers when
multiple players share similar names.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update src/panels/media-browser/ha-bar-media-player.ts
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
Remove the unmaintained eslint-config-airbnb-base dependency (last
updated Nov 2021, no flat config support) along with its FlatCompat
shim infrastructure.
Replace with js.configs.recommended as the base config and explicitly
cherry-pick ~40 high-value safety and style rules from airbnb-base
that aren't already covered by other configs.
Remove 27 rule disables that only existed to suppress airbnb opinions,
and 5 dead TypeScript rule disables for rules no longer in the config.
Fix 4 real bugs caught by the newly added no-constant-binary-expression
rule where template literals were always truthy, making fallback values
unreachable.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
If the app config contains a schema field like this one:
```
privileges:
- "list(ALTER|CREATE|...|UPDATE)?"
```
it was rendered incorrectly as a drop-down where only one item can be
selected - but this is wrong because of the preceding `-` denoting it
should be a list containing the listed values. Supervisor translated
this to an entry of type `select` with `multiple: true`. The `multiple`
flag wasn't passed along, with the flag set the field renders as
expected.
Fixes#51533
* Fix history/sensor cards stuck loading after backend restart
- Add { resubscribe: false } to history subscriptions to prevent
corrupt HistoryStream state on auto-resubscription
- Add connection-status handlers to re-subscribe on reconnect
- Add sentinel pattern to prevent re-entrant async subscriptions
- Add shouldUpdate/updated retry when components become available
- Clear sensor device classes cache on WS error
- Clear _error on reconnect so cards can retry
- Add .catch() on unsubscribe to handle dead subscriptions
* Fix type annotation for callWS in getSensorNumericDeviceClasses
* Address review: type connection-status handlers, add reconnect to history panel
- Use HASSDomEvent<ConnectionStatus> instead of (ev as CustomEvent).detail
for proper type safety on all connection-status handlers
- Add connection-status handler to ha-panel-history so it re-subscribes
after backend restart (addresses concern about resubscribe: false)
* Address review: sentinel pattern, reconnect handling, stale data reset
- Add sentinel pattern to ha-more-info-history, ha-panel-history,
hui-history-graph-card to prevent re-entrant subscription races
- Refactor hui-trend-graph-card-feature from SubscribeMixin to manual
subscription management with connection-status reconnect support
- Reset stale history/statistics data on reconnect in
hui-history-graph-card and hui-map-card before re-subscribing
- Wrap fetchStatistics and getSensorNumericDeviceClasses calls in
ha-panel-history with try/catch to handle errors gracefully
- Chain .catch directly on subscribeHistoryStatesTimeWindow in
hui-trend-graph-card-feature to avoid detached-promise race condition
* Centralize history stream reconnect handling in data layer
Move the reconnect logic from every consumer into `subscribeHistoryStream`
in data/history.ts. The helper listens to the connection's `ready` event
itself, and on reconnect creates a fresh `HistoryStream` and rebuilds
params (so `start_time` for the time-window variant is re-anchored to
"now"). `resubscribe: false` stays as an internal implementation detail.
Removes the duplicated `_handleConnectionStatus` boilerplate and
`connection-status` window listeners from all six history consumers.
* Render subscription errors and make _error reactive
`_error` was declared as a plain string field in hui-graph-header-footer
and ha-more-info-history (non-reactive) and typed as Error/string while
being assigned the WS error object. hui-trend-graph-card-feature had it
reactive but never rendered it.
Align all three with the hui-history-graph-card pattern: reactive
`{ code, message }` and a user-visible error branch in render(). Without
this, a failed subscription would leave the component stuck on a spinner
forever.
---------
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
* Fix base time inputs reportValidity() function
The queryAll selector returns a NodeList not not an array. Need to spread it to an array before we can use every().
* Validate the date range picker time inputs
Enable auto validation to get the nice red underline on invalid values, and then check validity before accepting the input.
* Fix automatic 24hr value conversion in AM/PM format
When using AM/PM, entering a 24 hour value will automatically convert the first time. For example 15 will become 3. However if you then enter 15 again it will stay as 15 and not update.
To fix this, make sure we trigger an update of the input field once the current update cycle is complete.
* Validate time inputs on save not value update
In the value changed callback, the update 24->12hr input correction will not have been updated and therefore they will report invalid.
Add `white-space: pre-line` to the event description style so that
newlines in the calendar event description are rendered correctly
instead of being collapsed into a single line.
2026-03-26 17:07:32 +01:00
1235 changed files with 55135 additions and 23946 deletions
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.
**Note**: This file contains high-level guidelines and references to implementation patterns. For gallery-specific documentation, demos, page structure, and usage examples, see [`gallery/AGENTS.md`](gallery/AGENTS.md).
## Table of Contents
- [Quick Reference](#quick-reference)
- [Core Architecture](#core-architecture)
- [State Access: Contexts Instead of `hass`](#state-access-contexts-instead-of-hass)
- [Development Standards](#development-standards)
- [Component Library](#component-library)
- [Common Patterns](#common-patterns)
@@ -40,7 +41,7 @@ script/develop # Development server
@@ -52,13 +53,64 @@ The Home Assistant frontend is a modern web application that:
- Communicates with the backend via WebSocket API
- Provides comprehensive theming and internationalization
## State Access: Contexts Instead of `hass`
Every component used to take the whole `hass: HomeAssistant` object — a god-object that re-renders on any unrelated `hass` change, forces tests to mock everything, and hides what a component actually reads. We're moving leaf components to **fine-grained [Lit context](https://lit.dev/docs/data/context/)**: consume only the slice you need and re-render only when it changes.
For new code, consume the matching context instead of adding a `hass` property. `hass` stays for container components that own it and feed the providers; the canonical migration is [`hui-button-card.ts`](src/panels/lovelace/cards/hui-button-card.ts). Infrastructure: contexts in [`src/data/context/index.ts`](src/data/context/index.ts), the `consume…` helpers in [`src/common/decorators/consume-context-entry.ts`](src/common/decorators/consume-context-entry.ts), and `@transform` in [`src/common/decorators/transform.ts`](src/common/decorators/transform.ts). Providers are wired automatically by `contextMixin` on `HassBaseEl` — you only consume.
### Contexts
Consume the narrowest context that covers your reads:
Lazy contexts (subscribe on first consumer, tear down after the last): `labelsContext`, `fullEntitiesContext`, `configEntriesContext`, `manifestsContext`. The single-field contexts (`localizeContext`, `themesContext`, `userContext`, …) are **deprecated** — use the grouped ones above.
### Consuming
Use the `consume…` helpers for entity-scoped and `localize` reads. `entityIdPath` is resolved against `this`, so these watch `this._config.entity`:
`@transform`'s `watch` option re-runs the transformer when a host prop changes — needed when an entity id is computed, since `consumeEntityState` only watches the first path segment. To consume a whole group untransformed, drop `@transform` and type it `ContextType<typeof statesContext>`.
- ESLint config (flat config) extends TypeScript strict, Lit, Web Components, Accessibility (lit-a11y), and import-x
- Prettier with ES5 trailing commas enforced
- No console statements (`no-console: "error"`) - use proper logging
- Import organization: No unused imports, consistent type imports
@@ -136,6 +188,7 @@ export class HaMyComponent extends LitElement {
### Data Management
- **Use WebSocket API**: All backend communication via home-assistant-js-websocket
- **Prefer contexts over `hass`**: For state reads, consume the relevant Lit context instead of taking the whole `hass` object — see [State Access: Contexts Instead of `hass`](#state-access-contexts-instead-of-hass)
- **Cache appropriately**: Use collections and caching for frequently accessed data
- **Handle errors gracefully**: All API calls should have error handling
- **Update real-time**: Subscribe to state changes for live updates
@@ -160,7 +213,7 @@ try {
- Defined in `src/resources/theme/core.globals.ts`
- Common values: `--ha-space-2` (8px), `--ha-space-4` (16px), `--ha-space-8` (32px)
- **Mobile-first responsive**: Design for mobile, enhance for desktop
- **Follow Material Design**: Use Material Web Components where appropriate
- **Prefer `ha-*` components**: Build on the Home Assistant component library (many now wrap Web Awesome components); avoid new use of legacy Material Web Components (`mwc-*`), which are being phased out
- **Support RTL**: Ensure all layouts work in RTL languages
- **Primary action**: `appearance="filled"` for emphasis (or the default appearance for a lighter look)
- **Secondary action**: `appearance="plain"` for cancel/dismiss actions
- **Destructive actions**: `variant="danger"` for delete/remove operations (the generic confirmation dialog uses `variant="danger"` for its confirm button — see `src/dialogs/generic/dialog-box.ts`)
- Always place primary action in `slot="primaryAction"` and secondary in `slot="secondaryAction"` within `ha-dialog-footer`
5.**Build**: `script/build_frontend` - Test production build
### Gallery
For Gallery-specific structure, page/demo naming, sidebar behavior, content standards, and commands, see [`gallery/AGENTS.md`](gallery/AGENTS.md).
### Common Pitfalls to Avoid
- Don't use`querySelector` - Use refs or component properties
- Don't manually query the DOM with`querySelector` - use the `@query`/`@queryAll` decorators 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
@@ -477,7 +528,7 @@ When creating a pull request, you **must** use the PR template located at `.gith
#### Terminology Standards
**Delete vs Remove** (Based on gallery/src/pages/Text/remove-delete-add-create.markdown)
**Delete vs Remove**
- **Use "Remove"** for actions that can be restored or reapplied:
- Removing a user's permission
@@ -540,35 +591,24 @@ When creating a pull request, you **must** use the PR template located at `.gith
#### Translation Considerations
- **Add translation keys**: All user-facing text must be translatable
- **Use placeholders**: Support dynamic content in translations
All user-facing text must be translatable — see the **Internationalization** section (under Common Patterns) for the `localize` API and placeholder usage. From a copy perspective:
- **Keep context**: Provide enough context for translators
("Are you sure you want to delete this automation?");
```
- **Avoid concatenation**: Prefer full localized strings with placeholders over stitching translated fragments together
### Common Review Issues (From PR Analysis)
Recurring, easy-to-miss problems surfaced in real PR reviews. These complement the standards above rather than repeating them — items already covered earlier (loading states, error handling, mobile layout, theming, import hygiene) are intentionally not duplicated here.
#### User Experience and Accessibility
- **Form validation**: Always provide proper field labels and validation feedback
- **Form accessibility**: Prevent password managers from incorrectly identifying fields
- **Loading states**: Show clear progress indicators during async operations
- **Error handling**: Display meaningful error messages when operations fail
- **Mobile responsiveness**: Ensure components work well on small screens
- **Hit targets**: Make clickable areas large enough for touch interaction
- **Visual feedback**: Provide clear indication of interactive states
- **Visual feedback**: Provide clear indication of interactive states (hover, active, focus)
#### Dialog and Modal Patterns
- **Dialog width constraints**: Respect minimum and maximum width requirements
- **Interview progress**: Show clear progress for multi-step operations
- **State persistence**: Handle dialog state properly during background operations
- **Cancel behavior**: Ensure cancel/close buttons work consistently
Final pre-submission checklist. Linting and formatting areenforced by tooling, so this focuses on what tools can't catch rather than restating every rule above.
This file applies to all files under `gallery/`. Follow the root `AGENTS.md` for repository-wide Home Assistant frontend, TypeScript, Lit, accessibility, and copy standards. This file adds gallery-specific structure, page, demo, and verification guidance.
## Quick Reference
Run commands from the repository root unless noted otherwise:
```bash
gallery/script/develop_gallery # Start the gallery development server
gallery/script/build_gallery # Build the static gallery
yarn lint # ESLint, Prettier, TypeScript, and Lit checks
yarn lint:types # TypeScript compiler, without file arguments
```
Never run `yarn lint:types` or `tsc` with file arguments. See the root `AGENTS.md` for the generated `.js` file risk.
## Purpose
The gallery is a developer and designer reference for Home Assistant frontend UI patterns. It documents component APIs, shows realistic Lovelace and more-info states, captures brand and copy guidance, and provides reproducible demos that are safe to inspect outside a running Home Assistant instance.
- Prefer demonstrating real production components from `src/` instead of creating gallery-only replacements.
-`public/`: Static assets copied into the gallery output.
## Page Model
Gallery pages are generated by `gather-gallery-pages` in `build-scripts/gulp/gallery.js`.
- A page id is the path under `src/pages/` without the extension, like `components/ha-button`.
- A `.markdown` file and a `.ts` file with the same page id become one gallery page.
- A page may have only markdown, only a TypeScript demo, or both.
- Markdown can contain YAML frontmatter with `title` and optional `subtitle`.
- Markdown that contains only frontmatter contributes metadata without rendering a description block.
- TypeScript demo modules are dynamically imported for side effects when the page is opened.
- A demo module must define a custom element named `demo-${category}-${page}` with slashes replaced by hyphens, like `demo-components-ha-button` for `components/ha-button`.
-`ha-gallery.ts` renders that element with `dynamicElement()` based on the current page id.
## Sidebar
Use `sidebar.js` when a page needs a visible section, section header, or deterministic ordering.
-`category` must match the first directory name under `src/pages/`.
-`header` is the section label shown in the drawer.
-`pages` is optional. When present, listed pages keep that exact order.
- Pages in a category that are not listed are appended alphabetically after the listed pages.
- New categories without a sidebar entry are appended by the generator with their category name as the header.
- If a listed page does not exist, the generator logs an error during `gather-gallery-pages`.
## Markdown Pages
Use markdown pages for explanations, design guidance, API notes, and copy standards.
- Start with frontmatter when the page needs a title or subtitle.
- Use sentence case for titles, headings, labels, and UI copy.
- Put the live example before the reference API when that makes the page easier to scan.
- Use fenced code blocks with a language tag for copyable examples.
- Keep examples short and focused on the behavior being documented.
- Prefer real component names and attributes over prose-only descriptions.
- Use Home Assistant terminology from the root `AGENTS.md`.
- For remove/delete and add/create wording, follow `src/pages/misc/remove-delete-add-create.markdown`.
Gallery markdown is documentation content and is not localized with `localize`. If demo code creates production UI strings, keep those strings aligned with the root localization and copy guidance.
## Demo Components
Use TypeScript demo pages for interactive or stateful examples.
- Import production components from `../../../src/...` or the correct relative path from the demo file.
- Import reusable gallery helpers from `gallery/src/components/` when they already model the pattern.
- Use `demo-card` and `demo-cards` for Lovelace card examples that render YAML card configs.
- Use `demo-more-info` and `demo-more-infos` for more-info dialog examples.
- Use shared mock data from `src/data/` instead of repeating large fake state objects inline.
- Show meaningful states, such as loading, unavailable, empty, error, active, inactive, and disabled when relevant.
- Check responsive behavior and the gallery RTL toggle when layout or direction-sensitive UI changes.
- Keep unavoidable casts or loose demo parsing local to the demo helper or demo page.
The gallery ESLint config allows `console` for gallery diagnostics. Do not copy that exception into production frontend code.
## Content Standards
The root copy standards still apply: use American English, sentence case, active voice, inclusive language, direct user-focused wording, and consistent Home Assistant terminology.
- Use `Home Assistant` in full, not `HA` or `HASS`.
- Use `integration` instead of `component` for product concepts.
- Use `Remove` for reversible disassociation and `Delete` for permanent deletion.
- Use `Add` for existing items and `Create` for something made from scratch.
- Avoid Latin abbreviations like `e.g.` and `i.e.` in prose.
- Avoid stitching sentence fragments together in production UI examples.
## Verification
- For markdown, sidebar, and page-generation changes, run `gallery/script/build_gallery`.
- For TypeScript demo or gallery shell changes, run the smallest relevant check plus `yarn lint` when practical.
- For type checking, run `yarn lint:types` without file arguments.
- For visual changes, run `gallery/script/develop_gallery` and check the affected page on desktop, narrow viewport, and RTL when relevant.
- If verification is skipped, state which command was skipped and why.
These five traits describe who Home Assistant is, not how it speaks. Tone of voice – the playfulness, the informality, the warmth, etc – should flow naturally from these, and will help guide writers on how to bring the brand personality to life.
The first four traits are relational: they describe how Home Assistant behaves toward its users.
_Welcoming_ is about how we receive them.\
_Candid_ is about how we communicate with them.\
_Supportive_ is about how we help them.\
_Generous_ is about how/what we give to them.\
If any of these feel similar, it’s because they’re all expressions of the same underlying character, just in different moments of the user relationship.
_Independent_ is different. It’s foundational: it describes who Home Assistant is at its core.\
And it’s because of that independence that the other four traits feel genuine rather than performed. A corporate brand can try to be welcoming, candid, supportive, and generous,
but without independence, those traits will always be managed and moderated.
## Welcoming
**Warm and open, kind, friendly, approachable, accommodating**\
_But not: people pleasing, appeasing, sycophantic, ingratiating_\
Home Assistant feels like a knowledgeable friend, not a product. We meet you at your own level, never talk down to you, and make you feel valued regardless of your technical ability. This isn’t performative, it’s expressed naturally in the small things: how errors are explained, documentation is written, and how the community talks to newcomers.
## Candid
**Direct, honest, transparent, unpretentious**\
_But not: unfriendly, rude, blunt, unempathetic_\
Home Assistant says what it means. We don’t hide complexity behind false simplicity, fall back on marketing fluff, or pretend limitations don’t exist. We respect users enough to be straight with them: about what Home Assistant can do, what it can't, and why it works the way it does. This is what builds real trust with users.
Home Assistant always has your back. Whether you’re just starting out or deep into a complex setup, we steer you forward without taking over. Our support is genuine: practical, patient, and there when it’s needed most. Because Home Assistant wants every user to succeed in building a smart home with privacy, choice, and sustainability at its heart.
Home Assistant gives you everything you need today, and as your setup evolves. There are no strings attached: no artificial limits, features locked behind tiers, or deceptive patterns designed to tie you to a closed platform. We trust users with control, access, and transparency. This generosity is reciprocal: the time, knowledge, and care our community gives freely is what keeps Home Assistant thriving and truly open.
Home Assistant doesn’t feel the need to behave like an established tech brand or follow corporate rules. With no shareholders or VCs to answer to, we can say what we think, do things our own way, and not take ourselves too seriously. This freedom of spirit comes from the confidence of knowing our own values, and that our community shares them.
@@ -13,7 +13,7 @@ Our dialogs are based on the latest version of Material Design. Please note that
- Dialogs have a max width of 560px. Alert and confirmation dialogs have a fixed width of 320px. If you need more width, consider a dedicated page instead.
- The close X-icon is on the top left, on all screen sizes. Except for alert and confirmation dialogs, they only have buttons and no X-icon. This is different compared to the Material guidelines.
- Dialogs can't be closed with ESC or clicked outside of the dialog when there is a form that the user needs to fill out. Instead it will animate "no" by a little shake.
- Dialogs can't be closed with ESC or clicked outside of the dialog when there is a form that the user **has made changes to**. Instead it will animate "no" by a little shake.
- Extra icon buttons are on the top right, for example help, settings and expand dialog. More than 2 icon buttons, they will be in an overflow menu.
- The submit button is grouped with a cancel button at the bottom right, on all screen sizes. Fullscreen mobile dialogs have them sticky at the bottom.
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
@@ -10,7 +10,7 @@ All pages are stored in [the pages folder][pages-folder] on GitHub. Pages are gr
## Development
You can develop design.home-assistant.io locally by checking out [the Home Assistant frontend repository](https://github.com/home-assistant/frontend). The command to run the gallery is `gallery/script/develop_gallery`. It will automatically open a browser window and load the development version of the website.
You can develop design.home-assistant.io locally by checking out [the Home Assistant frontend repository](https://github.com/home-assistant/frontend). The command to run the gallery is `gallery/script/develop_gallery`. After the first build finishes, the command prints the local URL for the development version of the website.
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.