From 7c879cc2914596f55ac341176267a8a9b8858b32 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 9 May 2025 04:23:52 -0400 Subject: [PATCH 1/9] Add installation method to the about page (#25378) Co-authored-by: Bram Kragten Co-authored-by: Franck Nijhof --- src/data/system_health.ts | 26 +++++++++++++++---- src/panels/config/info/ha-config-info.ts | 18 +++++++++++++ .../repairs/dialog-system-information.ts | 4 +-- src/translations/en.json | 1 + 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/data/system_health.ts b/src/data/system_health.ts index 60bdc21337..d8fb40c924 100644 --- a/src/data/system_health.ts +++ b/src/data/system_health.ts @@ -26,13 +26,29 @@ export type SystemCheckValue = | boolean | SystemCheckValueObject; -export type SystemHealthInfo = Record< - string, - { +export type SystemHealthInfo = Partial<{ + homeassistant: { + info: { + version: string; + installation_type: string; + dev: boolean; + hassio: boolean; + docker: boolean; + user: string; + virtualenv: boolean; + python_version: string; + os_name: string; + os_version: string; + arch: string; + timezone: string; + config_dir: string; + }; + }; + [domain: string]: { manage_url?: string; info: Record; - } ->; + }; +}>; interface SystemHealthEventInitial { type: "initial"; diff --git a/src/panels/config/info/ha-config-info.ts b/src/panels/config/info/ha-config-info.ts index d58f4a858e..cb2b7fbf23 100644 --- a/src/panels/config/info/ha-config-info.ts +++ b/src/panels/config/info/ha-config-info.ts @@ -29,6 +29,7 @@ import { mdiHomeAssistant } from "../../../resources/home-assistant-logo-svg"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant, Route } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; +import { subscribeSystemHealthInfo } from "../../../data/system_health"; const JS_TYPE = __BUILD__; const JS_VERSION = __VERSION__; @@ -99,6 +100,8 @@ class HaConfigInfo extends LitElement { @state() private _hassioInfo?: HassioInfo; + @state() private _installationMethod?: string; + protected render(): TemplateResult { const hass = this.hass; const customUiList: { name: string; url: string; version: string }[] = @@ -127,6 +130,14 @@ class HaConfigInfo extends LitElement {

Home Assistant

    +
  • + ${this.hass.localize( + `ui.panel.config.info.installation_method` + )} + ${this._installationMethod || "…"} +
  • Core ${hass.connection.haVersion} @@ -249,6 +260,13 @@ class HaConfigInfo extends LitElement { if (isComponentLoaded(this.hass, "hassio")) { this._loadSupervisorInfo(); } + + const unsubSystemHealth = subscribeSystemHealthInfo(this.hass, (info) => { + if (info?.homeassistant) { + this._installationMethod = info.homeassistant.info.installation_type; + unsubSystemHealth.then((unsub) => unsub()); + } + }); } private async _loadSupervisorInfo(): Promise { diff --git a/src/panels/config/repairs/dialog-system-information.ts b/src/panels/config/repairs/dialog-system-information.ts index b9666b0c57..a556d96ce9 100644 --- a/src/panels/config/repairs/dialog-system-information.ts +++ b/src/panels/config/repairs/dialog-system-information.ts @@ -301,7 +301,7 @@ class DialogSystemInformation extends LitElement { } else { const domains = Object.keys(this._systemInfo).sort(sortKeys); for (const domain of domains) { - const domainInfo = this._systemInfo[domain]; + const domainInfo = this._systemInfo[domain]!; const keys: TemplateResult[] = []; for (const key of Object.keys(domainInfo.info)) { @@ -387,7 +387,7 @@ class DialogSystemInformation extends LitElement { const domainParts: string[] = []; for (const domain of Object.keys(this._systemInfo!).sort(sortKeys)) { - const domainInfo = this._systemInfo![domain]; + const domainInfo = this._systemInfo![domain]!; let first = true; const parts = [ `${ diff --git a/src/translations/en.json b/src/translations/en.json index 11f047653f..daa4acfb7f 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3110,6 +3110,7 @@ }, "info": { "caption": "About", + "installation_method": "Installation method", "copy_menu": "Copy menu", "copy_raw": "Raw text", "copy_github": "For GitHub", From 30d46f2f8a0d853191cc2b4129d3187d54b60b5b Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 9 May 2025 18:37:14 +0200 Subject: [PATCH 2/9] Fix domain max-width in entity picker (#25400) --- src/components/entity/ha-entity-combo-box.ts | 4 +++- src/components/ha-combo-box-item.ts | 2 +- src/dialogs/quick-bar/ha-quick-bar.ts | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/entity/ha-entity-combo-box.ts b/src/components/entity/ha-entity-combo-box.ts index db18168c3a..d1d22b4c55 100644 --- a/src/components/entity/ha-entity-combo-box.ts +++ b/src/components/entity/ha-entity-combo-box.ts @@ -178,7 +178,9 @@ export class HaEntityComboBox extends LitElement { : nothing} ${item.domain_name && !showEntityId ? html` -
    ${item.domain_name}
    +
    + ${item.domain_name} +
    ` : nothing} diff --git a/src/components/ha-combo-box-item.ts b/src/components/ha-combo-box-item.ts index 1a061174fb..feef58b47e 100644 --- a/src/components/ha-combo-box-item.ts +++ b/src/components/ha-combo-box-item.ts @@ -39,7 +39,7 @@ export class HaComboBoxItem extends HaMdListItem { font-family: var(--ha-font-family-code); font-size: var(--ha-font-size-xs); } - [slot="trailing-supporting-text"] { + ::slotted(.domain) { font-size: var(--ha-font-size-s); font-weight: var(--ha-font-weight-normal); line-height: var(--ha-line-height-normal); diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 68833ad0bf..21b1215e06 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -386,7 +386,7 @@ export class QuickBar extends LitElement { ` : nothing} ${item.translatedDomain && !showEntityId - ? html`
    + ? html`
    ${item.translatedDomain}
    ` : nothing} @@ -1027,7 +1027,7 @@ export class QuickBar extends LitElement { font-size: var(--ha-font-size-xs); } - ha-md-list-item [slot="trailing-supporting-text"] { + ha-md-list-item .domain { font-size: var(--ha-font-size-s); font-weight: var(--ha-font-weight-normal); line-height: var(--ha-line-height-normal); From d392bb4c831a0942ad5a9162aeda63fa80ff7b7f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 9 May 2025 16:01:03 +0200 Subject: [PATCH 3/9] Fallback to preferred pipeline if the pipeline doesn't exist (#25404) --- .../voice-command-dialog/ha-voice-command-dialog.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts index 1b2a0e88a0..a9f1a8d771 100644 --- a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts +++ b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts @@ -57,16 +57,22 @@ export class HaVoiceCommandDialog extends LitElement { public async showDialog( params: Required ): Promise { + await this._loadPipelines(); + const pipelinesIds = this._pipelines?.map((pipeline) => pipeline.id) || []; if ( params.pipeline_id === "preferred" || (params.pipeline_id === "last_used" && !this._pipelineId) ) { - await this._loadPipelines(); this._pipelineId = this._preferredPipeline; } else if (!["last_used", "preferred"].includes(params.pipeline_id)) { this._pipelineId = params.pipeline_id; } + // If the pipeline id is not in the list of pipelines, set it to preferred + if (this._pipelineId && !pipelinesIds.includes(this._pipelineId)) { + this._pipelineId = this._preferredPipeline; + } + this._startListening = params.start_listening; this._opened = true; } From c00b4120abaab30f22a3e405b23ba314ba015b1b Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 9 May 2025 18:35:38 +0200 Subject: [PATCH 4/9] Fix selected entity in the entity picker dropdown (#25405) --- src/components/entity/ha-entity-combo-box.ts | 2 ++ src/components/entity/ha-statistic-combo-box.ts | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/entity/ha-entity-combo-box.ts b/src/components/entity/ha-entity-combo-box.ts index d1d22b4c55..5d31baa43b 100644 --- a/src/components/entity/ha-entity-combo-box.ts +++ b/src/components/entity/ha-entity-combo-box.ts @@ -410,7 +410,9 @@ export class HaEntityComboBox extends LitElement { protected render(): TemplateResult { return html` Date: Fri, 9 May 2025 09:35:54 -0700 Subject: [PATCH 5/9] Fix devices graph max_devices (#25406) --- .../lovelace/cards/energy/hui-energy-devices-graph-card.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts index fc987b3300..708514d836 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts @@ -244,7 +244,10 @@ export class HuiEnergyDevicesGraphCard chartData.sort((a: any, b: any) => b.value[0] - a.value[0]); - chartData.length = this._config?.max_devices || chartData.length; + chartData.length = Math.min( + this._config?.max_devices || Infinity, + chartData.length + ); this._chartData = datasets; await this.updateComplete; From 75a2c061c2cf8d66ff8d3b07216767b9f06ec239 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Fri, 9 May 2025 09:38:03 -0700 Subject: [PATCH 6/9] Fix card editor help links (#25408) --- .../card-editor/hui-dialog-edit-card.ts | 6 +----- .../editor/get-dashboard-documentation-url.ts | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts index e2e5aebd4e..ed1a507966 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts @@ -110,11 +110,7 @@ export class HuiDialogEditCard } protected updated(changedProps: PropertyValues): void { - if ( - !this._cardConfig || - this._documentationURL !== undefined || - !changedProps.has("_cardConfig") - ) { + if (!this._cardConfig || !changedProps.has("_cardConfig")) { return; } diff --git a/src/panels/lovelace/editor/get-dashboard-documentation-url.ts b/src/panels/lovelace/editor/get-dashboard-documentation-url.ts index 658e1f2cec..c4e66db8aa 100644 --- a/src/panels/lovelace/editor/get-dashboard-documentation-url.ts +++ b/src/panels/lovelace/editor/get-dashboard-documentation-url.ts @@ -7,6 +7,23 @@ import { import type { HomeAssistant } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; +const NON_STANDARD_URLS = { + "energy-date-selection": "energy/#energy-date-picker", + "energy-usage-graph": "energy/#energy-usage-graph", + "energy-solar-graph": "energy/#solar-production-graph", + "energy-gas-graph": "energy/#gas-consumption-graph", + "energy-water-graph": "energy/#water-consumption-graph", + "energy-distribution": "energy/#energy-distribution", + "energy-sources-table": "energy/#energy-sources-table", + "energy-grid-neutrality-gauge": "energy/#grid-neutrality-gauge", + "energy-solar-consumed-gauge": "energy/#solar-consumed-gauge", + "energy-carbon-consumed-gauge": "energy/#carbon-consumed-gauge", + "energy-self-sufficiency-gauge": "energy/#self-sufficiency-gauge", + "energy-devices-graph": "energy/#devices-energy-graph", + "energy-devices-detail-graph": "energy/#detail-devices-energy-graph", + "energy-sankey": "energy/#sankey-energy-graph", +}; + export const getCardDocumentationURL = ( hass: HomeAssistant, type: string @@ -15,7 +32,7 @@ export const getCardDocumentationURL = ( return getCustomCardEntry(stripCustomPrefix(type))?.documentationURL; } - return `${documentationUrl(hass, "/dashboards/")}${type}`; + return `${documentationUrl(hass, "/dashboards/")}${NON_STANDARD_URLS[type] || type}`; }; export const getBadgeDocumentationURL = ( From 98a296643281b1c798b43c52aa95b3713b766736 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 10 May 2025 09:03:53 +0200 Subject: [PATCH 7/9] Render choose and if action correctly on mobile (#25411) --- .../automation/action/types/ha-automation-action-choose.ts | 4 ++++ .../automation/action/types/ha-automation-action-if.ts | 5 +++++ .../config/automation/option/ha-automation-option-row.ts | 2 ++ 3 files changed, 11 insertions(+) diff --git a/src/panels/config/automation/action/types/ha-automation-action-choose.ts b/src/panels/config/automation/action/types/ha-automation-action-choose.ts index 07498c4606..2c193cc024 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-choose.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-choose.ts @@ -18,6 +18,8 @@ export class HaChooseAction extends LitElement implements ActionElement { @property({ attribute: false }) public action!: ChooseAction; + @property({ type: Boolean }) public narrow = false; + @state() private _showDefault = false; public static get defaultConfig(): ChooseAction { @@ -35,6 +37,7 @@ export class HaChooseAction extends LitElement implements ActionElement { .disabled=${this.disabled} @value-changed=${this._optionsChanged} .hass=${this.hass} + .narrow=${this.narrow} > ${this._showDefault || action.default @@ -49,6 +52,7 @@ export class HaChooseAction extends LitElement implements ActionElement { .disabled=${this.disabled} @value-changed=${this._defaultChanged} .hass=${this.hass} + .narrow=${this.narrow} > ` : html` diff --git a/src/panels/config/automation/action/types/ha-automation-action-if.ts b/src/panels/config/automation/action/types/ha-automation-action-if.ts index 2d7b8f1ecf..3b1eb99120 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-if.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-if.ts @@ -18,6 +18,8 @@ export class HaIfAction extends LitElement implements ActionElement { @property({ attribute: false }) public action!: IfAction; + @property({ type: Boolean }) public narrow = false; + @state() private _showElse = false; public static get defaultConfig(): IfAction { @@ -41,6 +43,7 @@ export class HaIfAction extends LitElement implements ActionElement { .disabled=${this.disabled} @value-changed=${this._ifChanged} .hass=${this.hass} + .narrow=${this.narrow} >

    @@ -53,6 +56,7 @@ export class HaIfAction extends LitElement implements ActionElement { .disabled=${this.disabled} @value-changed=${this._thenChanged} .hass=${this.hass} + .narrow=${this.narrow} > ${this._showElse || action.else ? html` @@ -66,6 +70,7 @@ export class HaIfAction extends LitElement implements ActionElement { .disabled=${this.disabled} @value-changed=${this._elseChanged} .hass=${this.hass} + .narrow=${this.narrow} > ` : html` From 67f3d31a4be84d3371f76620589c1cff0508ec74 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 13 May 2025 07:54:31 +0200 Subject: [PATCH 8/9] Fix domain not translated in entity picker and quick bar (#25444) * Fix domain not translated in entity picker * Remove unused param --- src/components/entity/ha-entity-picker.ts | 15 +++++++++++- src/dialogs/quick-bar/ha-quick-bar.ts | 30 +++++++++++------------ 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 515a5a58b4..40e448bae2 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -1,6 +1,13 @@ import { mdiClose, mdiMenuDown, mdiShape } from "@mdi/js"; import type { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light"; -import { css, html, LitElement, nothing, type CSSResultGroup } from "lit"; +import { + css, + html, + LitElement, + nothing, + type CSSResultGroup, + type PropertyValues, +} from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; import { stopPropagation } from "../../common/dom/stop_propagation"; @@ -106,6 +113,12 @@ export class HaEntityPicker extends LitElement { @state() private _opened = false; + protected firstUpdated(changedProperties: PropertyValues): void { + super.firstUpdated(changedProperties); + // Load title translations so it is available when the combo-box opens + this.hass.loadBackendTranslation("title"); + } + private _renderContent() { const entityId = this.value || ""; diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 21b1215e06..f27ff5b428 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -9,50 +9,50 @@ import { mdiReload, mdiServerNetwork, } from "@mdi/js"; +import Fuse from "fuse.js"; import type { TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; -import Fuse from "fuse.js"; import { canShowPage } from "../../common/config/can_show_page"; import { componentsWithService } from "../../common/config/components_with_service"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { fireEvent } from "../../common/dom/fire_event"; +import { computeAreaName } from "../../common/entity/compute_area_name"; import { computeDeviceName, computeDeviceNameDisplay, } from "../../common/entity/compute_device_name"; +import { computeDomain } from "../../common/entity/compute_domain"; +import { computeEntityName } from "../../common/entity/compute_entity_name"; +import { computeStateName } from "../../common/entity/compute_state_name"; +import { getEntityContext } from "../../common/entity/context/get_entity_context"; import { navigate } from "../../common/navigate"; import { caseInsensitiveStringCompare } from "../../common/string/compare"; import type { ScorableTextItem } from "../../common/string/filter/sequence-matching"; +import { computeRTL } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; import "../../components/ha-icon-button"; import "../../components/ha-label"; import "../../components/ha-list"; +import "../../components/ha-md-list-item"; import "../../components/ha-spinner"; import "../../components/ha-textfield"; import "../../components/ha-tip"; -import "../../components/ha-md-list-item"; import { fetchHassioAddonsInfo } from "../../data/hassio/addon"; import { domainToName } from "../../data/integration"; import { getPanelNameTranslationKey } from "../../data/panel"; import type { PageNavigation } from "../../layouts/hass-tabs-subpage"; import { configSections } from "../../panels/config/ha-panel-config"; +import { HaFuse } from "../../resources/fuse"; import { haStyleDialog, haStyleScrollbar } from "../../resources/styles"; import { loadVirtualizer } from "../../resources/virtualizer"; import type { HomeAssistant } from "../../types"; import { showConfirmationDialog } from "../generic/show-dialog-box"; import { showShortcutsDialog } from "../shortcuts/show-shortcuts-dialog"; import { QuickBarMode, type QuickBarParams } from "./show-dialog-quick-bar"; -import { getEntityContext } from "../../common/entity/context/get_entity_context"; -import { computeEntityName } from "../../common/entity/compute_entity_name"; -import { computeAreaName } from "../../common/entity/compute_area_name"; -import { computeRTL } from "../../common/util/compute_rtl"; -import { computeDomain } from "../../common/entity/compute_domain"; -import { computeStateName } from "../../common/entity/compute_state_name"; -import { HaFuse } from "../../resources/fuse"; interface QuickBarItem extends ScorableTextItem { primaryText: string; @@ -152,11 +152,6 @@ export class QuickBar extends LitElement { } } - protected firstUpdated(changedProps) { - super.firstUpdated(changedProps); - this.hass.loadBackendTranslation("title"); - } - private _getItems = memoizeOne( ( mode: QuickBarMode, @@ -304,7 +299,8 @@ export class QuickBar extends LitElement { } else if (this._mode === QuickBarMode.Device) { this._deviceItems = this._deviceItems || this._generateDeviceItems(); } else { - this._entityItems = this._entityItems || this._generateEntityItems(); + this._entityItems = + this._entityItems || (await this._generateEntityItems()); } } @@ -581,9 +577,11 @@ export class QuickBar extends LitElement { ); } - private _generateEntityItems(): EntityItem[] { + private async _generateEntityItems(): Promise { const isRTL = computeRTL(this.hass); + await this.hass.loadBackendTranslation("title"); + return Object.keys(this.hass.states) .map((entityId) => { const stateObj = this.hass.states[entityId]; From 69a156f3522520479f92ad503bb4095a7d91b3f4 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 16 May 2025 18:57:02 +0200 Subject: [PATCH 9/9] Bumped version to 20250516.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2eec5c49cb..0a5a6fb847 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250509.0" +version = "20250516.0" license = "Apache-2.0" license-files = ["LICENSE*"] description = "The Home Assistant frontend"