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>
* 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
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 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.
* 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.
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.
* 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
* 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
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.
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.
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.
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.