Merge branch 'rc'

This commit is contained in:
Bram Kragten 2025-05-16 18:57:16 +02:00
commit 95c3013497
17 changed files with 123 additions and 40 deletions

View File

@ -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"

View File

@ -178,7 +178,9 @@ export class HaEntityComboBox extends LitElement {
: nothing}
${item.domain_name && !showEntityId
? html`
<div slot="trailing-supporting-text">${item.domain_name}</div>
<div slot="trailing-supporting-text" class="domain">
${item.domain_name}
</div>
`
: nothing}
</ha-combo-box-item>
@ -408,7 +410,9 @@ export class HaEntityComboBox extends LitElement {
protected render(): TemplateResult {
return html`
<ha-combo-box
item-id-path="id"
item-value-path="id"
item-label-path="label"
.hass=${this.hass}
.value=${this._value}
.label=${this.label === undefined

View File

@ -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 || "";

View File

@ -396,6 +396,9 @@ export class HaStatisticComboBox extends LitElement {
return html`
<ha-combo-box
item-id-path="id"
item-value-path="id"
item-label-path="label"
.hass=${this.hass}
.label=${this.label === undefined && this.hass
? this.hass.localize("ui.components.statistic-picker.statistic")
@ -405,9 +408,6 @@ export class HaStatisticComboBox extends LitElement {
.disabled=${this.disabled}
.allowCustomValue=${this.allowCustomEntity}
.filteredItems=${this._items}
item-value-path="id"
item-id-path="id"
item-label-path="label"
@opened-changed=${this._openedChanged}
@value-changed=${this._statisticChanged}
@filter-changed=${this._filterChanged}

View File

@ -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);

View File

@ -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<string, SystemCheckValue>;
}
>;
};
}>;
interface SystemHealthEventInitial {
type: "initial";

View File

@ -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());
}
}
@ -386,7 +382,7 @@ export class QuickBar extends LitElement {
`
: nothing}
${item.translatedDomain && !showEntityId
? html`<div slot="trailing-supporting-text">
? html`<div slot="trailing-supporting-text" class="domain">
${item.translatedDomain}
</div>`
: nothing}
@ -581,9 +577,11 @@ export class QuickBar extends LitElement {
);
}
private _generateEntityItems(): EntityItem[] {
private async _generateEntityItems(): Promise<EntityItem[]> {
const isRTL = computeRTL(this.hass);
await this.hass.loadBackendTranslation("title");
return Object.keys(this.hass.states)
.map((entityId) => {
const stateObj = this.hass.states[entityId];
@ -1027,7 +1025,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);

View File

@ -57,16 +57,22 @@ export class HaVoiceCommandDialog extends LitElement {
public async showDialog(
params: Required<VoiceCommandDialogParams>
): Promise<void> {
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;
}

View File

@ -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}
></ha-automation-option>
${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}
></ha-automation-action>
`
: html`

View File

@ -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}
></ha-automation-condition>
<h3>
@ -53,6 +56,7 @@ export class HaIfAction extends LitElement implements ActionElement {
.disabled=${this.disabled}
@value-changed=${this._thenChanged}
.hass=${this.hass}
.narrow=${this.narrow}
></ha-automation-action>
${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}
></ha-automation-action>
`
: html` <div class="link-button-row">

View File

@ -183,6 +183,7 @@ export default class HaAutomationOptionRow extends LitElement {
)}
.disabled=${this.disabled}
.hass=${this.hass}
.narrow=${this.narrow}
@value-changed=${this._conditionChanged}
></ha-automation-condition>
<h4>
@ -194,6 +195,7 @@ export default class HaAutomationOptionRow extends LitElement {
.actions=${ensureArray(this.option.sequence) || []}
.disabled=${this.disabled}
.hass=${this.hass}
.narrow=${this.narrow}
@value-changed=${this._actionChanged}
></ha-automation-action>
</div>

View File

@ -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 {
</a>
<p>Home Assistant</p>
<ul class="versions">
<li>
<span class="version-label"
>${this.hass.localize(
`ui.panel.config.info.installation_method`
)}</span
>
<span class="version">${this._installationMethod || "…"}</span>
</li>
<li>
<span class="version-label">Core</span>
<span class="version">${hass.connection.haVersion}</span>
@ -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<void> {

View File

@ -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 = [
`${

View File

@ -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;

View File

@ -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;
}

View File

@ -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 = (

View File

@ -3110,6 +3110,7 @@
},
"info": {
"caption": "About",
"installation_method": "Installation method",
"copy_menu": "Copy menu",
"copy_raw": "Raw text",
"copy_github": "For GitHub",